From e62c01edd0c6bfc6c9c2e7b9645497603b8f8e83 Mon Sep 17 00:00:00 2001 From: Ricardo Mones Date: Mon, 17 Oct 2016 14:09:25 +0200 Subject: [PATCH] Libravatar: refactor image retrieval And use a synchronous thread for fetching, in preparation of a future asynchronous fetching. Thanks Andrej for the ideas. --- src/plugins/libravatar/Makefile.am | 1 + src/plugins/libravatar/libravatar.c | 88 ++++-------- src/plugins/libravatar/libravatar_image.c | 168 ++++++++++++++++++++++ src/plugins/libravatar/libravatar_image.h | 37 +++++ 4 files changed, 232 insertions(+), 62 deletions(-) create mode 100644 src/plugins/libravatar/libravatar_image.c create mode 100644 src/plugins/libravatar/libravatar_image.h diff --git a/src/plugins/libravatar/Makefile.am b/src/plugins/libravatar/Makefile.am index 7edf1f5c8..4fcc57c77 100644 --- a/src/plugins/libravatar/Makefile.am +++ b/src/plugins/libravatar/Makefile.am @@ -79,6 +79,7 @@ libravatar_la_SOURCES = \ libravatar.c libravatar.h \ libravatar_prefs.c libravatar_prefs.h \ libravatar_cache.c libravatar_cache.h \ + libravatar_image.c libravatar_image.h \ libravatar_missing.c libravatar_missing.h \ libravatar_federation.c libravatar_federation.h diff --git a/src/plugins/libravatar/libravatar.c b/src/plugins/libravatar/libravatar.c index e36829e61..7ce4341e3 100644 --- a/src/plugins/libravatar/libravatar.c +++ b/src/plugins/libravatar/libravatar.c @@ -30,6 +30,7 @@ #include "libravatar.h" #include "libravatar_prefs.h" #include "libravatar_cache.h" +#include "libravatar_image.h" #include "libravatar_missing.h" #include "libravatar_federation.h" #include "prefs_common.h" @@ -96,9 +97,21 @@ default_url: return g_strdup(libravatarprefs.base_url); } -static GtkWidget *image_widget_from_filename(const gchar *filename) +static GtkWidget *image_widget_from_pixbuf(GdkPixbuf *picture) { GtkWidget *image = NULL; + + if (picture) { + image = gtk_image_new_from_pixbuf(picture); + g_object_unref(picture); + } else + g_warning("null picture returns null widget"); + + return image; +} + +static GtkWidget *image_widget_from_filename(const gchar *filename) +{ GdkPixbuf *picture = NULL; GError *error = NULL; gint w, h; @@ -115,15 +128,10 @@ static GtkWidget *image_widget_from_filename(const gchar *filename) if (error != NULL) { g_warning("failed to load image '%s': %s", filename, error->message); g_error_free(error); - } else { - if (picture) { - image = gtk_image_new_from_pixbuf(picture); - g_object_unref(picture); - } else - g_warning("failed to load image '%s': no error returned!", filename); + return NULL; } - return image; + return image_widget_from_pixbuf(picture); } static gchar *cache_name_for_md5(const gchar *md5) @@ -149,61 +157,17 @@ static size_t write_image_data_cb(void *ptr, size_t size, size_t nmemb, void *st static GtkWidget *image_widget_from_url(const gchar *url, const gchar *md5) { GtkWidget *image = NULL; - gchar *filename; - FILE *file; - CURL *curl; - - curl = curl_easy_init(); - if (curl == NULL) { - g_warning("could not initialize curl to get image from URL"); - return NULL; + AvatarImageFetch aif; + + aif.url = url; + aif.md5 = md5; + aif.filename = cache_name_for_md5(md5); + libravatar_image_fetch(&aif); + if (aif.pixbuf) { + image = gtk_image_new_from_pixbuf(aif.pixbuf); + g_object_unref(aif.pixbuf); } - curl_easy_setopt(curl, CURLOPT_URL, url); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_image_data_cb); - /* make sure timeout is less than general IO timeout */ - curl_easy_setopt(curl, CURLOPT_TIMEOUT, - (libravatarprefs.timeout == 0 - || libravatarprefs.timeout - > prefs_common_get_prefs()->io_timeout_secs) - ? prefs_common_get_prefs()->io_timeout_secs - : libravatarprefs.timeout); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - - filename = cache_name_for_md5(md5); - file = fopen(filename, "wb"); - if (file != NULL) { - long filesize; - - if (libravatarprefs.allow_redirects) { - long maxredirs = (libravatarprefs.default_mode == DEF_MODE_URL)? 3L - : ((libravatarprefs.default_mode == DEF_MODE_MM)? 2L: 1L); - - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, maxredirs); - } - curl_easy_setopt(curl, CURLOPT_FILE, file); - debug_print("retrieving URL to file: %s -> %s\n", url, filename); - curl_easy_perform(curl); - filesize = ftell(file); - fclose(file); - - if (filesize < MIN_PNG_SIZE) - debug_print("not enough data for an avatar image: %ld bytes\n", filesize); - else - image = image_widget_from_filename(filename); - - if (!libravatarprefs.cache_icons || filesize == 0) { - if (g_unlink(filename) < 0) - g_warning("failed to delete cache file '%s'", filename); - } - - if (filesize == 0) - missing_add_md5(libravatarmisses, md5); - } else { - g_warning("could not open '%s' for writing", filename); - } - curl_easy_cleanup(curl); - g_free(filename); + g_free(aif.filename); return image; } diff --git a/src/plugins/libravatar/libravatar_image.c b/src/plugins/libravatar/libravatar_image.c new file mode 100644 index 000000000..fa1f78ab7 --- /dev/null +++ b/src/plugins/libravatar/libravatar_image.c @@ -0,0 +1,168 @@ +/* + * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2016 Ricardo Mones and the Claws Mail Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#include "claws-features.h" +#endif + +#include +#include +#include + +#include +#include + +#include "libravatar.h" +#include "libravatar_prefs.h" +#include "libravatar_missing.h" +#include "libravatar_image.h" + +static size_t write_image_data_cb(void *ptr, size_t size, size_t nmemb, void *stream) +{ + size_t written = fwrite(ptr, size, nmemb, (FILE *)stream); + debug_print("received %zu bytes from avatar server\n", written); + + return written; +} + +static GdkPixbuf *image_pixbuf_from_filename(const gchar *filename) +{ + GdkPixbuf *picture = NULL; + GError *error = NULL; + gint w, h; + + gdk_pixbuf_get_file_info(filename, &w, &h); + + if (w != AVATAR_SIZE || h != AVATAR_SIZE) + /* server can provide a different size from the requested in URL */ + picture = gdk_pixbuf_new_from_file_at_scale( + filename, AVATAR_SIZE, AVATAR_SIZE, TRUE, &error); + else /* exact size */ + picture = gdk_pixbuf_new_from_file(filename, &error); + + if (error != NULL) { + g_warning("failed to load image '%s': %s", filename, error->message); + g_error_free(error); + } else { + if (!picture) + g_warning("failed to load image '%s': no error returned!", filename); + } + + return picture; +} + +static GdkPixbuf *pixbuf_from_url(const gchar *url, const gchar *md5, const gchar *filename) { + GdkPixbuf *image = NULL; + FILE *file; + CURL *curl; + long filesize; + + file = fopen(filename, "wb"); + if (file == NULL) { + g_warning("could not open '%s' for writing", filename); + return NULL; + } + curl = curl_easy_init(); + if (curl == NULL) { + g_warning("could not initialize curl to get image from URL"); + return NULL; + } + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_image_data_cb); + /* make sure timeout is less than general IO timeout */ + curl_easy_setopt(curl, CURLOPT_TIMEOUT, + (libravatarprefs.timeout == 0 + || libravatarprefs.timeout + > prefs_common_get_prefs()->io_timeout_secs) + ? prefs_common_get_prefs()->io_timeout_secs + : libravatarprefs.timeout); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + + if (libravatarprefs.allow_redirects) { + long maxredirs = (libravatarprefs.default_mode == DEF_MODE_URL)? 3L + : ((libravatarprefs.default_mode == DEF_MODE_MM)? 2L: 1L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_MAXREDIRS, maxredirs); + } + curl_easy_setopt(curl, CURLOPT_FILE, file); + debug_print("retrieving URL to file: %s -> %s\n", url, filename); + curl_easy_perform(curl); + filesize = ftell(file); + fclose(file); + if (filesize < MIN_PNG_SIZE) + debug_print("not enough data for an avatar image: %ld bytes\n", filesize); + else + image = image_pixbuf_from_filename(filename); + + if (!libravatarprefs.cache_icons || filesize == 0) { + if (g_unlink(filename) < 0) + g_warning("failed to delete cache file '%s'", filename); + } + + if (filesize == 0) + missing_add_md5(libravatarmisses, md5); + + curl_easy_cleanup(curl); + + return image; +} + +static void *get_image_thread(void *arg) { + AvatarImageFetch *ctx = (AvatarImageFetch *)arg; + + /* get image */ + ctx->pixbuf = pixbuf_from_url(ctx->url, ctx->md5, ctx->filename); + /* done here */ + ctx->ready = TRUE; + + return arg; +} + +GdkPixbuf *libravatar_image_fetch(AvatarImageFetch *ctx) +{ +#ifdef USE_PTHREAD + pthread_t pt; +#endif + + g_return_if_fail(ctx != NULL); + +#ifdef USE_PTHREAD + if (pthread_create(&pt, PTHREAD_CREATE_JOINABLE, get_image_thread, (void *)ctx) != 0) { + debug_print("synchronous image fetching (couldn't create thread)\n"); + get_image_thread(ctx); + } else { + debug_print("waiting for thread completion\n"); + /* + while (!ctx->ready ) { + claws_do_idle(); + } + */ + pthread_join(pt, NULL); + debug_print("thread completed\n"); + } +#else + debug_print("synchronous image fetching (pthreads unavailable)\n"); + get_image_thread(ctx); +#endif + if (ctx->pixbuf == NULL) { + g_warning("could not get image"); + } + return ctx->pixbuf; +} diff --git a/src/plugins/libravatar/libravatar_image.h b/src/plugins/libravatar/libravatar_image.h new file mode 100644 index 000000000..dc298b75c --- /dev/null +++ b/src/plugins/libravatar/libravatar_image.h @@ -0,0 +1,37 @@ +/* + * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2016 Ricardo Mones and the Claws Mail Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef __LIBRAVATAR_IMAGE_H +#define __LIBRAVATAR_IMAGE_H + +#include + +typedef struct _AvatarImageFetch AvatarImageFetch; + +GdkPixbuf *libravatar_image_fetch(AvatarImageFetch *ctx); + +struct _AvatarImageFetch +{ + const gchar *url; + const gchar *md5; + gchar *filename; + GdkPixbuf *pixbuf; + gboolean ready; +}; + +#endif -- 2.25.1