2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2016 Ricardo Mones and the Claws Mail Team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "claws-features.h"
25 #include <curl/curl.h>
28 #include <common/claws.h>
29 #include <prefs_common.h>
30 #include <file-utils.h>
32 #include "libravatar.h"
33 #include "libravatar_prefs.h"
34 #include "libravatar_missing.h"
35 #include "libravatar_image.h"
37 static size_t write_image_data_cb(void *ptr
, size_t size
, size_t nmemb
, void *stream
)
39 size_t written
= claws_fwrite(ptr
, size
, nmemb
, (FILE *)stream
);
40 debug_print("received %"G_GSIZE_FORMAT
" bytes from avatar server\n", written
);
45 static GdkPixbuf
*image_pixbuf_from_filename(const gchar
*filename
)
47 GdkPixbuf
*picture
= NULL
;
51 gdk_pixbuf_get_file_info(filename
, &w
, &h
);
53 if (w
!= AVATAR_SIZE
|| h
!= AVATAR_SIZE
)
54 /* server can provide a different size from the requested in URL */
55 picture
= gdk_pixbuf_new_from_file_at_scale(
56 filename
, AVATAR_SIZE
, AVATAR_SIZE
, TRUE
, &error
);
58 picture
= gdk_pixbuf_new_from_file(filename
, &error
);
61 g_warning("failed to load image '%s': %s", filename
, error
->message
);
65 g_warning("failed to load image '%s': no error returned!", filename
);
71 static GdkPixbuf
*pixbuf_from_url(const gchar
*url
, const gchar
*md5
, const gchar
*filename
) {
72 GdkPixbuf
*image
= NULL
;
78 file
= claws_fopen(filename
, "wb");
80 g_warning("could not open '%s' for writing", filename
);
83 curl
= curl_easy_init();
85 g_warning("could not initialize curl to get image from URL");
91 curl_easy_setopt(curl
, CURLOPT_URL
, url
);
92 curl_easy_setopt(curl
, CURLOPT_WRITEFUNCTION
, write_image_data_cb
);
93 /* make sure timeout is less than general IO timeout */
94 curl_easy_setopt(curl
, CURLOPT_TIMEOUT
,
95 (libravatarprefs
.timeout
== 0
96 || libravatarprefs
.timeout
97 > prefs_common_get_prefs()->io_timeout_secs
)
98 ? prefs_common_get_prefs()->io_timeout_secs
99 : libravatarprefs
.timeout
);
100 curl_easy_setopt(curl
, CURLOPT_NOSIGNAL
, 1);
102 if (libravatarprefs
.allow_redirects
) {
103 long maxredirs
= (libravatarprefs
.default_mode
== DEF_MODE_URL
)
104 ? libravatarprefs
.max_redirects_url
105 : ((libravatarprefs
.default_mode
== DEF_MODE_MM
)
106 ? libravatarprefs
.max_redirects_mm
107 : libravatarprefs
.max_redirects_url
);
108 debug_print("setting max redirects to %ld\n", maxredirs
);
109 curl_easy_setopt(curl
, CURLOPT_FOLLOWLOCATION
, 1L);
110 curl_easy_setopt(curl
, CURLOPT_MAXREDIRS
, maxredirs
);
112 curl_easy_setopt(curl
, CURLOPT_FILE
, file
);
113 curl_easy_setopt(curl
, CURLOPT_FAILONERROR
, 1L); /* fail on HTTP error */
115 curl_easy_setopt(curl
, CURLOPT_CAINFO
, claws_ssl_get_cert_file());
117 debug_print("retrieving URL to file: %s -> %s\n", url
, filename
);
118 res
= curl_easy_perform(curl
);
119 if (res
!= CURLE_OK
) {
120 debug_print("curl_easy_perfom failed: %s\n", curl_easy_strerror(res
));
123 missing_add_md5(libravatarmisses
, md5
);
125 filesize
= ftell(file
);
126 claws_safe_fclose(file
);
127 if (filesize
< MIN_PNG_SIZE
) {
128 debug_print("not enough data for an avatar image: %ld bytes\n", filesize
);
129 missing_add_md5(libravatarmisses
, md5
);
131 image
= image_pixbuf_from_filename(filename
);
134 if (!libravatarprefs
.cache_icons
|| filesize
< MIN_PNG_SIZE
) {
135 if (g_unlink(filename
) < 0)
136 g_warning("failed to delete cache file '%s'", filename
);
140 curl_easy_cleanup(curl
);
145 static void *get_image_thread(void *arg
) {
146 AvatarImageFetch
*ctx
= (AvatarImageFetch
*)arg
;
149 ctx
->pixbuf
= pixbuf_from_url(ctx
->url
, ctx
->md5
, ctx
->filename
);
156 GdkPixbuf
*libravatar_image_fetch(AvatarImageFetch
*ctx
)
162 g_return_val_if_fail(ctx
!= NULL
, NULL
);
165 if (pthread_create(&pt
, NULL
, get_image_thread
, (void *)ctx
) != 0) {
166 debug_print("synchronous image fetching (couldn't create thread)\n");
167 get_image_thread(ctx
);
169 debug_print("waiting for thread completion\n");
171 while (!ctx->ready ) {
175 pthread_join(pt
, NULL
);
176 debug_print("thread completed\n");
179 debug_print("synchronous image fetching (pthreads unavailable)\n");
180 get_image_thread(ctx
);
182 if (ctx
->pixbuf
== NULL
) {
183 g_warning("could not get image");