Add geolocation plugin
authorHolger Berndt <hb@claws-mail.org>
Fri, 5 Apr 2013 23:11:06 +0000 (01:11 +0200)
committerHolger Berndt <hb@claws-mail.org>
Sat, 6 Apr 2013 18:04:38 +0000 (20:04 +0200)
.gitignore
configure.ac
src/plugins/Makefile.am
src/plugins/geolocation/Makefile.am [new file with mode: 0644]
src/plugins/geolocation/geolocation_plugin.c [new file with mode: 0644]
src/plugins/geolocation/pixmap_earth.h [new file with mode: 0644]

index dc7e934..228c585 100644 (file)
 /src/plugins/gdata/Makefile
 /src/plugins/gdata/Makefile.in
 /src/plugins/gdata/*.o
+/src/plugins/geolocation/.deps
+/src/plugins/geolocation/*.la
+/src/plugins/geolocation/.libs
+/src/plugins/geolocation/*.lo
+/src/plugins/geolocation/Makefile
+/src/plugins/geolocation/Makefile.in
+/src/plugins/geolocation/*.o
 /src/plugins/mailmbox/.deps
 /src/plugins/mailmbox/*.la
 /src/plugins/mailmbox/.libs
index 51d0a86..d489268 100644 (file)
@@ -993,6 +993,10 @@ AC_ARG_ENABLE(gdata-plugin,
                [  --disable-gdata-plugin          Do not build gdata plugin],
                [enable_gdata_plugin=$enableval], [enable_gdata_plugin=auto])
 
+AC_ARG_ENABLE(geolocation-plugin,
+               [  --disable-geolocation-plugin    Do not build geolocation plugin],
+               [enable_geolocation_plugin=$enableval], [enable_geolocation_plugin=auto])
+
 AC_ARG_ENABLE(mailmbox-plugin,
                [  --disable-mailmbox-plugin       Do not build mailmbox plugin],
                [enable_mailmbox_plugin=$enableval], [enable_mailmbox_plugin=auto])
@@ -1069,6 +1073,7 @@ dnl either yes or no, and do the AC_SUBST calls.
 dnl Archive:           libarchive
 dnl Fancy:             Webkit, curl, optionally libsoup-gnome
 dnl Gdata:             libgdata
+dnl Geolocation                libchamplain, libsoup
 dnl Notification:      optionally libnotify libindicate libcanberra_gtk hotkey
 dnl Pdf-Viewer:                libpoppler
 dnl Perl:              sed perl
@@ -1096,6 +1101,11 @@ PKG_CHECK_MODULES(WEBKIT, webkit-1.0 >= 1.1.14, HAVE_WEBKIT=yes, HAVE_WEBKIT=no)
 AC_SUBST(WEBKIT_LIBS)
 AC_SUBST(WEBKIT_CFLAGS)
 
+dnl libsoup ********************************************************************
+PKG_CHECK_MODULES(LIBSOUP, libsoup-2.4)
+AC_SUBST(LIBSOUP_CFLAGS)
+AC_SUBST(LIBSOUP_LIBS)
+
 dnl libsoup-gnome **************************************************************
 PKG_CHECK_MODULES(LIBSOUP_GNOME, libsoup-gnome-2.4 >= 2.26, HAVE_LIBSOUP_GNOME=yes, HAVE_LIBSOUP_GNOME=no)
 if test x"$HAVE_LIBSOUP_GNOME" = xyes; then
@@ -1272,6 +1282,16 @@ fi
 AC_SUBST(CM_NP_HOTKEY_CFLAGS)
 AC_SUBST(CM_NP_HOTKEY_LIBS)
 
+dnl libchamplain ***************************************************************
+CHAMPLAIN_MODULE=champlain-gtk-0.4
+CHAMPLAIN_VERSION=0.4.0
+PKG_CHECK_EXISTS(champlain-gtk-0.6 > 0.6.0,[CHAMPLAIN_MODULE=champlain-gtk-0.6
+       CHAMPLAIN_VERSION=0.6.0], [])
+PKG_CHECK_EXISTS(champlain-gtk-0.8 > 0.8.0,[CHAMPLAIN_MODULE=champlain-gtk-0.8
+       CHAMPLAIN_VERSION=0.8.0], [])
+PKG_CHECK_MODULES(CHAMPLAIN, [$CHAMPLAIN_MODULE >= $CHAMPLAIN_VERSION clutter-gtk-0.10], HAVE_CHAMPLAIN=yes, HAVE_CHAMPLAIN=no)
+AC_SUBST(CHAMPLAIN_CFLAGS)
+AC_SUBST(CHAMPLAIN_LIBS)
 
 dnl Third, we now cross the requested plugins and the available dependencies
 dnl If some dependencies are missing and the plugin was explicitely enabled,
@@ -1436,6 +1456,30 @@ else
        AC_MSG_RESULT(no)
 fi
 
+if test x"$enable_geolocation_plugin" != xno; then
+       dependencies_missing=""
+
+       if test x"$HAVE_CHAMPLAIN" = xno; then
+               dependencies_missing="libchamplain $dependencies_missing"
+       fi
+
+       if test x"$dependencies_missing" = x; then
+               PLUGINS="$PLUGINS geolocation"
+               AC_MSG_RESULT(yes)
+       elif test x"$enable_geolocation_plugin" = xauto; then
+               AC_MSG_RESULT(no)
+               AC_MSG_WARN("Plugin geolocation will not be built; missing $dependencies_missing")
+               enable_geolocation_plugin=no
+               MISSING_DEPS_PLUGINS="$MISSING_DEPS_PLUGINS geolocation"
+       else
+               AC_MSG_RESULT(no)
+               AC_MSG_ERROR("Plugin geolocation cannot be built; missing $dependencies_missing")
+       fi
+else
+       DISABLED_PLUGINS="$DISABLED_PLUGINS geolocation"
+       AC_MSG_RESULT(no)
+fi
+
 AC_MSG_CHECKING([whether to build mailmbox plugin])
 if test x"$enable_mailmbox_plugin" != xno; then
        PLUGINS="$PLUGINS mailmbox"
@@ -1795,6 +1839,7 @@ AM_CONDITIONAL(BUILD_DEMO_PLUGIN,         test x"$enable_demo_plugin" != xno)
 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_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)
@@ -1841,6 +1886,7 @@ src/plugins/demo/Makefile
 src/plugins/fancy/Makefile
 src/plugins/fetchinfo/Makefile
 src/plugins/gdata/Makefile
+src/plugins/geolocation/Makefile
 src/plugins/mailmbox/Makefile
 src/plugins/newmail/Makefile
 src/plugins/notification/Makefile
index 17dce52..d440d96 100644 (file)
@@ -11,6 +11,7 @@ SUBDIRS = \
        fancy \
        fetchinfo \
        gdata \
+       geolocation \
        mailmbox \
        newmail \
        notification \
diff --git a/src/plugins/geolocation/Makefile.am b/src/plugins/geolocation/Makefile.am
new file mode 100644 (file)
index 0000000..1c1ae46
--- /dev/null
@@ -0,0 +1,31 @@
+plugindir = $(pkglibdir)/plugins
+
+if BUILD_GEOLOCATION_PLUGIN
+plugin_LTLIBRARIES = geolocation.la
+endif
+
+geolocation_la_SOURCES = \
+       geolocation_plugin.c \
+       pixmap_earth.h
+
+geolocation_la_LDFLAGS = \
+       -avoid-version -module \
+       $(GTK_LIBS) \
+       $(LIBSOUP_LIBS) \
+       $(CHAMPLAIN_LIBS)
+
+
+INCLUDES = \
+       -I$(top_srcdir)/src \
+       -I$(top_srcdir)/src/common \
+       -I$(top_builddir)/src/common \
+       -I$(top_srcdir)/src/gtk
+
+AM_CPPFLAGS = \
+       -Wall \
+       $(CLAWS_MAIL_CFLAGS) \
+       $(GLIB_CFLAGS) \
+       $(GTK_CFLAGS) \
+       $(LIBSOUP_CFLAGS) \
+       $(CHAMPLAIN_CFLAGS) \
+       -DLOCALEDIR=\""$(localedir)"\"
diff --git a/src/plugins/geolocation/geolocation_plugin.c b/src/plugins/geolocation/geolocation_plugin.c
new file mode 100644 (file)
index 0000000..0860fc3
--- /dev/null
@@ -0,0 +1,903 @@
+/* GeoLocation plugin for Claws-Mail
+ * Copyright (C) 2009 Holger Berndt
+ *
+ * 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 <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "pixmap_earth.h"
+
+#include "common/plugin.h"
+#include "common/version.h"
+#include "common/utils.h"
+#include "common/hooks.h"
+#include "prefs_common.h"
+#include "mimeview.h"
+#include "procheader.h"
+#include "main.h"
+
+#include <libsoup/soup.h>
+
+#include <champlain/champlain.h>
+#include <champlain-gtk/champlain-gtk.h>
+#include <clutter-gtk/clutter-gtk.h>
+
+#include <string.h>
+
+
+/* For now, make extracting the ip address string from the Received header
+ * as simple as possible. This should be made more accurate, some day.
+ * Logic:
+ * Find four 1-3 digit numbers, separated by dots, encosed in word boundaries,
+ * where the word "from" must be ahead, and the word "by" must follow, but must
+ * not be between the "from" and the digits.
+ * The result will be in backreference 2, the individual numbers in 3 to 6 */
+#define EXTRACT_IP_FROM_RECEIVED_HEADER_REGEX "\\bfrom\\b((?!\\bby\\b).)*(\\b(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\b).*\\bby\\b"
+
+#define EXTRACT_LAT_LON_FROM_HOSTIP_RESPONSE_REGEX "<gml:coordinates>(-?[\\d.]+),(-?[\\d.]+)</gml:coordinates>"
+
+#define GEOLOCATION_CONTAINER_NAME "cm_geolocation_container"
+#define GEOLOCATION_CONTAINER_DATA_TOGGLEBUTTON "togglebutton"
+#define GEOLOCATION_CONTAINER_DATA_LABEL "label"
+
+#define GEOLOCATION_TOGGLE_BUTTON_DATA_CB_ID "toggled_cb_id"
+#define GEOLOCATION_TOGGLE_BUTTON_DATA_IP "ip"
+
+#define GEOLOCATION_NOTEBOOK_DATA_MAPWIDGET "mapwidget"
+
+#define GEOLOCATION_MAP_DATA_VIEW "view"
+#define GEOLOCATION_MAP_DATA_ORIGIN_MARKER "originmarker"
+
+#define BUFFSIZE 8192
+
+#define HOSTIP_URL "http://api.hostip.info/"
+
+#define COUNTRY_RESOLVER_SUFFIX "zz.countries.nerd.dk"
+
+
+static gboolean my_messageview_show_hook(gpointer, gpointer);
+static guint hook_messageview_show;
+
+static GRegex *ip_from_received_header_regex = NULL;
+static GRegex *lat_lon_from_hostip_response_regex = NULL;
+
+static GSList *container_list = NULL;
+
+static GHashTable *iso_country_hash = NULL;
+
+static gchar* get_ip_from_received_header(gchar *received)
+{
+  GMatchInfo *match_info;
+  gchar *ip;
+  gchar *n1;
+
+  g_regex_match(ip_from_received_header_regex, received, 0, &match_info);
+  ip = g_match_info_fetch(match_info, 2);
+
+  if (ip) {
+    /* check for loopback and local subnetworks */
+    if(!strcmp(ip, "127.0.0.1")) {
+      g_free(ip);
+      ip = NULL;
+    }
+    else {
+      n1 = g_match_info_fetch(match_info, 3);
+      if(n1) {
+        /* 10.x.x.x */
+        if(!strcmp(n1, "10")) {
+          g_free(ip);
+          ip = NULL;
+        }
+        /* 192.168.x.x */
+        else if(!strcmp(n1, "192")) {
+          gchar *n2;
+          n2 = g_match_info_fetch(match_info, 4);
+          if(n2 && !strcmp(n2, "168")) {
+            g_free(ip);
+            ip = NULL;
+          }
+        }
+        /* 172.16.xx to 172.31.x.x */
+        else if(!strcmp(n1, "172")) {
+          gchar *n2;
+          int i2;
+          n2 = g_match_info_fetch(match_info, 4);
+          if(n2) {
+            i2 = atoi(n2);
+            if((i2 >= 16) && (i2 <= 31)) {
+              g_free(ip);
+              ip = NULL;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  g_match_info_free(match_info);
+  return ip;
+}
+
+enum {
+  H_RECEIVED = 0
+};
+
+static HeaderEntry hentry[] = {
+    {"Received:", NULL, TRUE},
+    {NULL, NULL, FALSE}};
+
+static gchar* get_ip_from_msginfo(MsgInfo *msginfo)
+{
+  gchar *file;
+  struct stat ss;
+  FILE *fp;
+  gchar buf[BUFFSIZE];
+  gint hnum;
+  gchar *hp;
+  gchar *ip;
+  gchar *new_ip;
+
+  file = procmsg_get_message_file_path(msginfo);
+  g_return_val_if_fail(file, NULL);
+
+  if(g_stat(file, &ss) < 0) {
+    FILE_OP_ERROR(file, "stat");
+    return NULL;
+  }
+  if(!S_ISREG(ss.st_mode)) {
+    g_warning("mail file is not a regular file\n");
+    return NULL;
+  }
+
+  if((fp = g_fopen(file, "rb")) == NULL) {
+    FILE_OP_ERROR(file, "fopen");
+    return NULL;
+  }
+
+  ip = NULL;
+  new_ip = NULL;
+  while((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry)) != -1) {
+    switch(hnum) {
+    case H_RECEIVED:
+      hp = buf + strlen(hentry[hnum].name);
+      while(*hp == ' ' || *hp == '\t') hp++;
+      new_ip = get_ip_from_received_header(hp);
+      if(new_ip) {
+        g_free(ip);
+        ip = new_ip;
+      }
+      break;
+    default:
+      break;
+    }
+  }
+
+  fclose(fp);
+
+  g_free(file);
+  return ip;
+}
+
+/* inspired by GeoClue */
+static gboolean get_lat_lon_from_ip(const gchar *ip, double *lat, double *lon)
+{
+  gchar *url;
+  GMatchInfo *match_info;
+  gchar *slat;
+  gchar *slon;
+  SoupSession *session;
+  SoupMessage *msg;
+
+  /* fetch data from hostip.info */
+  url = g_strdup_printf("%s?ip=%s", HOSTIP_URL, ip);
+  session = soup_session_async_new();
+  msg = soup_message_new(SOUP_METHOD_GET, url);
+  g_free(url);
+
+  soup_session_send_message(session, msg);
+  g_object_unref(session);
+  if(!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code) || !msg->response_body->data)
+    return FALSE;
+
+  /* extract longitude and latitude from */
+  g_regex_match(lat_lon_from_hostip_response_regex, msg->response_body->data, 0, &match_info);
+  g_object_unref(msg);
+  slon = g_match_info_fetch(match_info, 1);
+  slat = g_match_info_fetch(match_info, 2);
+  g_match_info_free(match_info);
+
+  if(!slat || !slon)
+    return FALSE;
+
+  *lat = g_ascii_strtod(slat, NULL);
+  *lon = g_ascii_strtod(slon, NULL);
+
+  return TRUE;
+}
+
+/* try to look up country */
+static const gchar* get_country_from_ip(const gchar *ip)
+{
+  gchar **tok;
+  gchar *uri;
+  SoupAddress *addr;
+  const gchar *val = NULL;
+
+  tok = g_strsplit_set(ip, ".", 4);
+  if(!(tok[0] && tok[1] && tok[2] && tok[3] && !tok[4])) {
+    g_strfreev(tok);
+    debug_print("GeoLocation plugin: Error parsing ip address '%s'\n", ip);
+    return NULL;
+  }
+  uri = g_strjoin(".", tok[3], tok[2], tok[1], tok[0], COUNTRY_RESOLVER_SUFFIX, NULL);
+  g_strfreev(tok);
+
+  addr = soup_address_new(uri, SOUP_ADDRESS_ANY_PORT);
+  g_free(uri);
+
+  if(soup_address_resolve_sync(addr, NULL) == SOUP_STATUS_OK) {
+    gchar *physical = soup_address_get_physical(addr);
+    val = g_hash_table_lookup(iso_country_hash, physical);
+  }
+
+  g_object_unref(addr);
+  return val;
+}
+
+static GtkWidget* create_map_widget()
+{
+  GtkWidget *map;
+  ChamplainLayer *layer;
+  ChamplainView *view;
+  ClutterActor *origin_marker;
+  ClutterColor orange = { 0xf3, 0x94, 0x07, 0xbb };
+
+  /* create map widget */
+  map = gtk_champlain_embed_new();
+  view = gtk_champlain_embed_get_view(GTK_CHAMPLAIN_EMBED(map));
+  g_object_set(G_OBJECT(view), "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC, "zoom-level", 8, NULL);
+
+  /* mail origin marker */
+  layer = champlain_layer_new();
+  origin_marker = champlain_marker_new_with_text("mail origin", "Serif 14", NULL, NULL);
+  champlain_marker_set_color(CHAMPLAIN_MARKER(origin_marker), &orange);
+  clutter_container_add(CLUTTER_CONTAINER(layer), origin_marker, NULL);
+  champlain_view_add_layer(view, layer);
+
+  clutter_actor_show(CLUTTER_ACTOR(view));
+  clutter_actor_show(CLUTTER_ACTOR(layer));
+  clutter_actor_show(origin_marker);
+
+  g_object_set_data(G_OBJECT(map), GEOLOCATION_MAP_DATA_VIEW, view);
+  g_object_set_data(G_OBJECT(map), GEOLOCATION_MAP_DATA_ORIGIN_MARKER, origin_marker);
+
+  return map;
+}
+
+static int get_notebook_map_page_num(GtkWidget *notebook)
+{
+  GtkWidget *wid;
+  int page_num = 0;
+
+  wid = g_object_get_data(G_OBJECT(notebook), GEOLOCATION_NOTEBOOK_DATA_MAPWIDGET);
+
+  if(!wid) {
+    /* create page */
+    GtkWidget *map;
+    map = create_map_widget();
+    page_num = gtk_notebook_append_page(GTK_NOTEBOOK(notebook), map, NULL);
+    gtk_widget_show_all(map);
+    if(page_num == -1)
+      page_num = 0;
+    else
+      g_object_set_data(G_OBJECT(notebook), GEOLOCATION_NOTEBOOK_DATA_MAPWIDGET, map);
+  }
+  else {
+    /* find page */
+    page_num = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), wid);
+    if(page_num == -1)
+      page_num = 0;
+  }
+
+  return page_num;
+}
+
+static void geolocation_button_toggled_cb(GtkToggleButton *button, gpointer data)
+{
+  if(gtk_toggle_button_get_active(button)) {
+    gchar *ip;
+    double lat, lon;
+    gchar *text = NULL;
+    GtkWidget *container, *label;
+
+    ip = g_object_get_data(G_OBJECT(button), GEOLOCATION_TOGGLE_BUTTON_DATA_IP);
+    container = gtk_widget_get_parent(GTK_WIDGET(button));
+    g_return_if_fail(container);
+    label = g_object_get_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_LABEL);
+    g_return_if_fail(label);
+
+    if(ip && get_lat_lon_from_ip(ip, &lat, &lon)) {
+      GtkWidget *notebook;
+      GtkWidget *map;
+      MessageView *messageview;
+      ClutterActor *view;
+      ClutterActor *origin_marker;
+      gint page_num;
+
+      /* TRANSLATORS: The two numbers are latitude and longitude coordinates */
+      text = g_strdup_printf(_("Found location: (%.2f,%.2f)"), lat, lon);
+      gtk_label_set_text(GTK_LABEL(label), text);
+      debug_print("%s\n", text);
+
+      /* switch to map widget, and center on found location */
+      messageview = data;
+      notebook = messageview->mimeview->mime_notebook;
+      page_num = get_notebook_map_page_num(notebook);
+      map = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num);
+      view = g_object_get_data(G_OBJECT(map), GEOLOCATION_MAP_DATA_VIEW);
+      origin_marker = g_object_get_data(G_OBJECT(map), GEOLOCATION_MAP_DATA_ORIGIN_MARKER);
+      champlain_view_center_on(CHAMPLAIN_VIEW(view), lat, lon);
+      champlain_base_marker_set_position(CHAMPLAIN_BASE_MARKER(origin_marker), lat, lon);
+      gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), page_num);
+    }
+    else {
+      const char *country;
+
+      if(ip && ((country = get_country_from_ip(ip)) != NULL)) {
+        /* TRANSLATORS: The country name is appended to the string */
+        text = g_strconcat(_("Alleged country of origin: "), country, NULL);
+      }
+      else {
+        /* TRANSLATORS: The IP address is appended to the string */
+        text = g_strconcat(_("Could not resolve location of IP address "), ip, NULL);
+      }
+      gtk_label_set_text(GTK_LABEL(label), text);
+      debug_print("%s\n", text);
+      gtk_toggle_button_set_active(button, FALSE);
+    }
+    g_free(text);
+  }
+  else {
+    /* swich to first notebook page */
+    MessageView *messageview;
+    messageview = data;
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(messageview->mimeview->mime_notebook), 0);
+  }
+}
+
+static void container_finalize_cb(gpointer data, GObject *where_the_object_was)
+{
+  container_list = g_slist_remove(container_list, where_the_object_was);
+}
+
+static GtkWidget* get_geolocation_container_from_messageview(MessageView *messageview)
+{
+  GtkWidget *container = NULL;
+  GtkWidget *vbox;
+  GList *children, *walk;
+  GtkWidget *button;
+  gulong *cb_id;
+
+  g_return_val_if_fail(messageview && messageview->mimeview && messageview->mimeview->paned, NULL);
+
+  vbox = gtk_paned_get_child2(GTK_PANED(messageview->mimeview->paned));
+
+  /* check if there is already a geolocation container */
+  children = gtk_container_get_children(GTK_CONTAINER(vbox));
+  for(walk = children; walk; walk = walk->next) {
+    GtkWidget *child = walk->data;
+    if(!strcmp(gtk_widget_get_name(child), GEOLOCATION_CONTAINER_NAME)) {
+      container = child;
+      break;
+    }
+  }
+  g_list_free(children);
+
+  /* if not, create one */
+  if(!container) {
+    GtkWidget *label;
+    GtkWidget *image;
+    GdkPixbuf *pixbuf, *scaled_pixbuf;
+
+    container = gtk_hbox_new(FALSE, 0);
+    gtk_widget_set_name(container, GEOLOCATION_CONTAINER_NAME);
+
+    /* button */
+    pixbuf = gdk_pixbuf_new_from_inline(-1, pixmap_earth, FALSE, NULL);
+    scaled_pixbuf = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR);
+    g_object_unref(pixbuf);
+    image = gtk_image_new_from_pixbuf(scaled_pixbuf);
+    gtk_widget_show(image);
+    g_object_unref(scaled_pixbuf);
+    button = gtk_toggle_button_new();
+    gtk_widget_show(button);
+    gtk_container_add(GTK_CONTAINER(button), image);
+    gtk_box_pack_start(GTK_BOX(container), button, FALSE, FALSE, 0);
+    g_object_set_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_TOGGLEBUTTON, button);
+
+    /* label */
+    label = gtk_label_new("");
+    gtk_widget_show(label);
+    gtk_box_pack_start(GTK_BOX(container), label, FALSE, FALSE, 2);
+    g_object_set_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_LABEL, label);
+
+    gtk_box_pack_start(GTK_BOX(vbox), container, FALSE, FALSE, 0);
+
+    /* add new container to list */
+    container_list = g_slist_prepend(container_list, container);
+    g_object_weak_ref(G_OBJECT(container), container_finalize_cb, NULL);
+  }
+  /* if it existed, changed the button callback data */
+  else {
+    /* disconnect old signal handler from button */
+    gulong *id;
+    button = g_object_get_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_TOGGLEBUTTON);
+    g_return_val_if_fail(button, NULL);
+    id = g_object_get_data(G_OBJECT(button), GEOLOCATION_TOGGLE_BUTTON_DATA_CB_ID);
+    g_signal_handler_disconnect(button, *id);
+  }
+
+  /* connect new signal handler to button, and store its id */
+  cb_id = g_new(gulong, 1);
+  *cb_id = g_signal_connect(button, "toggled", G_CALLBACK(geolocation_button_toggled_cb), messageview);
+  g_object_set_data_full(G_OBJECT(button), GEOLOCATION_TOGGLE_BUTTON_DATA_CB_ID, cb_id, g_free);
+
+  return container;
+}
+
+static gboolean my_messageview_show_hook(gpointer source, gpointer data)
+{
+  MessageView *messageview;
+  GtkWidget *container;
+  GtkWidget *button;
+  gchar *ip;
+
+  messageview = source;
+
+  g_return_val_if_fail(messageview, FALSE);
+
+  container = get_geolocation_container_from_messageview(messageview);
+  g_return_val_if_fail(container, FALSE);
+
+  /* make sure toggle button is turned off */
+  button = g_object_get_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_TOGGLEBUTTON);
+  g_return_val_if_fail(button, FALSE);
+  if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) {
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+  }
+
+  /* do nothing except hiding the button if claws mail is in offline mode */
+  if(prefs_common.work_offline) {
+    gtk_widget_hide(container);
+    return FALSE;
+  }
+
+  /* try to get ip address from message */
+  if(!messageview->msginfo) {
+    debug_print("Warning: Messageview does not have a MsgInfo\n");
+    return FALSE;
+  }
+  ip = get_ip_from_msginfo(messageview->msginfo);
+  if(!ip) {
+    debug_print("Could not get IP address from message\n");
+    gtk_widget_hide(container);
+  }
+  else {
+    GtkWidget *label;
+    debug_print("Found sender ip address: %s\n", ip);
+    label = g_object_get_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_LABEL);
+    if(label) {
+      gchar *text;
+      text = g_strconcat(_("Try to locate sender"), NULL);
+      gtk_label_set_text(GTK_LABEL(label), text);
+      g_free(text);
+    }
+    /* button takes ownership of ip string */
+    g_object_set_data_full(G_OBJECT(button), GEOLOCATION_TOGGLE_BUTTON_DATA_IP, ip, g_free);
+    gtk_widget_show(container);
+  }
+
+  return FALSE;
+}
+
+#define ISO_IN(k,v) g_hash_table_insert(iso_country_hash, (k), (v))
+static void fill_iso_country_hash(void)
+{
+  iso_country_hash = g_hash_table_new(g_str_hash, g_str_equal);
+  ISO_IN("127.0.0.20", _("Andorra"));
+  ISO_IN("127.0.3.16", _("United Arab Emirates"));
+  ISO_IN("127.0.0.4", _("Afghanistan"));
+  ISO_IN("127.0.0.28", _("Antigua And Barbuda"));
+  ISO_IN("127.0.2.148", _("Anguilla"));
+  ISO_IN("127.0.0.8", _("Albania"));
+  ISO_IN("127.0.0.51", _("Armenia"));
+  ISO_IN("127.0.2.18", _("Netherlands Antilles"));
+  ISO_IN("127.0.0.24", _("Angola"));
+  ISO_IN("127.0.0.10", _("Antarctica"));
+  ISO_IN("127.0.0.32", _("Argentina"));
+  ISO_IN("127.0.0.16", _("American Samoa"));
+  ISO_IN("127.0.0.40", _("Austria"));
+  ISO_IN("127.0.0.36", _("Australia"));
+  ISO_IN("127.0.2.21", _("Aruba"));
+  ISO_IN("127.0.0.31", _("Azerbaijan"));
+  ISO_IN("127.0.0.70", _("Bosnia And Herzegovina"));
+  ISO_IN("127.0.0.52", _("Barbados"));
+  ISO_IN("127.0.0.50", _("Bangladesh"));
+  ISO_IN("127.0.0.56", _("Belgium"));
+  ISO_IN("127.0.3.86", _("Burkina Faso"));
+  ISO_IN("127.0.0.100", _("Bulgaria"));
+  ISO_IN("127.0.0.48", _("Bahrain"));
+  ISO_IN("127.0.0.108", _("Burundi"));
+  ISO_IN("127.0.0.204", _("Benin"));
+  ISO_IN("127.0.0.60", _("Bermuda"));
+  ISO_IN("127.0.0.96", _("Brunei Darussalam"));
+  ISO_IN("127.0.0.68", _("Bolivia"));
+  ISO_IN("127.0.0.76", _("Brazil"));
+  ISO_IN("127.0.0.44", _("Bahamas"));
+  ISO_IN("127.0.0.64", _("Bhutan"));
+  ISO_IN("127.0.0.74", _("Bouvet Island"));
+  ISO_IN("127.0.0.72", _("Botswana"));
+  ISO_IN("127.0.0.112", _("Belarus"));
+  ISO_IN("127.0.0.84", _("Belize"));
+  ISO_IN("127.0.0.124", _("Canada"));
+  ISO_IN("127.0.0.166", _("Cocos (Keeling) Islands"));
+  ISO_IN("127.0.0.140", _("Central African Republic"));
+  ISO_IN("127.0.0.178", _("Congo"));
+  ISO_IN("127.0.2.244", _("Switzerland"));
+  ISO_IN("127.0.1.128", _("Cote D'Ivoire"));
+  ISO_IN("127.0.0.184", _("Cook Islands"));
+  ISO_IN("127.0.0.152", _("Chile"));
+  ISO_IN("127.0.0.120", _("Cameroon"));
+  ISO_IN("127.0.0.156", _("China"));
+  ISO_IN("127.0.0.170", _("Colombia"));
+  ISO_IN("127.0.0.188", _("Costa Rica"));
+  ISO_IN("127.0.0.192", _("Cuba"));
+  ISO_IN("127.0.0.132", _("Cape Verde"));
+  ISO_IN("127.0.0.162", _("Christmas Island"));
+  ISO_IN("127.0.0.196", _("Cyprus"));
+  ISO_IN("127.0.0.203", _("Czech Republic"));
+  ISO_IN("127.0.1.20", _("Germany"));
+  ISO_IN("127.0.1.6", _("Djibouti"));
+  ISO_IN("127.0.0.208", _("Denmark"));
+  ISO_IN("127.0.0.212", _("Dominica"));
+  ISO_IN("127.0.0.214", _("Dominican Republic"));
+  ISO_IN("127.0.0.12", _("Algeria"));
+  ISO_IN("127.0.0.218", _("Ecuador"));
+  ISO_IN("127.0.0.233", _("Estonia"));
+  ISO_IN("127.0.3.50", _("Egypt"));
+  ISO_IN("127.0.2.220", _("Western Sahara"));
+  ISO_IN("127.0.0.232", _("Eritrea"));
+  ISO_IN("127.0.2.212", _("Spain"));
+  ISO_IN("127.0.0.231", _("Ethiopia"));
+  ISO_IN("127.0.0.246", _("Finland"));
+  ISO_IN("127.0.0.242", _("Fiji"));
+  ISO_IN("127.0.0.238", _("Falkland Islands (Malvinas)"));
+  ISO_IN("127.0.2.71", _("Micronesia, Federated States Of"));
+  ISO_IN("127.0.0.234", _("Faroe Islands"));
+  ISO_IN("127.0.0.250", _("France"));
+  ISO_IN("127.0.0.249", _("France, Metropolitan"));
+  ISO_IN("127.0.1.10", _("Gabon"));
+  ISO_IN("127.0.3.58", _("United Kingdom"));
+  ISO_IN("127.0.1.52", _("Grenada"));
+  ISO_IN("127.0.1.12", _("Georgia"));
+  ISO_IN("127.0.0.254", _("French Guiana"));
+  ISO_IN("127.0.1.32", _("Ghana"));
+  ISO_IN("127.0.1.36", _("Gibraltar"));
+  ISO_IN("127.0.1.48", _("Greenland"));
+  ISO_IN("127.0.1.14", _("Gambia"));
+  ISO_IN("127.0.1.68", _("Guinea"));
+  ISO_IN("127.0.1.56", _("Guadeloupe"));
+  ISO_IN("127.0.0.226", _("Equatorial Guinea"));
+  ISO_IN("127.0.1.44", _("Greece"));
+  ISO_IN("127.0.0.239", _("South Georgia And The South Sandwich Islands"));
+  ISO_IN("127.0.1.64", _("Guatemala"));
+  ISO_IN("127.0.1.60", _("Guam"));
+  ISO_IN("127.0.2.112", _("Guinea-Bissau"));
+  ISO_IN("127.0.1.72", _("Guyana"));
+  ISO_IN("127.0.1.88", _("Hong Kong"));
+  ISO_IN("127.0.1.78", _("Heard Island And Mcdonald Islands"));
+  ISO_IN("127.0.1.84", _("Honduras"));
+  ISO_IN("127.0.0.191", _("Croatia"));
+  ISO_IN("127.0.1.76", _("Haiti"));
+  ISO_IN("127.0.1.92", _("Hungary"));
+  ISO_IN("127.0.1.104", _("Indonesia"));
+  ISO_IN("127.0.1.116", _("Ireland"));
+  ISO_IN("127.0.1.120", _("Israel"));
+  ISO_IN("127.0.1.100", _("India"));
+  ISO_IN("127.0.0.86", _("British Indian Ocean Territory"));
+  ISO_IN("127.0.1.112", _("Iraq"));
+  ISO_IN("127.0.1.108", _("Iran, Islamic Republic Of"));
+  ISO_IN("127.0.1.96", _("Iceland"));
+  ISO_IN("127.0.1.124", _("Italy"));
+  ISO_IN("127.0.1.132", _("Jamaica"));
+  ISO_IN("127.0.1.144", _("Jordan"));
+  ISO_IN("127.0.1.136", _("Japan"));
+  ISO_IN("127.0.1.148", _("Kenya"));
+  ISO_IN("127.0.1.161", _("Kyrgyzstan"));
+  ISO_IN("127.0.0.116", _("Cambodia"));
+  ISO_IN("127.0.1.40", _("Kiribati"));
+  ISO_IN("127.0.0.174", _("Comoros"));
+  ISO_IN("127.0.2.147", _("Saint Kitts And Nevis"));
+  ISO_IN("127.0.1.152", _("Korea, Democratic People'S Republic Of"));
+  ISO_IN("127.0.1.154", _("Korea, Republic Of"));
+  ISO_IN("127.0.1.158", _("Kuwait"));
+  ISO_IN("127.0.0.136", _("Cayman Islands"));
+  ISO_IN("127.0.1.142", _("Kazakhstan"));
+  ISO_IN("127.0.1.162", _("Lao People'S Democratic Republic"));
+  ISO_IN("127.0.1.166", _("Lebanon"));
+  ISO_IN("127.0.2.150", _("Saint Lucia"));
+  ISO_IN("127.0.1.182", _("Liechtenstein"));
+  ISO_IN("127.0.0.144", _("Sri Lanka"));
+  ISO_IN("127.0.1.174", _("Liberia"));
+  ISO_IN("127.0.1.170", _("Lesotho"));
+  ISO_IN("127.0.1.184", _("Lithuania"));
+  ISO_IN("127.0.1.186", _("Luxembourg"));
+  ISO_IN("127.0.1.172", _("Latvia"));
+  ISO_IN("127.0.1.178", _("Libyan Arab Jamahiriya"));
+  ISO_IN("127.0.1.248", _("Morocco"));
+  ISO_IN("127.0.1.236", _("Monaco"));
+  ISO_IN("127.0.1.242", _("Moldova, Republic Of"));
+  ISO_IN("127.0.1.194", _("Madagascar"));
+  ISO_IN("127.0.2.72", _("Marshall Islands"));
+  ISO_IN("127.0.3.39", _("Macedonia, The Former Yugoslav Republic Of"));
+  ISO_IN("127.0.1.210", _("Mali"));
+  ISO_IN("127.0.0.104", _("Myanmar"));
+  ISO_IN("127.0.1.240", _("Mongolia"));
+  ISO_IN("127.0.1.190", _("Macao"));
+  ISO_IN("127.0.2.68", _("Northern Mariana Islands"));
+  ISO_IN("127.0.1.218", _("Martinique"));
+  ISO_IN("127.0.1.222", _("Mauritania"));
+  ISO_IN("127.0.1.244", _("Montserrat"));
+  ISO_IN("127.0.1.214", _("Malta"));
+  ISO_IN("127.0.1.224", _("Mauritius"));
+  ISO_IN("127.0.1.206", _("Maldives"));
+  ISO_IN("127.0.1.198", _("Malawi"));
+  ISO_IN("127.0.1.228", _("Mexico"));
+  ISO_IN("127.0.1.202", _("Malaysia"));
+  ISO_IN("127.0.1.252", _("Mozambique"));
+  ISO_IN("127.0.2.4", _("Namibia"));
+  ISO_IN("127.0.2.28", _("New Caledonia"));
+  ISO_IN("127.0.2.50", _("Niger"));
+  ISO_IN("127.0.2.62", _("Norfolk Island"));
+  ISO_IN("127.0.2.54", _("Nigeria"));
+  ISO_IN("127.0.2.46", _("Nicaragua"));
+  ISO_IN("127.0.2.16", _("Netherlands"));
+  ISO_IN("127.0.2.66", _("Norway"));
+  ISO_IN("127.0.2.12", _("Nepal"));
+  ISO_IN("127.0.2.8", _("Nauru"));
+  ISO_IN("127.0.2.58", _("Niue"));
+  ISO_IN("127.0.2.42", _("New Zealand"));
+  ISO_IN("127.0.2.0", _("Oman"));
+  ISO_IN("127.0.2.79", _("Panama"));
+  ISO_IN("127.0.2.92", _("Peru"));
+  ISO_IN("127.0.1.2", _("French Polynesia"));
+  ISO_IN("127.0.2.86", _("Papua New Guinea"));
+  ISO_IN("127.0.2.96", _("Philippines"));
+  ISO_IN("127.0.2.74", _("Pakistan"));
+  ISO_IN("127.0.2.104", _("Poland"));
+  ISO_IN("127.0.2.154", _("Saint Pierre And Miquelon"));
+  ISO_IN("127.0.2.100", _("Pitcairn"));
+  ISO_IN("127.0.2.118", _("Puerto Rico"));
+  ISO_IN("127.0.2.108", _("Portugal"));
+  ISO_IN("127.0.2.73", _("Palau"));
+  ISO_IN("127.0.2.88", _("Paraguay"));
+  ISO_IN("127.0.2.122", _("Qatar"));
+  ISO_IN("127.0.2.126", _("Reunion"));
+  ISO_IN("127.0.2.130", _("Romania"));
+  ISO_IN("127.0.2.131", _("Russian Federation"));
+  ISO_IN("127.0.2.134", _("Rwanda"));
+  ISO_IN("127.0.2.170", _("Saudi Arabia"));
+  ISO_IN("127.0.0.90", _("Solomon Islands"));
+  ISO_IN("127.0.2.178", _("Seychelles"));
+  ISO_IN("127.0.2.224", _("Sudan"));
+  ISO_IN("127.0.2.240", _("Sweden"));
+  ISO_IN("127.0.2.190", _("Singapore"));
+  ISO_IN("127.0.2.142", _("Saint Helena"));
+  ISO_IN("127.0.2.193", _("Slovenia"));
+  ISO_IN("127.0.2.232", _("Svalbard And Jan Mayen"));
+  ISO_IN("127.0.2.191", _("Slovakia"));
+  ISO_IN("127.0.2.182", _("Sierra Leone"));
+  ISO_IN("127.0.2.162", _("San Marino"));
+  ISO_IN("127.0.2.174", _("Senegal"));
+  ISO_IN("127.0.2.194", _("Somalia"));
+  ISO_IN("127.0.2.228", _("Suriname"));
+  ISO_IN("127.0.2.166", _("Sao Tome And Principe"));
+  ISO_IN("127.0.0.222", _("El Salvador"));
+  ISO_IN("127.0.2.248", _("Syrian Arab Republic"));
+  ISO_IN("127.0.2.236", _("Swaziland"));
+  ISO_IN("127.0.3.28", _("Turks And Caicos Islands"));
+  ISO_IN("127.0.0.148", _("Chad"));
+  ISO_IN("127.0.1.4", _("French Southern Territories"));
+  ISO_IN("127.0.3.0", _("Togo"));
+  ISO_IN("127.0.2.252", _("Thailand"));
+  ISO_IN("127.0.2.250", _("Tajikistan"));
+  ISO_IN("127.0.3.4", _("Tokelau"));
+  ISO_IN("127.0.3.27", _("Turkmenistan"));
+  ISO_IN("127.0.3.20", _("Tunisia"));
+  ISO_IN("127.0.3.8", _("Tonga"));
+  ISO_IN("127.0.2.114", _("East Timor"));
+  ISO_IN("127.0.3.24", _("Turkey"));
+  ISO_IN("127.0.3.12", _("Trinidad And Tobago"));
+  ISO_IN("127.0.3.30", _("Tuvalu"));
+  ISO_IN("127.0.0.158", _("Taiwan, Province Of China"));
+  ISO_IN("127.0.3.66", _("Tanzania, United Republic Of"));
+  ISO_IN("127.0.3.36", _("Ukraine"));
+  ISO_IN("127.0.3.32", _("Uganda"));
+  ISO_IN("127.0.3.58", _("United Kingdom"));
+  ISO_IN("127.0.2.69", _("United States Minor Outlying Islands"));
+  ISO_IN("127.0.3.72", _("United States"));
+  ISO_IN("127.0.3.90", _("Uruguay"));
+  ISO_IN("127.0.3.92", _("Uzbekistan"));
+  ISO_IN("127.0.1.80", _("Holy See (Vatican City State)"));
+  ISO_IN("127.0.2.158", _("Saint Vincent And The Grenadines"));
+  ISO_IN("127.0.3.94", _("Venezuela"));
+  ISO_IN("127.0.0.92", _("Virgin Islands, British"));
+  ISO_IN("127.0.3.82", _("Virgin Islands, U.S."));
+  ISO_IN("127.0.2.192", _("Viet Nam"));
+  ISO_IN("127.0.2.36", _("Vanuatu"));
+  ISO_IN("127.0.3.108", _("Wallis And Futuna"));
+  ISO_IN("127.0.3.114", _("Samoa"));
+  ISO_IN("127.0.3.119", _("Yemen"));
+  ISO_IN("127.0.0.175", _("Mayotte"));
+  ISO_IN("127.0.3.123", _("Serbia And Montenegro"));
+  ISO_IN("127.0.2.198", _("South Africa"));
+  ISO_IN("127.0.3.126", _("Zambia"));
+  ISO_IN("127.0.0.180", _("Democratic Republic Of The Congo"));
+  ISO_IN("127.0.2.204", _("Zimbabwe"));
+}
+#undef ISO_IN
+
+gint plugin_init(gchar **error)
+{
+  GError *err = NULL;
+
+  /* Version check */
+  if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,7,1,55),
+                          VERSION_NUMERIC, _("GeoLocation"), error))
+    return -1;
+
+  if(gtk_clutter_init(0, NULL) != CLUTTER_INIT_SUCCESS)
+  {
+    *error = g_strdup_printf(_("Could not initialize clutter"));
+    return -1;
+  }
+
+  ip_from_received_header_regex = g_regex_new(EXTRACT_IP_FROM_RECEIVED_HEADER_REGEX, 0, 0, &err);
+  if(err) {
+    *error = g_strdup_printf(_("Could not create regular expression: %s\n"), err->message);
+    g_error_free(err);
+    return -1;
+  }
+
+  lat_lon_from_hostip_response_regex = g_regex_new(EXTRACT_LAT_LON_FROM_HOSTIP_RESPONSE_REGEX, 0, 0, &err);
+  if(err) {
+    *error = g_strdup_printf(_("Could not create regular expression: %s\n"), err->message);
+    g_error_free(err);
+    g_regex_unref(ip_from_received_header_regex);
+    return -1;
+  }
+
+  hook_messageview_show = hooks_register_hook(MESSAGE_VIEW_SHOW_DONE_HOOKLIST, my_messageview_show_hook, NULL);
+  if(hook_messageview_show == (guint) -1) {
+    *error = g_strdup(_("Failed to register messageview_show hook in the GeoLocation plugin"));
+    g_regex_unref(ip_from_received_header_regex);
+    g_regex_unref(lat_lon_from_hostip_response_regex);
+    return -1;
+  }
+
+  fill_iso_country_hash();
+
+  debug_print("GeoLocation plugin loaded\n");
+
+  return 0;
+}
+
+gboolean plugin_done(void)
+{
+
+  hooks_unregister_hook(MESSAGE_VIEW_SHOW_DONE_HOOKLIST, hook_messageview_show);
+
+  if(!claws_is_exiting()) {
+    GSList *copy, *walk;
+
+    copy = g_slist_copy(container_list);
+    for(walk = copy; walk; walk = walk->next) {
+      GtkWidget *wid = walk->data;
+      GtkWidget *button = g_object_get_data(G_OBJECT(wid), GEOLOCATION_CONTAINER_DATA_TOGGLEBUTTON);
+      if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
+        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
+      gtk_widget_destroy(GTK_WIDGET(wid));
+    }
+    g_slist_free(copy);
+    if(container_list) {
+      g_slist_free(container_list);
+      container_list = NULL;
+    }
+
+    if(ip_from_received_header_regex) {
+      g_regex_unref(ip_from_received_header_regex);
+      ip_from_received_header_regex = NULL;
+    }
+
+    if(lat_lon_from_hostip_response_regex) {
+      g_regex_unref(lat_lon_from_hostip_response_regex);
+      lat_lon_from_hostip_response_regex = NULL;
+    }
+
+    if(iso_country_hash) {
+      g_hash_table_destroy(iso_country_hash);
+      iso_country_hash = NULL;
+    }
+  }
+
+  debug_print("GeoLocation plugin unloaded\n");
+
+  /* returning FALSE because dependant libraries may not be unload-safe. */
+  return FALSE;
+}
+
+const gchar *plugin_name(void)
+{
+  return _("GeoLocation");
+}
+
+const gchar *plugin_desc(void)
+{
+  return _("This plugin provides GeoLocation functionality "
+          "for Claws Mail.\n\n"
+          "Warning: It is technically impossible to derive the geographic "
+          "location of senders from their E-Mails with any amount of certainty. "
+          "The results presented by "
+          "this plugin are only rough estimates. In particular, mailing list managers "
+          "often strip sender information from the mails, so mails from mailing lists "
+          "may be assigned to the location of the mailing list server instead of the "
+          "mail sender.\n"
+          "When in doubt, don't trust the results of this plugin, and don't rely on this "
+          "information to divorce your spouse.\n"
+           "\nFeedback to <berndth@gmx.de> is welcome "
+          "(but only if it's not about marital quarrels).");
+}
+
+const gchar *plugin_type(void)
+{
+  return "GTK2";
+}
+
+const gchar *plugin_licence(void)
+{
+  return "GPL3+";
+}
+
+const gchar *plugin_version(void)
+{
+  return VERSION;
+}
+
+struct PluginFeature *plugin_provides(void)
+{
+  static struct PluginFeature features[] =
+    { {PLUGIN_UTILITY, N_("GeoLocation integration")},
+      {PLUGIN_NOTHING, NULL}};
+  return features;
+}
diff --git a/src/plugins/geolocation/pixmap_earth.h b/src/plugins/geolocation/pixmap_earth.h
new file mode 100644 (file)
index 0000000..625ed07
--- /dev/null
@@ -0,0 +1,123 @@
+/* GdkPixbuf RGBA C-Source image dump */
+
+#ifdef __SUNPRO_C
+#pragma align 4 (pixmap_earth)
+#endif
+#ifdef __GNUC__
+static const guint8 pixmap_earth[] __attribute__ ((__aligned__ (4))) = 
+#else
+static const guint8 pixmap_earth[] = 
+#endif
+{ ""
+  /* Pixbuf magic (0x47646b50) */
+  "GdkP"
+  /* length: header (24) + pixel_data (2304) */
+  "\0\0\11\30"
+  /* pixdata_type (0x1010002) */
+  "\1\1\0\2"
+  /* rowstride (96) */
+  "\0\0\0`"
+  /* width (24) */
+  "\0\0\0\30"
+  /* height (24) */
+  "\0\0\0\30"
+  /* pixel_data: */
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "99l\12""99m)99l077l\36++\200\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0""88k\15RR\200s\231\234\264\316\236\241\273\372"
+  "\271\275\317\372\271\276\320\372\234\246\302\371\206\216\257\361_d\217"
+  "\244:=oD\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0KM{W\227\233\266\351\331"
+  "\340\350\377\311\327\347\377\356\360\361\377\376\376\376\377\353\354"
+  "\355\377\253\302\332\377\245\301\337\377\244\277\335\377\227\250\306"
+  "\374[b\215\270;;n\32\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0jl\222\202\324\325\336\375\302\313\324"
+  "\377\305\314\324\377\307\322\335\377\350\354\357\377\313\322\330\377"
+  "\271\306\323\377\273\314\337\377\264\314\346\377\306\325\344\377\327"
+  "\332\335\377\315\322\331\377\207\213\244\344:=p.\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\\_\211l\343\344\352\375"
+  "\267\310\332\377\337\353\366\377\325\332\337\377\341\357\376\377\322"
+  "\335\351\377\331\350\370\377\320\342\364\377\305\331\356\377\273\316"
+  "\341\377\320\327\336\377\264\276\311\377\360\361\361\377\347\347\347"
+  "\377\210\214\243\343::m\30\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0@Bu)\306\310\326\371\376\376\376\377\360\362\363\377\364\364\364"
+  "\377\327\332\335\377\323\336\351\377\342\361\377\377\335\355\374\377"
+  "\323\345\367\377\310\331\354\377\307\316\326\377\355\357\360\377\371"
+  "\372\372\377\370\370\370\377\346\346\346\377\334\334\335\377fi\212\262"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\211\213\250\255\376"
+  "\376\376\377\376\376\376\377\376\376\376\377\340\343\346\377\323\337"
+  "\353\377\333\351\367\377\341\360\377\377\337\356\375\377\324\346\370"
+  "\377\315\335\356\377\345\347\352\377\321\325\333\377\355\356\360\377"
+  "\372\372\372\377\352\352\352\377\330\330\330\377\262\264\274\3759<oA"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0""88n\33\304\305\324\373\376\376\376"
+  "\377\376\376\376\377\320\325\333\377\325\345\365\377\337\357\377\377"
+  "\337\357\377\377\340\357\377\377\335\355\375\377\322\345\367\377\315"
+  "\334\353\377\303\313\323\377\266\310\334\377\241\265\313\377\242\265"
+  "\312\377\323\326\332\377\330\330\330\377\321\321\321\377`c\200\247\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0BEvi\371\371\372\377\330\333\336\377\314"
+  "\321\325\377\274\322\352\377\327\352\376\377\336\356\377\377\336\357"
+  "\377\377\336\356\376\377\327\351\373\377\315\340\365\377\334\345\357"
+  "\377\376\376\376\377\376\376\376\377\337\343\350\377\317\330\342\377"
+  "\330\332\335\377\327\327\327\377\274\275\277\377\206\207\235\363@@j\3"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\200\201\240\216\323\327\333\377\240\277\337"
+  "\377\254\310\346\377\274\326\362\377\306\336\367\377\325\350\373\377"
+  "\330\352\373\377\324\347\372\377\315\341\366\377\314\334\356\377\376"
+  "\376\376\377\376\376\376\377\376\376\376\377\376\376\376\377\366\366"
+  "\366\377\247\262\277\377\321\321\322\377\214\231\250\377t~\230\37199"
+  "k\"\0\0\0\0\0\0\0\0\0\0\0\0ns\230\237\334\340\345\377\321\327\335\377"
+  "\244\301\337\377\257\314\353\377\267\322\357\377\275\326\362\377\303"
+  "\333\363\377\306\334\363\377\301\330\360\377\320\335\353\377\376\376"
+  "\376\377\376\376\376\377\376\376\376\377\375\375\375\377\360\360\360"
+  "\377\324\325\327\377\232\247\265\377\204\217\234\377ex\236\37388k7\0"
+  "\0\0\0\0\0\0\0\0\0\0\0]c\215\227\241\275\334\377\232\262\314\377\254"
+  "\273\313\377\335\345\354\377\323\342\361\377\251\306\345\377\254\312"
+  "\351\377\251\307\347\377\246\304\345\377\247\300\333\377\367\367\370"
+  "\377\376\376\376\377\376\376\376\377\367\367\367\377\350\350\350\377"
+  "\330\330\330\377\262\265\271\377Pm\220\377dv\234\37288l/\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\77Ar}\242\275\333\377\201\247\321\377\241\273\327\377\376"
+  "\376\376\377\376\376\376\377\320\327\340\377\234\274\337\377\231\273"
+  "\337\377\224\267\334\377\214\261\330\377\230\262\317\377\206\241\275"
+  "\377\251\271\314\377\354\354\354\377\336\336\336\377\317\317\317\377"
+  "\251\254\257\377;^\213\377bn\224\37166l\17\0\0\0\0\0\0\0\0\0\0\0\0;="
+  "pC\221\246\307\374s\235\312\377\310\327\351\377\376\376\376\377\376\376"
+  "\376\377\376\376\376\377\345\351\355\377\245\275\327\377\204\253\323"
+  "\377~\245\320\377v\236\313\377l\227\306\377v\231\300\377\340\340\340"
+  "\377\322\322\322\377\304\304\304\377\\s\217\377Jj\225\377U]\206\324\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0..t\2r~\244\343t\234\310\377\232\262\315"
+  "\377\367\367\367\377\373\373\373\377\375\375\375\377\375\375\375\377"
+  "\256\274\313\377u\236\313\377o\231\310\377h\223\304\377_\214\277\377"
+  "X\206\272\377\310\312\314\377\305\305\305\377\270\270\270\377Kd\204\377"
+  "]t\230\377<>ov\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\77Cth\206\242"
+  "\307\375`\215\277\377\255\273\312\377\352\352\352\377\355\355\355\377"
+  "\357\357\357\377~\231\266\377e\221\302\377`\215\277\377Y\210\274\377"
+  "Q\201\267\377L}\263\377\303\303\303\377\270\270\270\377\227\232\235\377"
+  "Lc\203\377V`\207\351;;m\16\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\10"
+  "\10\20\17V_\211\315v\234\305\377d\214\270\377\330\330\330\377\333\333"
+  "\333\377\252\264\300\377W\201\256\377U\205\271\377Q\201\267\377J|\264"
+  "\377Cv\257\377\77p\250\377\240\244\251\377\252\252\252\377bp\203\377"
+  "`o\222\374/0Xm\0\0\0\12\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0"
+  "\32\24\25'I[f\220\346q\226\300\377\304\304\304\377\307\307\307\377Xz"
+  "\240\377Hz\262\377Ex\261\377Bu\255\377\77p\250\377=k\242\377<f\231\377"
+  "v\206\232\377\211\213\216\377eq\215\37546]\253\0\0\0""0\0\0\0\27\0\0"
+  "\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\4\0\0\0\33\0\0\0""4\21\21\40aSY\177"
+  "\336\265\267\273\376t\206\233\377=m\244\377>n\245\377=l\242\377=i\236"
+  "\377<e\230\377;a\222\377;]\212\377Vl\215\377]h\214\375,.P\253\0\0\0J"
+  "\0\0\0""1\0\0\0\30\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\17\0"
+  "\0\0'\0\0\0>\2\2\5X)+K\252Ze\213\363g~\245\376b\202\252\377Xx\241\377"
+  "Ts\234\377Yt\233\377_u\231\377^j\217\375@Dk\332\27\27,\205\0\0\0Q\0\0"
+  "\0:\0\0\0#\0\0\0\13\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\15\0\0\0!\0\0\0""5\0\0\0G\2\2\5Z\35\36""9\215,-Q\262<@c\306@Dh\314"
+  "68[\277%'H\250\20\20\40w\0\0\0U\0\0\0D\0\0\0""2\0\0\0\36\0\0\0\12\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\3"
+  "\0\0\0\15\0\0\0\34\0\0\0(\0\0\0""3\0\0\0;\0\0\0\77\0\0\0A\0\0\0\77\0"
+  "\0\0:\0\0\0""1\0\0\0'\0\0\0\31\0\0\0\13\0\0\0\2\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0\0\0\0\0\0\1\0\0\0\6\0\0\0\11\0\0\0\13\0\0\0\14\0\0\0\13\0\0\0\11\0"
+  "\0\0\5\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+  "\0"};
+
+