/* * This file Copyright (C) 2012-2014 Mnemosyne LLC * * It may be used under the GNU GPL versions 2 or 3 * or any future license endorsed by Mnemosyne LLC. * */ #include /* g_remove() */ #include #include #include /* tr_webRun() */ #include "favicon.h" #include "util.h" /* gtr_get_host_from_url() */ #define IMAGE_TYPES 4 static char const* image_types[IMAGE_TYPES] = { "ico", "png", "gif", "jpg" }; struct favicon_data { tr_session* session; GFunc func; gpointer data; char* host; char* contents; size_t len; int type; }; static char* get_url(char const* host, int image_type) { return g_strdup_printf("http://%s/favicon.%s", host, image_types[image_type]); } static char* favicon_get_cache_dir(void) { static char* dir = NULL; if (dir == NULL) { dir = g_build_filename(g_get_user_cache_dir(), "transmission", "favicons", NULL); g_mkdir_with_parents(dir, 0777); } return dir; } static char* favicon_get_cache_filename(char const* host) { return g_build_filename(favicon_get_cache_dir(), host, NULL); } static void favicon_save_to_cache(char const* host, void const* data, size_t len) { char* filename = favicon_get_cache_filename(host); g_file_set_contents(filename, data, len, NULL); g_free(filename); } static GdkPixbuf* favicon_load_from_cache(char const* host) { char* filename = favicon_get_cache_filename(host); GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, 16, 16, NULL); if (pixbuf == NULL) /* bad file */ { g_remove(filename); } g_free(filename); return pixbuf; } static void favicon_web_done_cb(tr_session*, bool, bool, long, void const*, size_t, void*); static gboolean favicon_web_done_idle_cb(gpointer vfav) { GdkPixbuf* pixbuf = NULL; gboolean finished = FALSE; struct favicon_data* fav = vfav; if (fav->len > 0) /* we got something... try to make a pixbuf from it */ { favicon_save_to_cache(fav->host, fav->contents, fav->len); pixbuf = favicon_load_from_cache(fav->host); finished = pixbuf != NULL; } if (!finished) /* no pixbuf yet... */ { if (++fav->type == IMAGE_TYPES) /* failure */ { finished = TRUE; } else /* keep trying */ { char* url = get_url(fav->host, fav->type); g_free(fav->contents); fav->contents = NULL; fav->len = 0; tr_webRun(fav->session, url, favicon_web_done_cb, fav); g_free(url); } } if (finished) { fav->func(pixbuf, fav->data); g_free(fav->host); g_free(fav->contents); g_free(fav); } return G_SOURCE_REMOVE; } static void favicon_web_done_cb( tr_session* session, bool did_connect, bool did_timeout, long code, void const* data, size_t len, void* vfav) { TR_UNUSED(session); TR_UNUSED(did_connect); TR_UNUSED(did_timeout); TR_UNUSED(code); struct favicon_data* fav = vfav; fav->contents = g_memdup(data, len); fav->len = len; gdk_threads_add_idle(favicon_web_done_idle_cb, fav); } void gtr_get_favicon(tr_session* session, char const* host, GFunc pixbuf_ready_func, gpointer pixbuf_ready_func_data) { GdkPixbuf* pixbuf = favicon_load_from_cache(host); if (pixbuf != NULL) { pixbuf_ready_func(pixbuf, pixbuf_ready_func_data); } else { struct favicon_data* data; char* url = get_url(host, 0); data = g_new(struct favicon_data, 1); data->session = session; data->func = pixbuf_ready_func; data->data = pixbuf_ready_func_data; data->host = g_strdup(host); data->type = 0; tr_webRun(session, url, favicon_web_done_cb, data); g_free(url); } } void gtr_get_favicon_from_url(tr_session* session, char const* url, GFunc pixbuf_ready_func, gpointer pixbuf_ready_func_data) { char host[1024]; gtr_get_host_from_url(host, sizeof(host), url); gtr_get_favicon(session, host, pixbuf_ready_func, pixbuf_ready_func_data); }