[claws-mail-cvs-commit] Makefile.am
Holger Berndt <[email protected]> Tue, 15 Sep 2009 20:39:46 +0000
| Newsgroups | gmane.mail.sylpheed.claws.cvs |
|---|---|
| Message-ID | <[email protected]> |
Update of /srv/cvs/claws-mail/plugins/geolocation/src
In directory ran:/tmp/cvs-serv20339/src
Added Files:
Tag: gtk2
Makefile.am geolocation_plugin.c gettext.h pixmap_earth.h
Log Message:
2009-09-15 [holger] 0.0.1cvs2
* AUTHORS
* COPYING
* ChangeLog
* INSTALL
* Makefile.am
* NEWS
* README
* autogen.sh
* commitHelper
* configure.ac
* earth.svg
* config/Makefile.am
* config/config.rpath
* po/Makefile.in.in
* po/Makevars
* po/POTFILES.in
* po/remove-potcdate.sin
* src/Makefile.am
* src/geolocation_plugin.c
* src/gettext.h
* src/pixmap_earth.h
Initial import, moving from github
--- NEW FILE: pixmap_earth.h ---
/* 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"};
--- NEW FILE: Makefile.am ---
plugindir = $(CLAWS_MAIL_PLUGINDIR)
plugin_LTLIBRARIES = geolocation_plugin.la
geolocation_plugin_la_SOURCES = \
geolocation_plugin.c \
gettext.h
geolocation_plugin_la_LDFLAGS = \
-avoid-version -module \
$(GTK_LIBS) \
$(LIBXML_LIBS) \
$(CHAMPLAIN_LIBS)
INCLUDES = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/common \
-I$(top_srcdir)/src/gtk
AM_CPPFLAGS = \
-Wall \
$(CLAWS_MAIL_CFLAGS) \
$(GLIB_CFLAGS) \
$(GTK_CFLAGS) \
$(LIBXML_CFLAGS) \
$(CHAMPLAIN_CFLAGS) \
-DLOCALEDIR=\""$(localedir)"\"
--- NEW FILE: geolocation_plugin.c ---
/* 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"
#endif
#include "pluginconfig.h"
#include <glib.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 "messageview.h"
#include "procheader.h"
#include "main.h"
#include "gettext.h"
#include <libxml/nanohttp.h>
#include <libxml/xpath.h>
#include <champlain/champlain.h>
#include <champlain-gtk/champlain-gtk.h>
#include <clutter-gtk/gtk-clutter-embed.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/"
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 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;
void* ctxt = NULL;
xmlBuffer *output;
gint len;
guchar *response;
gint response_length;
xmlChar buf[1024];
GMatchInfo *match_info;
gchar *slat;
gchar *slon;
/* fetch data from hostip.info */
url = g_strdup_printf("%s?ip=%s", HOSTIP_URL, ip);
xmlNanoHTTPInit();
ctxt = xmlNanoHTTPMethod(url, "GET", NULL, NULL, NULL, 0);
g_free(url);
if(!ctxt) {
g_print("xmlNanoHTTPMethod did not get a response from %s\n", url);
return FALSE;
}
output = xmlBufferCreate();
while((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
if(xmlBufferAdd(output, buf, len) != 0) {
xmlNanoHTTPClose(ctxt);
xmlBufferFree(output);
g_print("xmlBufferAdd failed\n");
return FALSE;
}
}
xmlNanoHTTPClose(ctxt);
response_length = xmlBufferLength(output);
response = g_malloc(response_length+1);
memcpy(response, xmlBufferContent(output), response_length);
response[response_length] = '\0';
xmlBufferFree(output);
/* extract longitude and latitude from */
g_regex_match(lat_lon_from_hostip_response_regex, response, 0, &match_info);
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;
}
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 {
/* 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, 0);
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;
}
gint plugin_init(gchar **error)
{
GError *err = NULL;
#ifdef G_OS_UNIX
bindtextdomain(TEXTDOMAIN, LOCALEDIR);
#else
bindtextdomain(TEXTDOMAIN, get_locale_dir());
#endif
bind_textdomain_codeset(TEXTDOMAIN, "UTF-8");
/* Version check */
if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,7,1,55),
VERSION_NUMERIC, _("GeoLocation"), error))
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"));
return -1;
}
gtk_clutter_init(0, NULL);
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;
}
}
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 <[email protected]> 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 PLUGINVERSION;
}
struct PluginFeature *plugin_provides(void)
{
static struct PluginFeature features[] =
{ {PLUGIN_UTILITY, N_("GeoLocation integration")},
{PLUGIN_NOTHING, NULL}};
return features;
}
--- NEW FILE: gettext.h ---
/* Convenience header for conditional use of GNU <libintl.h>.
Copyright (C) 1995-1998, 2000-2002, 2004 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2, 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
USA. */
#ifndef _LIBGETTEXT_H
#define _LIBGETTEXT_H 1
/* NLS can be disabled through the configure --disable-nls option. */
#if ENABLE_NLS
/* Get declarations of GNU message catalog functions. */
# include <libintl.h>
#define _(str) dgettext(TEXTDOMAIN, str)
#else
/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
chokes if dcgettext is defined as a macro. So include it now, to make
later inclusions of <locale.h> a NOP. We don't include <libintl.h>
as well because people using "gettext.h" will not include <libintl.h>,
and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
is OK. */
#if defined(__sun)
# include <locale.h>
#endif
/* Many header files from the libstdc++ coming with g++ 3.3 or newer include
<libintl.h>, which chokes if dcgettext is defined as a macro. So include
it now, to make later inclusions of <libintl.h> a NOP. */
#if defined(__cplusplus) && defined(__GNUG__) && (__GNUC__ >= 3)
# include <cstdlib>
# if (__GLIBC__ >= 2) || _GLIBCXX_HAVE_LIBINTL_H
# include <libintl.h>
# endif
#endif
/* Disabled NLS.
The casts to 'const char *' serve the purpose of producing warnings
for invalid uses of the value returned from these functions.
On pre-ANSI systems without 'const', the config.h file is supposed to
contain "#define const". */
# define gettext(Msgid) ((const char *) (Msgid))
# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
# define ngettext(Msgid1, Msgid2, N) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define dngettext(Domainname, Msgid1, Msgid2, N) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
# define textdomain(Domainname) ((const char *) (Domainname))
# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
#define _(str) str
#endif
/* A pseudo function call that serves as a marker for the automated
extraction of messages, but does not call gettext(). The run-time
translation is done at a different place in the code.
The argument, String, should be a literal string. Concatenated strings
and other string expressions won't work.
The macro's expansion is not parenthesized, so that it is suitable as
initializer for static 'char[]' or 'const char[]' variables. */
#define gettext_noop(String) String
#define N_(str) str
#endif /* _LIBGETTEXT_H */