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