[Commits] [SCM] claws branch, master, updated. 3.9.3-32-g7593136

mones at claws-mail.org mones at claws-mail.org
Tue Mar 11 00:35:21 CET 2014


The branch master of project "claws" (Claws Mail) has been updated
       via  7593136257a76ae5618fdcaeacd435475deee5e8 (commit)
      from  c8409a47933b566ff93f2805e91980efd7df1c7d (commit)


- Log -----------------------------------------------------------------
commit 7593136257a76ae5618fdcaeacd435475deee5e8
Author: Ricardo Mones <ricardo at mones.org>
Date:   Tue Mar 11 00:33:43 2014 +0100

    New libravatar plugin

diff --git a/configure.ac b/configure.ac
index 9bbd9ae..474971f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -964,6 +964,10 @@ AC_ARG_ENABLE(geolocation-plugin,
 		[  --disable-geolocation-plugin    Do not build geolocation plugin],
 		[enable_geolocation_plugin=$enableval], [enable_geolocation_plugin=auto])
 
+AC_ARG_ENABLE(libravatar-plugin,
+		[  --disable-libravatar-plugin     Do not build libravatar  plugin],
+		[enable_libravatar_plugin=$enableval], [enable_libravatar_plugin=auto])
+
 AC_ARG_ENABLE(mailmbox-plugin,
 		[  --disable-mailmbox-plugin       Do not build mailmbox plugin],
 		[enable_mailmbox_plugin=$enableval], [enable_mailmbox_plugin=auto])
@@ -1470,6 +1474,31 @@ else
 	AC_MSG_RESULT(no)
 fi
 
+AC_MSG_CHECKING([whether to build libravatar plugin])
+if test x"$enable_libravatar_plugin" != xno; then
+	dependencies_missing=""
+
+	if test x"$HAVE_CURL" = xno; then
+		dependencies_missing="libcurl $dependencies_missing"
+	fi
+
+	if test x"$dependencies_missing" = x; then
+		PLUGINS="$PLUGINS libravatar"
+		AC_MSG_RESULT(yes)
+	elif test x"$enable_libravatar_plugin" = xauto; then
+		AC_MSG_RESULT(no)
+		AC_MSG_WARN("Plugin libravatar will not be built; missing $dependencies_missing")
+		enable_rssyl_plugin=no
+		MISSING_DEPS_PLUGINS="$MISSING_DEPS_PLUGINS libravatar"
+	else
+		AC_MSG_RESULT(no)
+		AC_MSG_ERROR("Plugin libravatar cannot be built; missing $dependencies_missing")
+	fi
+else
+	DISABLED_PLUGINS="$DISABLED_PLUGINS libravatar"
+	AC_MSG_RESULT(no)
+fi
+
 AC_MSG_CHECKING([whether to build mailmbox plugin])
 if test x"$enable_mailmbox_plugin" != xno; then
 	PLUGINS="$PLUGINS mailmbox"
@@ -1830,6 +1859,7 @@ AM_CONDITIONAL(BUILD_FANCY_PLUGIN,		test x"$enable_fancy_plugin" != xno)
 AM_CONDITIONAL(BUILD_FETCHINFO_PLUGIN,		test x"$enable_fetchinfo_plugin" != xno)
 AM_CONDITIONAL(BUILD_GDATA_PLUGIN,		test x"$enable_gdata_plugin" != xno)
 AM_CONDITIONAL(BUILD_GEOLOCATION_PLUGIN,	test x"$enable_geolocation_plugin" != xno)
+AM_CONDITIONAL(BUILD_LIBRAVATAR_PLUGIN,		test x"$enable_libravatar_plugin" != xno)
 AM_CONDITIONAL(BUILD_MAILMBOX_PLUGIN,		test x"$enable_mailmbox_plugin" != xno)
 AM_CONDITIONAL(BUILD_NEWMAIL_PLUGIN,		test x"$enable_newmail_plugin" != xno)
 AM_CONDITIONAL(BUILD_NOTIFICATION_PLUGIN,	test x"$enable_notification_plugin" != xno)
@@ -1877,6 +1907,7 @@ src/plugins/fancy/Makefile
 src/plugins/fetchinfo/Makefile
 src/plugins/gdata/Makefile
 src/plugins/geolocation/Makefile
+src/plugins/libravatar/Makefile
 src/plugins/mailmbox/Makefile
 src/plugins/newmail/Makefile
 src/plugins/notification/Makefile
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
index d440d96..2914aa5 100644
--- a/src/plugins/Makefile.am
+++ b/src/plugins/Makefile.am
@@ -12,6 +12,7 @@ SUBDIRS = \
 	fetchinfo \
 	gdata \
 	geolocation \
+	libravatar \
 	mailmbox \
 	newmail \
 	notification \
diff --git a/src/plugins/libravatar/Makefile.am b/src/plugins/libravatar/Makefile.am
new file mode 100644
index 0000000..e49d965
--- /dev/null
+++ b/src/plugins/libravatar/Makefile.am
@@ -0,0 +1,74 @@
+EXTRA_DIST = claws.def plugin.def version.rc
+
+if OS_WIN32
+
+LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RC) \
+     `echo $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) | \
+     sed -e 's/-I/--include-dir /g;s/-D/--define /g'`
+
+%.lo : %.rc
+	$(LTRCCOMPILE) -i $< -o $@
+
+plugin_res = version.lo
+plugin_res_ldflag = -Wl,.libs/version.o
+
+export_symbols = -export-symbols $(srcdir)/plugin.def
+
+plugin_deps = libclaws.a $(plugin_res) plugin.def
+
+libclaws.a: claws.def
+	$(DLLTOOL) --output-lib $@ --def $<
+
+plugin_ldadd = -L. -lclaws
+
+else
+plugin_res =
+plugin_res_ldflag =
+export_symbols =
+plugin_deps =
+plugin_ldadd =
+endif
+
+if PLATFORM_WIN32
+no_undefined = -no-undefined
+else
+no_undefined =
+endif
+
+if CYGWIN
+cygwin_export_lib = -L$(top_builddir)/src -lclaws-mail
+else
+cygwin_export_lib =
+endif
+
+plugindir = $(pkglibdir)/plugins
+
+if BUILD_LIBRAVATAR_PLUGIN
+plugin_LTLIBRARIES = libravatar.la
+endif
+
+libravatar_la_LDFLAGS = \
+	$(plugin_res_ldflag) $(no_undefined) $(export_symbols) \
+	-avoid-version -module \
+	$(GTK_LIBS)
+
+libravatar_la_DEPENDENCIES = $(plugin_deps)
+
+libravatar_la_LIBADD = $(plugin_ldadd) $(cygwin_export_lib) \
+	$(GTK_LIBS)
+
+INCLUDES = \
+	-I$(top_srcdir)/src \
+	-I$(top_srcdir)/src/common \
+	-I$(top_builddir)/src/common \
+	-I$(top_srcdir)/src/gtk
+
+AM_CPPFLAGS = \
+	$(GLIB_CFLAGS) \
+	$(GTK_CFLAGS)
+
+libravatar_la_SOURCES = \
+	libravatar.c libravatar.h \
+	libravatar_prefs.c libravatar_prefs.h \
+	libravatar_missing.c libravatar_missing.h
+
diff --git a/src/plugins/libravatar/README b/src/plugins/libravatar/README
new file mode 100644
index 0000000..fdd9e71
--- /dev/null
+++ b/src/plugins/libravatar/README
@@ -0,0 +1,39 @@
+Libravatar plugin for Claws Mail
+--------------------------------
+
+This plugin allows showing the profile picture associated to email
+addresses provided by https://www.libravatar.org/. You can read
+more about what is this at http://wiki.libravatar.org/description/.
+
+By default missing profiles in the libravatar site are also searched
+in http://gravatar.com, so it will also show pictures from gravatar
+profiles. This can be turned off by disallowing redirects.
+
+When profile is missing in both sites there's options to use a
+'generated' avatar instead. It's also possible to not generate
+anything. In that case no image will be added and the existing
+Face/X-Face machinery will operate as usual, allowing to show the
+avatar images provided by those headers when no profile picture
+exists in the servers.
+
+This plugin uses libcurl to download images, so proxy support is
+already implemented. You just need to set the “http_proxy” variable
+in your environment before launching Claws Mail (see ENVIRONMENT
+section in curl(1) manpage for details).
+
+This plugin saves retrieved images for later reuse instead of making
+a network request on every message. Cache directory is in
+~/.claws-mail/avatarcache. Does also save the missing ones in a
+special file to avoid filling the cache with empty files. You can
+control how much time will be kept there before trying to download
+them again. Missing items are valid for more time, 7 times your
+configured interval cache time. You can disable the image cache,
+but notice that write access to avatarcache is still required.
+Missing cache cannot be disabled.
+
+The TODO file contains more known issues/enhancements, read it before
+reporting bugs.
+
+-- 
+Ricardo Mones <ricardo at mones.org>
+
diff --git a/src/plugins/libravatar/TODO b/src/plugins/libravatar/TODO
new file mode 100644
index 0000000..c21ff67
--- /dev/null
+++ b/src/plugins/libravatar/TODO
@@ -0,0 +1,27 @@
+Enhancements, possibilities and random ideas
+--------------------------------------------
+
+- Retrieve federated domain records (!)
+- Support federated IDN domains (??) (Claws Mail itself doesn't support it
+  http://www.thewildbeast.co.uk/claws-mail/bugzilla/show_bug.cgi?id=1670)
+- Domain validation when searching for federated avatars, or at least
+  exclude some domains (eg.: localhost) (???)
+- Check box to enable/disable domain validation (?)
+  http://data.iana.org/TLD/tlds-alpha-by-domain.txt may be useful.
+- Button for checking custom default URL is not 404 (!) check on apply (?)
+- Make it run in cache-less mode if cache dir cannot be created (??)
+- Only cache "mystery man" once for all hashes (what if changes) (?)
+- Cache information label (size on disk/# of avatars/missing cached) (?)
+- Empty avatar/missing cache button(s) (?)
+- Run network retrieval in a separate thread started at header capture
+  hook (??)
+- Alternate proxy support (send avatar requests through a proxy which
+  is not the one in http_proxy environment variable) (??)
+- Limit avatar download size (??) or time (???)
+- Configure missing factor/time separately (??)
+
+As these are already known, please do not request enhancement bugs for any
+of the above unless you have a working patch to be attached to the bug ;-)
+
+Discussion on ML is welcome, specially for items with one or more question
+marks.
diff --git a/src/plugins/libravatar/claws.def b/src/plugins/libravatar/claws.def
new file mode 100644
index 0000000..e7533f6
--- /dev/null
+++ b/src/plugins/libravatar/claws.def
@@ -0,0 +1,25 @@
+LIBRARY CLAWS-MAIL.EXE
+EXPORTS
+get_locale_dir
+check_plugin_version
+conv_codeset_strdup
+conv_get_locale_charset_str_no_utf8
+debug_print_real
+debug_srcname
+get_rc_dir
+hooks_register_hook
+hooks_unregister_hook
+line_has_quote_char
+pref_get_escaped_pref
+pref_get_unescaped_pref
+prefs_common
+prefs_file_close
+prefs_file_close_revert
+prefs_gtk_register_page
+prefs_gtk_unregister_page
+prefs_read_config
+prefs_set_block_label
+prefs_set_default
+prefs_write_open
+prefs_write_param
+prefs_common_get_prefs
diff --git a/src/plugins/libravatar/libravatar.c b/src/plugins/libravatar/libravatar.c
new file mode 100644
index 0000000..9f40a95
--- /dev/null
+++ b/src/plugins/libravatar/libravatar.c
@@ -0,0 +1,480 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2014 Hiroyuki Yamamoto and the Claws Mail Team
+ * Copyright (C) 2014 Ricardo Mones
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <curl/curl.h>
+
+#include "version.h"
+#include "libravatar.h"
+#include "libravatar_prefs.h"
+#include "libravatar_missing.h"
+#include "prefs_common.h"
+#include "procheader.h"
+#include "procmsg.h"
+#include "utils.h"
+#include "md5.h"
+
+/* indexes of keys are default_mode - 10 if applicable */
+static const char *def_mode[] = {
+	"404",	/* not used, only useful in web pages */
+	"mm",
+	"identicon",
+	"monsterid",
+	"wavatar",
+	"retro"
+};
+
+static guint update_hook_id;
+static guint render_hook_id;
+static gchar *cache_dir = NULL; /* dir-separator terminated */
+static GHashTable *misses;
+
+static gboolean libravatar_header_update_hook(gpointer source, gpointer data)
+{
+	AvatarCaptureData *acd = (AvatarCaptureData *)source;
+
+	debug_print("libravatar avatar_header_update invoked\n");
+
+	if (!strcmp(acd->header, "From:")) {
+		gchar *a = g_strdup(acd->content);
+		extract_address(a);
+		debug_print("libravatar added '%s'\n", a);
+		procmsg_msginfo_add_avatar(acd->msginfo, AVATAR_LIBRAVATAR, a);
+	}
+
+	return FALSE; /* keep getting */
+}
+
+static gchar *federated_base_url_from_address(const gchar *address)
+{
+	/*
+	   TODO: no federation supported right now
+	   Details on http://wiki.libravatar.org/running_your_own/
+	 */
+	return g_strdup(libravatarprefs.base_url);
+}
+
+static GtkWidget *image_widget_from_filename(const gchar *filename)
+{
+	GtkWidget *image = NULL;
+	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\n", 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!\n", filename);
+	}
+
+	return image;
+}
+
+static gchar *cache_name_for_md5(const gchar *md5)
+{
+	if (libravatarprefs.default_mode >= DEF_MODE_MM
+			&& libravatarprefs.default_mode <= DEF_MODE_RETRO) {
+		/* cache dir for generated avatars */
+		return g_strconcat(cache_dir, def_mode[libravatarprefs.default_mode - 10],
+				   G_DIR_SEPARATOR_S, md5, NULL);
+	}
+	/* default cache dir */
+	return g_strconcat(cache_dir, md5, NULL);
+}
+
+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 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\n");
+		return NULL;
+	}
+	curl_easy_setopt(curl, CURLOPT_URL, url);
+	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_image_data_cb);
+
+	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_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) {
+			if (g_unlink(filename) < 0)
+				g_warning("failed to delete cache file %s\n", filename);
+		}
+
+		if (filesize == 0)
+			missing_add_md5(misses, md5);
+	} else {
+		g_warning("could not open '%s' for writting\n", filename);
+	}
+	curl_easy_cleanup(curl);
+	g_free(filename);
+
+	return image;
+}
+
+static gboolean is_recent_enough(const gchar *filename)
+{
+	struct stat s;
+	time_t t;
+
+	if (libravatarprefs.cache_icons) {
+		t = time(NULL);
+		if (t != (time_t)-1 && !g_stat(filename, &s)) {
+			if (t - s.st_ctime <= libravatarprefs.cache_interval * 3600)
+				return TRUE;
+		}
+	}
+
+	return FALSE; /* re-download */
+}
+
+static GtkWidget *image_widget_from_cached_md5(const gchar *md5)
+{
+	GtkWidget *image = NULL;
+	gchar *filename;
+
+	filename = cache_name_for_md5(md5);
+	if (is_file_exist(filename) && is_recent_enough(filename)) {
+		debug_print("found cached image for %s\n", md5);
+		image = image_widget_from_filename(filename);
+	}
+	g_free(filename);
+
+	return image;
+}
+
+static gchar *libravatar_url_for_md5(const gchar *base, const gchar *md5)
+{
+	if (libravatarprefs.default_mode >= DEF_MODE_404) {
+		return g_strdup_printf("%s/%s?s=%u&d=%s",
+				base, md5, AVATAR_SIZE,
+				def_mode[libravatarprefs.default_mode - 10]);
+	} else if (libravatarprefs.default_mode == DEF_MODE_URL) {
+		return g_strdup_printf("%s/%s?s=%u&d=%s",
+				base, md5, AVATAR_SIZE,
+				libravatarprefs.default_mode_url);
+	} else if (libravatarprefs.default_mode == DEF_MODE_NONE) {
+		return g_strdup_printf("%s/%s?s=%u",
+				base, md5, AVATAR_SIZE);
+	}
+
+	g_warning("invalid libravatar default mode: %d\n", libravatarprefs.default_mode);
+	return NULL;
+}
+
+static gboolean libravatar_image_render_hook(gpointer source, gpointer data)
+{
+	AvatarRender *ar = (AvatarRender *)source;
+	GtkWidget *image = NULL;
+	gchar *a = NULL, *url = NULL;
+	gchar md5sum[33];
+
+	debug_print("libravatar avatar_image_render invoked\n");
+
+	a = procmsg_msginfo_get_avatar(ar->full_msginfo, AVATAR_LIBRAVATAR);
+	if (a != NULL) {
+		gchar *base;
+
+		md5_hex_digest(md5sum, a);
+		/* try missing cache */
+		if (is_missing_md5(misses, md5sum)) {
+			return FALSE;
+		}
+		/* try disk cache */
+		image = image_widget_from_cached_md5(md5sum);
+		if (image != NULL) {
+			if (ar->image) /* previous plugin set one */
+				gtk_widget_destroy(ar->image);
+			ar->image = image;
+			return FALSE;
+		}
+		/* not cached copy: try network */
+		if (prefs_common.work_offline) {
+			debug_print("working off-line: libravatar network retrieval skipped\n");
+			return FALSE;
+		}
+		base = federated_base_url_from_address(a);
+		url = libravatar_url_for_md5(base, md5sum);
+		if (url != NULL) {
+			image = image_widget_from_url(url, md5sum);
+			g_free(url);
+			if (image != NULL) {
+				if (ar->image) /* previous plugin set one */
+					gtk_widget_destroy(ar->image);
+				ar->image = image;
+			}
+		}
+		g_free(base);
+
+		return TRUE;
+	}
+
+	return FALSE; /* keep rendering */
+}
+
+static gint cache_dir_init()
+{
+	gchar *subdir;
+	int i;
+
+	cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+				LIBRAVATAR_CACHE_DIR, G_DIR_SEPARATOR_S,
+				NULL);
+	if (!is_dir_exist(cache_dir)) {
+		if (make_dir(cache_dir) < 0) {
+			g_free(cache_dir);
+			return -1;
+		}
+	}
+	for (i = DEF_MODE_MM; i <= DEF_MODE_RETRO; ++i) {
+		subdir = g_strconcat(cache_dir, def_mode[i - 10], NULL);
+		if (!is_dir_exist(subdir)) {
+			if (make_dir(subdir) < 0) {
+				g_warning("cannot create directory %s\n", subdir);
+				g_free(subdir);
+				return -1;
+			}
+		}
+		g_free(subdir);
+	}
+
+	return 0;
+}
+
+static gint missing_cache_init()
+{
+	gchar *cache_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+	                                LIBRAVATAR_CACHE_DIR, G_DIR_SEPARATOR_S,
+					LIBRAVATAR_MISSING_FILE, NULL);
+
+	misses = missing_load_from_file(cache_file);
+	g_free(cache_file);
+
+	if (misses == NULL)
+		return -1;
+
+	return 0;
+}
+
+static void missing_cache_done()
+{
+	gchar *cache_file;
+
+	if (misses != NULL) {
+		cache_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
+					LIBRAVATAR_CACHE_DIR, G_DIR_SEPARATOR_S,
+					LIBRAVATAR_MISSING_FILE, NULL);
+		missing_save_to_file(misses, cache_file);
+		g_free(cache_file);
+		g_hash_table_destroy(misses);
+	}
+}
+
+/**
+ * Initialize plugin.
+ *
+ * @param error  For storing the returned error message.
+ *
+ * @return 0 if initialization succeeds, -1 on failure.
+ */
+gint plugin_init(gchar **error)
+{
+	if (!check_plugin_version(MAKE_NUMERIC_VERSION(3,9,3,29),
+				  VERSION_NUMERIC, _("Libravatar"), error))
+		return -1;
+	/* get info from headers */
+	update_hook_id = hooks_register_hook(AVATAR_HEADER_UPDATE_HOOKLIST,
+					     libravatar_header_update_hook,
+					     NULL);
+	if (update_hook_id == -1) {
+		*error = g_strdup(_("Failed to register avatar header update hook"));
+		return -1;
+	}
+	/* get image for displaying */
+	render_hook_id = hooks_register_hook(AVATAR_IMAGE_RENDER_HOOKLIST,
+					     libravatar_image_render_hook,
+					     NULL);
+	if (render_hook_id == -1) {
+		*error = g_strdup(_("Failed to register avatar image render hook"));
+		return -1;
+	}
+	/* cache dir */
+	if (cache_dir_init() == -1) {
+		*error = g_strdup(_("Failed to create avatar image cache directory"));
+		return -1;
+	}
+	/* preferences page */
+	libravatar_prefs_init();
+	/* curl library */
+	curl_global_init(CURL_GLOBAL_DEFAULT);
+	/* missing cache */
+	if (missing_cache_init() == -1) {
+		*error = g_strdup(_("Failed to load missing items cache"));
+		return -1;
+	}
+	debug_print("Libravatar plugin loaded\n");
+
+	return 0;
+}
+
+/**
+ * Destructor for the plugin.
+ * Unregister the callback function and frees matcher.
+ *
+ * @return Always TRUE.
+ */
+gboolean plugin_done(void)
+{
+	if (render_hook_id != -1) {
+		hooks_unregister_hook(AVATAR_IMAGE_RENDER_HOOKLIST,
+				      render_hook_id);
+		render_hook_id = -1;
+	}
+	if (update_hook_id != -1) {
+		hooks_unregister_hook(AVATAR_HEADER_UPDATE_HOOKLIST,
+				      update_hook_id);
+		update_hook_id = -1;
+	}
+	libravatar_prefs_done();
+	missing_cache_done();
+	if (cache_dir != NULL)
+		g_free(cache_dir);
+	debug_print("Libravatar plugin unloaded\n");
+
+	return TRUE;
+}
+
+/**
+ * Get the name of the plugin.
+ *
+ * @return The plugin's name, maybe translated.
+ */
+const gchar *plugin_name(void)
+{
+	return _("Libravatar");
+}
+
+/**
+ * Get the description of the plugin.
+ *
+ * @return The plugin's description, maybe translated.
+ */
+const gchar *plugin_desc(void)
+{
+	return _("Get and display libravatar images for mail messages.\n\n"
+		 "Info about libravatar at http://www.libravatar.org/\n\n"
+		 "Feedback to <ricardo at mones.org> is welcome.\n");
+}
+
+/**
+ * Get the kind of plugin.
+ *
+ * @return The "GTK2" constant.
+ */
+const gchar *plugin_type(void)
+{
+	return "GTK2";
+}
+
+/**
+ * Get the license acronym the plugin is released under.
+ *
+ * @return The "GPL3+" constant.
+ */
+const gchar *plugin_licence(void)
+{
+	return "GPL3+";
+}
+
+/**
+ * Get the version of the plugin.
+ *
+ * @return The current version string.
+ */
+const gchar *plugin_version(void)
+{
+	return VERSION;
+}
+
+/**
+ * Get the features implemented by the plugin.
+ *
+ * @return A constant PluginFeature structure with the features.
+ */
+struct PluginFeature *plugin_provides(void)
+{
+	static struct PluginFeature features[] =
+		{ {PLUGIN_OTHER, N_("Libravatar")},
+		  {PLUGIN_NOTHING, NULL}};
+
+	return features;
+}
+
diff --git a/src/plugins/libravatar/libravatar.h b/src/plugins/libravatar/libravatar.h
new file mode 100644
index 0000000..9bb3c2e
--- /dev/null
+++ b/src/plugins/libravatar/libravatar.h
@@ -0,0 +1,49 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2014 Hiroyuki Yamamoto and the Claws Mail Team
+ * Copyright (C) 2014 Ricardo Mones
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBRAVATAR_H
+#define __LIBRAVATAR_H
+
+#include <glib.h>
+
+#include "version.h"
+#include "claws.h"
+#include "plugin.h"
+#include "utils.h"
+#include "hooks.h"
+#include "avatars.h"
+
+#define AVATAR_LIBRAVATAR 3
+#define AVATAR_SIZE 48
+#define LIBRAVATAR_CACHE_DIR "avatarcache"
+/* https://github.com/mathiasbynens/small/pull/19 */
+#define MIN_PNG_SIZE 67L
+#define MAX_URL_LENGTH 1024
+
+gint 		plugin_init	  	  (gchar **error);
+gboolean	plugin_done		  (void);
+const gchar *	plugin_name		  (void);
+const gchar *	plugin_desc		  (void);
+const gchar *	plugin_type		  (void);
+const gchar *	plugin_licence		  (void);
+const gchar *	plugin_version		  (void);
+struct PluginFeature *plugin_provides	  (void);
+
+#endif
+
diff --git a/src/plugins/libravatar/libravatar_missing.c b/src/plugins/libravatar/libravatar_missing.c
new file mode 100644
index 0000000..6288dc7
--- /dev/null
+++ b/src/plugins/libravatar/libravatar_missing.c
@@ -0,0 +1,182 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2014 Hiroyuki Yamamoto and the Claws Mail Team
+ * Copyright (C) 2014 Ricardo Mones
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+
+#include "libravatar_missing.h"
+#include "libravatar_prefs.h"
+#include "utils.h"
+
+/**
+ * Loads the hash table of md5sum → time from the given filename.
+ *
+ * @param filename  Name of the hash table filename.
+ *
+ * @return A hash table with the entries not expired contained in
+ *         the given filename or NULL if failed to load.
+ */
+GHashTable *missing_load_from_file(const gchar *filename)
+{
+	FILE *file = fopen(filename, "r");
+	time_t t;
+	long long unsigned seen;
+	gchar md5sum[33];
+	GHashTable *table;
+	int r = 0, a = 0, d = 0;
+
+	if (file == NULL) {
+		if (!is_file_exist(filename)) { /* first run, return an empty table */
+			return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+		}
+		g_warning("Cannot open %s for reading\n", filename);
+		return NULL;
+	}
+	t = time(NULL);
+	if (t == (time_t)-1) {
+		g_warning("Cannot get time!\n");
+		return NULL;
+	}
+
+	table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+	while ((r = fscanf(file, "%s %llu\n", md5sum, &seen)) != EOF) {
+		if (t - (time_t)seen <= LIBRAVATAR_MISSING_TIME) {
+			time_t *value = g_malloc0(sizeof(time_t));
+			if (value == NULL) {
+				g_warning("Cannot allocate memory\n");
+				g_hash_table_destroy(table);
+				return NULL;
+			}
+			*value = (time_t)seen;
+			g_hash_table_insert(table, g_strdup(md5sum), value);
+		} else
+			d++;
+		a++;
+	}
+
+	if (fclose(file) != 0)
+		g_warning("Error closing %s\n", filename);
+
+	debug_print("Read %d missing avatar entries, %d obsolete entries discarded\n", a, d);
+	return table;
+}
+
+/**
+ * Saves a hash table item.
+ *
+ * @param key    Hash table key, a md5sum string.
+ * @param vakue  Hash table value, a time_t.
+ * @param data   User data, a pointer to the open FILE being written.
+ */
+static void missing_save_item(gpointer key, gpointer value, gpointer data)
+{
+	FILE *file = (FILE *)data;
+	gchar *line = g_strdup_printf("%s %llu\n", (gchar *)key, *((long long unsigned *)value));
+	if (fputs(line, file) < 0)
+		g_warning("Error saving missing item\n");
+	g_free(line);
+}
+
+/**
+ * Saves a hash table of md5sum → time to a given file name.
+ *
+ * @param table     The table to save.
+ * @param filename  The name of the file where table data will be saved.
+ *
+ * @return 0 on success, -1 if there was some problem saving.
+ */
+gint missing_save_to_file(GHashTable *table, const gchar *filename)
+{
+	FILE *file = fopen(filename, "w");
+
+	if (file == NULL) {
+		g_warning("Cannot open %s for writting\n", filename);
+		return -1;
+	}
+
+	g_hash_table_foreach(table, missing_save_item, (gpointer)file);
+	debug_print("Saved %u missing avatar entries\n", g_hash_table_size(table));
+
+	if (fclose(file) != 0) {
+		g_warning("Error closing %s\n", filename);
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * Adds a md5sum to a md5sum → time hash table.
+ * If the md5sum is already in the table its time is updated.
+ *
+ * @param table  The table to use.
+ * @param md5    The md5sum to add or update.
+ */
+void missing_add_md5(GHashTable *table, const gchar *md5)
+{
+	time_t t = time(NULL);
+
+	if (t == (time_t)-1) {
+		g_warning("Cannot get time!\n");
+		return;
+	}
+
+	time_t *seen = g_hash_table_lookup(table, md5);
+	if (seen == NULL) {
+		seen = g_malloc0(sizeof(time_t));
+		if (seen == NULL) {
+			g_warning("Cannot allocate memory\n");
+			return;
+		}
+		*seen = t;
+		g_hash_table_insert(table, g_strdup(md5), seen);
+		debug_print("New md5 %s added with time %llu\n", md5, (long long unsigned)t);
+	} else {
+		*seen = t; /* just update */
+		debug_print("Updated md5 %s with time %llu\n", md5, (long long unsigned)t);
+	}
+}
+
+/**
+ * Check if a md5sum is in hash table and not expired.
+ *
+ * @param table   The table to check against.
+ * @param md5     The md5sum to check.
+ *
+ * @return TRUE if the md5sum is in the table and is not expired,
+           FALSE otherwise.
+ */
+gboolean is_missing_md5(GHashTable *table, const gchar *md5)
+{
+	time_t t;
+	time_t *seen = (time_t *)g_hash_table_lookup(table, md5);
+
+	if (seen == NULL)
+		return FALSE;
+
+	t = time(NULL);
+	if (t != (time_t)-1) {
+		if (t - *seen <= LIBRAVATAR_MISSING_TIME) {
+			debug_print("Found missing md5 %s\n", md5);
+			return TRUE;
+		}
+	}
+	return FALSE;
+}
+
diff --git a/src/plugins/libravatar/libravatar_missing.h b/src/plugins/libravatar/libravatar_missing.h
new file mode 100644
index 0000000..191159b
--- /dev/null
+++ b/src/plugins/libravatar/libravatar_missing.h
@@ -0,0 +1,37 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2014 Hiroyuki Yamamoto and the Claws Mail Team
+ * Copyright (C) 2014 Ricardo Mones
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBRAVATAR_MISSING_H
+#define __LIBRAVATAR_MISSING_H
+
+#include <glib.h>
+
+#define LIBRAVATAR_MISSING_FILE "missing"
+/* multiply cache interval time pref for missing items */
+#define LIBRAVATAR_MISSING_TIME (libravatarprefs.cache_interval * 3600 * 7)
+
+GHashTable *missing_load_from_file	(const gchar *filename);
+gint missing_save_to_file		(GHashTable *table,
+					 const gchar *filename);
+void missing_add_md5			(GHashTable *table,
+					 const gchar *md5);
+gboolean is_missing_md5			(GHashTable *table,
+					 const gchar *md5);
+
+#endif
diff --git a/src/plugins/libravatar/libravatar_prefs.c b/src/plugins/libravatar/libravatar_prefs.c
new file mode 100644
index 0000000..c14948d
--- /dev/null
+++ b/src/plugins/libravatar/libravatar_prefs.c
@@ -0,0 +1,382 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2014 Hiroyuki Yamamoto and the Claws Mail Team
+ * Copyright (C) 2014 Ricardo Mones
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#include "claws-features.h"
+#endif
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "libravatar.h"
+
+#include <gtk/gtk.h>
+#include <gtk/filesel.h>
+
+#include "defs.h"
+#include "libravatar_prefs.h"
+#include "prefs_common.h"
+#include "prefs_gtk.h"
+
+#define PREFS_BLOCK_NAME "Libravatar"
+#define NUM_DEF_BUTTONS 7
+/* cache interval goes from 1 hour to 30 days */
+#define INTERVAL_MIN_H 1.0
+#define INTERVAL_MAX_H 720.0
+
+LibravatarPrefs libravatarprefs;
+
+struct LibravatarPrefsPage
+{
+	PrefsPage page;
+
+	GtkWidget *cache_interval_spin;
+	GtkWidget *cache_icons_check;
+	GtkWidget *defm_radio[NUM_DEF_BUTTONS];
+	GtkWidget *defm_url_text;
+	GtkWidget *allow_redirects_check;
+};
+
+struct LibravatarPrefsPage libravatarprefs_page;
+
+static PrefParam param[] = {
+	{ "base_url", "http://cdn.libravatar.org/avatar",
+	  &libravatarprefs.base_url,
+          P_STRING, NULL, NULL, NULL },
+	{ "cache_interval", "24",
+	  &libravatarprefs.cache_interval,
+          P_INT, NULL, NULL, NULL },
+	{ "cache_icons", "TRUE",
+	  &libravatarprefs.cache_icons,
+          P_BOOL, NULL, NULL, NULL },
+	{ "default_mode", "0",
+	  &libravatarprefs.default_mode,
+          P_INT, NULL, NULL, NULL },
+	{ "default_mode_url", "",
+	  &libravatarprefs.default_mode_url,
+          P_STRING, NULL, NULL, NULL },
+	{ "allow_redirects", "TRUE",
+	  &libravatarprefs.allow_redirects,
+          P_BOOL, NULL, NULL, NULL },
+	{NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL}
+};
+
+static GtkWidget *create_checkbox(gchar *label, gchar *hint)
+{
+	GtkWidget *cb = gtk_check_button_new_with_mnemonic(label);
+	CLAWS_SET_TIP(cb, hint);
+	gtk_widget_show(cb);
+
+	return cb;
+}
+
+static void cache_icons_check_toggled_cb(GtkToggleButton *button, gpointer data)
+{
+	gtk_widget_set_sensitive(libravatarprefs_page.cache_interval_spin,
+				 gtk_toggle_button_get_active(button));
+}
+
+static GtkWidget *p_create_frame_cache(struct LibravatarPrefsPage *page)
+{
+	GtkWidget *vbox, *checkbox, *lbl, *lbla, *spinner, *hbox;
+	GtkAdjustment *adj;
+
+	vbox =  gtk_vbox_new(FALSE, 6);
+
+	checkbox = create_checkbox(_("_Use cached icons"),
+				   _("Keep icons on disk for reusing instead "
+				     "of making another network request"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+				     libravatarprefs.cache_icons);
+	g_signal_connect(checkbox, "toggled",
+			 G_CALLBACK(cache_icons_check_toggled_cb), NULL);
+	page->cache_icons_check = checkbox;
+
+	lbl = gtk_label_new(_("Cache refresh interval"));
+	gtk_widget_show(lbl);
+	lbla = gtk_label_new(_("hours"));
+	gtk_widget_show(lbla);
+	adj = (GtkAdjustment *) gtk_adjustment_new(
+					libravatarprefs.cache_interval,
+					INTERVAL_MIN_H, INTERVAL_MAX_H, 1.0,
+					0.0, 0.0);
+	spinner = gtk_spin_button_new(adj, 1.0, 0);
+	gtk_widget_show(spinner);
+	gtk_widget_set_sensitive(spinner, libravatarprefs.cache_icons);
+	hbox = gtk_hbox_new(FALSE, 6);
+	gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), spinner, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), lbla, FALSE, FALSE, 0);
+	page->cache_interval_spin = spinner;
+
+	gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+	return vbox;
+}
+
+static void default_mode_radio_button_cb(GtkToggleButton *button, gpointer data)
+{
+	gboolean is_url;
+
+	if (gtk_toggle_button_get_active(button) == TRUE) {
+		is_url = (*((guint *)data) == DEF_MODE_URL)? TRUE: FALSE;
+		gtk_widget_set_sensitive(libravatarprefs_page.defm_url_text, is_url);
+		if (is_url) /* custom URL requires following redirects */
+			gtk_toggle_button_set_active(
+				GTK_TOGGLE_BUTTON(libravatarprefs_page.allow_redirects_check),
+				TRUE);
+		/* don't waste time with headers that won't be displayed */
+		prefs_common.enable_avatars = (*((guint *)data) == DEF_MODE_NONE)
+						? AVATARS_ENABLE_BOTH: AVATARS_DISABLE;
+	}
+}
+
+static const guint radio_value[] = {
+	DEF_MODE_NONE,
+	DEF_MODE_MM,
+	DEF_MODE_IDENTICON,
+	DEF_MODE_MONSTERID,
+	DEF_MODE_WAVATAR,
+	DEF_MODE_RETRO,
+	DEF_MODE_URL
+};
+
+static GtkWidget *p_create_frame_missing(struct LibravatarPrefsPage *page)
+{
+	GtkWidget *vbox, *radio[NUM_DEF_BUTTONS], *hbox, *label, *entry;
+	gboolean enable = FALSE;
+	int i;
+	gchar *radio_label[] = {
+		_("None"),
+		_("Mystery man"),
+		_("Identicon"),
+		_("MonsterID"),
+		_("Wavatar"),
+		_("Retro"),
+		_("Custom URL")
+	};
+	gchar *radio_hint[] = {
+		_("A blank image"),
+		_("The unobtrusive low-contrast greyish silhouette"),
+		_("A generated geometric pattern"),
+		_("A generated full-body monster"),
+		_("A generated almost unique face"),
+		_("A generated 8-bit arcade-style pixelated image"),
+		_("Redirect to a user provided URL")
+	};
+
+	vbox =  gtk_vbox_new(FALSE, 6);
+
+	for (i = 0; i < NUM_DEF_BUTTONS; ++i) {
+		enable = (!enable && libravatarprefs.default_mode == radio_value[i])? TRUE: FALSE;
+		radio[i] = gtk_radio_button_new_with_label_from_widget(
+				(i > 0)? GTK_RADIO_BUTTON(radio[i - 1]): NULL, radio_label[i]);
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio[i]), enable);
+		gtk_box_pack_start(GTK_BOX(vbox), radio[i], FALSE, FALSE, 0);
+		g_signal_connect(radio[i], "toggled",
+				 G_CALLBACK(default_mode_radio_button_cb),
+				 (gpointer) &(radio_value[i]));
+		CLAWS_SET_TIP(radio[i], radio_hint[i]);
+		gtk_widget_show(radio[i]);
+		page->defm_radio[i] = radio[i];
+	}
+	if (!enable) { /* unknown value, go default */
+		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio[0]), TRUE);
+		libravatarprefs.default_mode = DEF_MODE_NONE;
+	}
+	/* don't waste time with headers that won't be displayed */
+	prefs_common.enable_avatars = (libravatarprefs.default_mode == DEF_MODE_NONE)
+						? AVATARS_ENABLE_BOTH: AVATARS_DISABLE;
+
+	label = gtk_label_new(_("URL:"));
+	gtk_widget_show(label);
+	entry = gtk_entry_new_with_max_length(MAX_URL_LENGTH);
+	gtk_widget_show(entry);
+	gtk_entry_set_text(GTK_ENTRY(entry), libravatarprefs.default_mode_url);
+
+	hbox = gtk_hbox_new(FALSE, 6);
+	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
+	gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 0);
+	gtk_widget_set_sensitive(entry,
+		(libravatarprefs.default_mode == DEF_MODE_URL)? TRUE: FALSE);
+	page->defm_url_text = entry;
+
+	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+
+	return vbox;
+}
+
+static GtkWidget *p_create_frame_network(struct LibravatarPrefsPage *page)
+{
+	GtkWidget *vbox, *checkbox;
+
+	vbox =  gtk_vbox_new(FALSE, 6);
+
+	checkbox = create_checkbox(_("_Allow redirects to other sites"),
+				   _("Follow redirect responses received from "
+				     "libravatar server to other avatar "
+				     "services like gravatar.com"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbox),
+				     libravatarprefs.allow_redirects);
+	page->allow_redirects_check = checkbox;
+
+	gtk_box_pack_start(GTK_BOX(vbox), checkbox, FALSE, FALSE, 0);
+
+	return vbox;
+}
+
+/*
+  ┌─Icon cache───────────────────────────────────────────┐
+  │ [✔] Use cached icons                                 │
+  │ Cache refresh interval [ 24 |⬘] hours                │
+  └──────────────────────────────────────────────────────┘
+  ┌─Default missing icon mode────────────────────────────┐
+  │ (•) None                                             │
+  │ ( ) Mystery man                                      │
+  │ ( ) Identicon                                        │
+  │ ( ) MonsterID                                        │
+  │ ( ) Wavatar                                          │
+  │ ( ) Retro                                            │
+  │ ( ) Custom URL                                       │
+  │     URL: [_________________________________________] │
+  └──────────────────────────────────────────────────────┘
+  ┌─Network──────────────────────────────────────────────┐
+  │ [✔] Allow redirects                                  │
+  └──────────────────────────────────────────────────────┘
+ */
+static void libravatar_prefs_create_widget_func(PrefsPage * _page,
+					        GtkWindow * window,
+					        gpointer data)
+{
+	struct LibravatarPrefsPage *page = (struct LibravatarPrefsPage *) _page;
+	GtkWidget *vbox, *vbox1, *vbox2, *vbox3, *frame;
+
+	vbox1 = p_create_frame_cache(page);
+	vbox2 = p_create_frame_missing(page);
+	vbox3 = p_create_frame_network(page);
+
+	vbox = gtk_vbox_new(FALSE, 6);
+	gtk_container_set_border_width(GTK_CONTAINER(vbox), VBOX_BORDER);
+
+	PACK_FRAME (vbox, frame, _("Icon cache"));
+	gtk_container_set_border_width(GTK_CONTAINER(vbox1), 6);
+	gtk_container_add(GTK_CONTAINER(frame), vbox1);
+
+	PACK_FRAME (vbox, frame, _("Default missing icon mode"));
+	gtk_container_set_border_width(GTK_CONTAINER(vbox2), 6);
+	gtk_container_add(GTK_CONTAINER(frame), vbox2);
+
+	PACK_FRAME (vbox, frame, _("Network"));
+	gtk_container_set_border_width(GTK_CONTAINER(vbox3), 6);
+	gtk_container_add(GTK_CONTAINER(frame), vbox3);
+
+	gtk_widget_show_all(vbox);
+	page->page.widget = vbox;
+}
+
+static void libravatar_prefs_destroy_widget_func(PrefsPage *_page)
+{
+	/* nothing */
+}
+
+static void libravatar_save_config(void)
+{
+	PrefFile *pfile;
+	gchar *rcpath;
+
+	debug_print("Saving Libravatar Page\n");
+
+	rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+	pfile = prefs_write_open(rcpath);
+	g_free(rcpath);
+	if (!pfile || (prefs_set_block_label(pfile, PREFS_BLOCK_NAME) < 0))
+		return;
+
+	if (prefs_write_param(param, pfile->fp) < 0) {
+		g_warning("Failed to write Libravatar configuration to file\n");
+		prefs_file_close_revert(pfile);
+		return;
+	}
+        if (fprintf(pfile->fp, "\n") < 0) {
+		FILE_OP_ERROR(rcpath, "fprintf");
+		prefs_file_close_revert(pfile);
+	} else
+	        prefs_file_close(pfile);
+}
+
+static void libravatar_prefs_save_func(PrefsPage * _page)
+{
+	struct LibravatarPrefsPage *page = (struct LibravatarPrefsPage *) _page;
+	int i;
+
+	/* cache */
+	libravatarprefs.cache_icons = gtk_toggle_button_get_active(
+		GTK_TOGGLE_BUTTON(page->cache_icons_check));
+	/* cache interval */
+	libravatarprefs.cache_interval = gtk_spin_button_get_value_as_int(
+		GTK_SPIN_BUTTON(page->cache_interval_spin));
+	/* default mode */
+	for (i = 0; i < NUM_DEF_BUTTONS; ++i) {
+		if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(page->defm_radio[i]))) {
+			libravatarprefs.default_mode = radio_value[i];
+			break;
+		}
+	}
+	/* custom url */
+	if (libravatarprefs.default_mode_url != NULL) {
+		g_free(libravatarprefs.default_mode_url);
+	}
+	libravatarprefs.default_mode_url = gtk_editable_get_chars(
+		GTK_EDITABLE(page->defm_url_text), 0, -1);
+	/* redirects */
+	libravatarprefs.allow_redirects = gtk_toggle_button_get_active(
+		GTK_TOGGLE_BUTTON(page->allow_redirects_check));
+
+	libravatar_save_config();
+}
+
+void libravatar_prefs_init(void)
+{
+	static gchar *path[3];
+	gchar *rcpath;
+
+	path[0] = _("Plugins");
+	path[1] = _("Libravatar");
+	path[2] = NULL;
+
+	prefs_set_default(param);
+	rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, COMMON_RC, NULL);
+	prefs_read_config(param, PREFS_BLOCK_NAME, rcpath, NULL);
+	g_free(rcpath);
+
+	libravatarprefs_page.page.path = path;
+	libravatarprefs_page.page.create_widget = libravatar_prefs_create_widget_func;
+	libravatarprefs_page.page.destroy_widget = libravatar_prefs_destroy_widget_func;
+	libravatarprefs_page.page.save_page = libravatar_prefs_save_func;
+
+	prefs_gtk_register_page((PrefsPage *) &libravatarprefs_page);
+}
+
+void libravatar_prefs_done(void)
+{
+	prefs_gtk_unregister_page((PrefsPage *) &libravatarprefs_page);
+}
+
diff --git a/src/plugins/libravatar/libravatar_prefs.h b/src/plugins/libravatar/libravatar_prefs.h
new file mode 100644
index 0000000..fe8ed6a
--- /dev/null
+++ b/src/plugins/libravatar/libravatar_prefs.h
@@ -0,0 +1,56 @@
+/*
+ * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
+ * Copyright (C) 1999-2014 Hiroyuki Yamamoto and the Claws Mail Team
+ * Copyright (C) 2014 Ricardo Mones
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LIBRAVATAR_PREFS_H
+#define __LIBRAVATAR_PREFS_H
+
+#include <glib.h>
+
+typedef struct _LibravatarPrefs LibravatarPrefs;
+
+/* http://wiki.libravatar.org/api/ */
+enum
+{
+	DEF_MODE_NONE		= 0,
+	DEF_MODE_URL		= 1,
+	DEF_MODE_404		= 10, /* not used, only useful in web pages */
+	DEF_MODE_MM		= 11,
+	DEF_MODE_IDENTICON	= 12,
+	DEF_MODE_MONSTERID	= 13,
+	DEF_MODE_WAVATAR	= 14,
+	DEF_MODE_RETRO		= 15,
+};
+
+struct _LibravatarPrefs
+{
+	gchar		*base_url; /* hidden pref */
+	guint		cache_interval;
+	gboolean	cache_icons;
+	guint		default_mode;
+	gchar		*default_mode_url;
+	gboolean	allow_redirects;
+};
+
+extern LibravatarPrefs libravatarprefs;
+
+void libravatar_prefs_init(void);
+void libravatar_prefs_done(void);
+
+#endif
+
diff --git a/src/plugins/libravatar/plugin.def b/src/plugins/libravatar/plugin.def
new file mode 100644
index 0000000..8471df1
--- /dev/null
+++ b/src/plugins/libravatar/plugin.def
@@ -0,0 +1,10 @@
+EXPORTS
+	plugin_desc
+	plugin_done
+	plugin_init
+	plugin_licence
+	plugin_name
+	plugin_type
+	plugin_provides
+	plugin_version
+
diff --git a/src/plugins/libravatar/version.rc b/src/plugins/libravatar/version.rc
new file mode 100644
index 0000000..5d93a3d
--- /dev/null
+++ b/src/plugins/libravatar/version.rc
@@ -0,0 +1,36 @@
+1 VERSIONINFO
+ FILEVERSION 0, 0, 0, 0
+ PRODUCTVERSION 0, 0, 0, 0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "000004b0"
+        BEGIN
+            VALUE "FileDescription", "Claws Mail Libravatar Plugin\0"
+            VALUE "FileVersion", "0.0.0.0\0"
+            VALUE "ProductVersion", "0.0.0.0 Win32\0"
+            VALUE "LegalCopyright", "GPL / © 1999-2014 Hiroyuki Yamamoto & The Claws Mail Team\0"
+            VALUE "CompanyName", "GNU / Free Software Foundation\0"
+            VALUE "ProductName", "Claws Mail\0"
+//            VALUE "Comments", "\0"
+//            VALUE "InternalName", "\0"
+//            VALUE "LegalTrademarks", "\0"
+//            VALUE "OriginalFilename", "\0"
+//            VALUE "PrivateBuild", "\0"
+//            VALUE "SpecialBuild", "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x0, 1200
+    END
+END

-----------------------------------------------------------------------

Summary of changes:
 configure.ac                                       |   31 ++
 src/plugins/Makefile.am                            |    1 +
 .../{address_keeper => libravatar}/Makefile.am     |   19 +-
 src/plugins/libravatar/README                      |   39 ++
 src/plugins/libravatar/TODO                        |   27 ++
 src/plugins/{mailmbox => libravatar}/claws.def     |    7 +-
 src/plugins/libravatar/libravatar.c                |  480 ++++++++++++++++++++
 .../address_keeper.h => libravatar/libravatar.h}   |   25 +-
 src/plugins/libravatar/libravatar_missing.c        |  182 ++++++++
 src/plugins/libravatar/libravatar_missing.h        |   37 ++
 src/plugins/libravatar/libravatar_prefs.c          |  382 ++++++++++++++++
 src/plugins/libravatar/libravatar_prefs.h          |   56 +++
 src/plugins/libravatar/plugin.def                  |   10 +
 src/plugins/{att_remover => libravatar}/version.rc |    4 +-
 14 files changed, 1272 insertions(+), 28 deletions(-)
 copy src/plugins/{address_keeper => libravatar}/Makefile.am (75%)
 create mode 100644 src/plugins/libravatar/README
 create mode 100644 src/plugins/libravatar/TODO
 copy src/plugins/{mailmbox => libravatar}/claws.def (84%)
 create mode 100644 src/plugins/libravatar/libravatar.c
 copy src/plugins/{address_keeper/address_keeper.h => libravatar/libravatar.h} (63%)
 create mode 100644 src/plugins/libravatar/libravatar_missing.c
 create mode 100644 src/plugins/libravatar/libravatar_missing.h
 create mode 100644 src/plugins/libravatar/libravatar_prefs.c
 create mode 100644 src/plugins/libravatar/libravatar_prefs.h
 create mode 100644 src/plugins/libravatar/plugin.def
 copy src/plugins/{att_remover => libravatar}/version.rc (87%)


hooks/post-receive
-- 
Claws Mail


More information about the Commits mailing list