From: Ricardo Mones Date: Mon, 10 Mar 2014 23:33:43 +0000 (+0100) Subject: New libravatar plugin X-Git-Tag: 3.10.0~192 X-Git-Url: http://git.claws-mail.org/?p=claws.git;a=commitdiff_plain;h=7593136257a76ae5618fdcaeacd435475deee5e8 New libravatar plugin --- diff --git a/configure.ac b/configure.ac index 9bbd9aec7..474971f93 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 d440d96bc..2914aa51a 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 000000000..e49d96503 --- /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 000000000..fdd9e715e --- /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 + diff --git a/src/plugins/libravatar/TODO b/src/plugins/libravatar/TODO new file mode 100644 index 000000000..c21ff67d5 --- /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 000000000..e7533f665 --- /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 000000000..9f40a95c9 --- /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 . + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#include "claws-features.h" +#endif + +#include +#include + +#include + +#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 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 000000000..9bb3c2e61 --- /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 . + */ + +#ifndef __LIBRAVATAR_H +#define __LIBRAVATAR_H + +#include + +#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 000000000..6288dc7cd --- /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 . + */ + +#include + +#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 000000000..191159be8 --- /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 . + */ + +#ifndef __LIBRAVATAR_MISSING_H +#define __LIBRAVATAR_MISSING_H + +#include + +#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 000000000..c14948d2c --- /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 . + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#include "claws-features.h" +#endif + +#include +#include + +#include "libravatar.h" + +#include +#include + +#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 000000000..fe8ed6a7f --- /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 . + */ + +#ifndef __LIBRAVATAR_PREFS_H +#define __LIBRAVATAR_PREFS_H + +#include + +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 000000000..8471df184 --- /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 000000000..5d93a3d53 --- /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