1/* 2 * This file Copyright (C) Mnemosyne LLC 3 * 4 * This file is licensed by the GPL version 2. Works owned by the 5 * Transmission project are granted a special exemption to clause 2(b) 6 * so that the bulk of its code can remain under the MIT license. 7 * This exemption does not extend to derived works not owned by 8 * the Transmission project. 9 * 10 * $Id: favicon.c 12655 2011-08-08 17:10:55Z jordan $ 11 */ 12 13#include <glib/gstdio.h> /* g_remove() */ 14#include <gtk/gtk.h> 15 16#include <libtransmission/transmission.h> 17#include <libtransmission/web.h> /* tr_webRun() */ 18 19#include "favicon.h" 20#include "util.h" /* gtr_get_host_from_url() */ 21 22#define IMAGE_TYPES 4 23static const char * image_types[IMAGE_TYPES] = { "ico", "png", "gif", "jpg" }; 24 25struct favicon_data 26{ 27 tr_session * session; 28 GFunc func; 29 gpointer data; 30 char * host; 31 char * contents; 32 size_t len; 33 int type; 34}; 35 36static char* 37get_url( const char * host, int image_type ) 38{ 39 return g_strdup_printf( "http://%s/favicon.%s", host, image_types[image_type] ); 40} 41 42static char* 43favicon_get_cache_dir( void ) 44{ 45 static char * dir = NULL; 46 47 if( dir == NULL ) 48 { 49 dir = g_build_filename( g_get_user_cache_dir(), 50 "transmission", 51 "favicons", 52 NULL ); 53 g_mkdir_with_parents( dir, 0777 ); 54 } 55 56 return dir; 57} 58 59static char* 60favicon_get_cache_filename( const char * host ) 61{ 62 return g_build_filename( favicon_get_cache_dir(), host, NULL ); 63} 64 65static void 66favicon_save_to_cache( const char * host, const void * data, size_t len ) 67{ 68 char * filename = favicon_get_cache_filename( host ); 69 g_file_set_contents( filename, data, len, NULL ); 70 g_free( filename ); 71} 72 73static GdkPixbuf* 74favicon_load_from_cache( const char * host ) 75{ 76 char * filename = favicon_get_cache_filename( host ); 77 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_size( filename, 16, 16, NULL ); 78 if( pixbuf == NULL ) /* bad file */ 79 g_remove( filename ); 80 g_free( filename ); 81 return pixbuf; 82} 83 84static void favicon_web_done_cb( tr_session*, bool, bool, long, const void*, size_t, void* ); 85 86static gboolean 87favicon_web_done_idle_cb( gpointer vfav ) 88{ 89 GdkPixbuf * pixbuf = NULL; 90 gboolean finished = FALSE; 91 struct favicon_data * fav = vfav; 92 93 if( fav->len > 0 ) /* we got something... try to make a pixbuf from it */ 94 { 95 favicon_save_to_cache( fav->host, fav->contents, fav->len ); 96 pixbuf = favicon_load_from_cache( fav->host ); 97 finished = pixbuf != NULL; 98 } 99 100 if( !finished ) /* no pixbuf yet... */ 101 { 102 if( ++fav->type == IMAGE_TYPES ) /* failure */ 103 { 104 finished = TRUE; 105 } 106 else /* keep trying */ 107 { 108 char * url = get_url( fav->host, fav->type ); 109 110 g_free( fav->contents ); 111 fav->contents = NULL; 112 fav->len = 0; 113 114 tr_webRun( fav->session, url, NULL, NULL, favicon_web_done_cb, fav ); 115 g_free( url ); 116 } 117 } 118 119 if( finished ) 120 { 121 fav->func( pixbuf, fav->data ); 122 g_free( fav->host ); 123 g_free( fav->contents ); 124 g_free( fav ); 125 } 126 127 return FALSE; 128} 129 130static void 131favicon_web_done_cb( tr_session * session UNUSED, 132 bool did_connect UNUSED, 133 bool did_timeout UNUSED, 134 long code UNUSED, 135 const void * data, 136 size_t len, 137 void * vfav ) 138{ 139 struct favicon_data * fav = vfav; 140 fav->contents = g_memdup( data, len ); 141 fav->len = len; 142 143 gdk_threads_add_idle( favicon_web_done_idle_cb, fav ); 144} 145 146void 147gtr_get_favicon( tr_session * session, 148 const char * host, 149 GFunc pixbuf_ready_func, 150 gpointer pixbuf_ready_func_data ) 151{ 152 GdkPixbuf * pixbuf = favicon_load_from_cache( host ); 153 154 if( pixbuf != NULL ) 155 { 156 pixbuf_ready_func( pixbuf, pixbuf_ready_func_data ); 157 } 158 else 159 { 160 struct favicon_data * data; 161 char * url = get_url( host, 0 ); 162 163 data = g_new( struct favicon_data, 1 ); 164 data->session = session; 165 data->func = pixbuf_ready_func; 166 data->data = pixbuf_ready_func_data; 167 data->host = g_strdup( host ); 168 data->type = 0; 169 170 tr_webRun( session, url, NULL, NULL, favicon_web_done_cb, data ); 171 g_free( url ); 172 } 173} 174 175void 176gtr_get_favicon_from_url( tr_session * session, 177 const char * url, 178 GFunc pixbuf_ready_func, 179 gpointer pixbuf_ready_func_data ) 180{ 181 char host[1024]; 182 gtr_get_host_from_url( host, sizeof( host ), url ); 183 gtr_get_favicon( session, host, pixbuf_ready_func, pixbuf_ready_func_data ); 184} 185