/* * 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 ); }