/* * This file Copyright (C) Mnemosyne LLC * * This file is licensed by the GPL version 2. Works owned by the * Transmission project are granted a special exemption to clause 2 (b) * so that the bulk of its code can remain under the MIT license. * This exemption does not extend to derived works not owned by * the Transmission project. * * $Id$ */ #include /* g_remove () */ #include #include #include /* tr_webRun () */ #include "favicon.h" #include "util.h" /* gtr_get_host_from_url () */ #define IMAGE_TYPES 4 static const char * 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 (const char * 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 (const char * host) { return g_build_filename (favicon_get_cache_dir (), host, NULL); } static void favicon_save_to_cache (const char * host, const void * 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 (const char * 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, const void*, 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, NULL, NULL, 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 FALSE; } static void favicon_web_done_cb (tr_session * session UNUSED, bool did_connect UNUSED, bool did_timeout UNUSED, long code UNUSED, const void * data, size_t len, void * vfav) { 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, const char * 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, NULL, NULL, favicon_web_done_cb, data); g_free (url); } } void gtr_get_favicon_from_url (tr_session * session, const char * 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); }