1 /* GeoLocation plugin for Claws-Mail
2 * Copyright (C) 2009 Holger Berndt
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # include "claws-features.h"
24 #include <glib/gi18n.h>
27 #include <gdk-pixbuf/gdk-pixbuf.h>
29 #include "pixmap_earth.h"
31 #include "common/plugin.h"
32 #include "common/version.h"
33 #include "common/utils.h"
34 #include "common/hooks.h"
35 #include "prefs_common.h"
37 #include "procheader.h"
40 #include <libsoup/soup.h>
42 #include <champlain/champlain.h>
43 #include <champlain-gtk/champlain-gtk.h>
44 #include <clutter-gtk/clutter-gtk.h>
49 /* For now, make extracting the ip address string from the Received header
50 * as simple as possible. This should be made more accurate, some day.
52 * Find four 1-3 digit numbers, separated by dots, encosed in word boundaries,
53 * where the word "from" must be ahead, and the word "by" must follow, but must
54 * not be between the "from" and the digits.
55 * The result will be in backreference 2, the individual numbers in 3 to 6 */
56 #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"
58 #define EXTRACT_LAT_LON_FROM_HOSTIP_RESPONSE_REGEX "<gml:coordinates>(-?[\\d.]+),(-?[\\d.]+)</gml:coordinates>"
60 #define GEOLOCATION_CONTAINER_NAME "cm_geolocation_container"
61 #define GEOLOCATION_CONTAINER_DATA_TOGGLEBUTTON "togglebutton"
62 #define GEOLOCATION_CONTAINER_DATA_LABEL "label"
64 #define GEOLOCATION_TOGGLE_BUTTON_DATA_CB_ID "toggled_cb_id"
65 #define GEOLOCATION_TOGGLE_BUTTON_DATA_IP "ip"
67 #define GEOLOCATION_NOTEBOOK_DATA_MAPWIDGET "mapwidget"
69 #define GEOLOCATION_MAP_DATA_VIEW "view"
70 #define GEOLOCATION_MAP_DATA_ORIGIN_MARKER "originmarker"
74 #define HOSTIP_URL "http://api.hostip.info/"
76 #define COUNTRY_RESOLVER_SUFFIX "zz.countries.nerd.dk"
79 static gboolean my_messageview_show_hook(gpointer, gpointer);
80 static guint hook_messageview_show;
82 static GRegex *ip_from_received_header_regex = NULL;
83 static GRegex *lat_lon_from_hostip_response_regex = NULL;
85 static GSList *container_list = NULL;
87 static GHashTable *iso_country_hash = NULL;
89 static gchar* get_ip_from_received_header(gchar *received)
91 GMatchInfo *match_info;
95 g_regex_match(ip_from_received_header_regex, received, 0, &match_info);
96 ip = g_match_info_fetch(match_info, 2);
99 /* check for loopback and local subnetworks */
100 if(!strcmp(ip, "127.0.0.1")) {
105 n1 = g_match_info_fetch(match_info, 3);
108 if(!strcmp(n1, "10")) {
113 else if(!strcmp(n1, "192")) {
115 n2 = g_match_info_fetch(match_info, 4);
116 if(n2 && !strcmp(n2, "168")) {
121 /* 172.16.xx to 172.31.x.x */
122 else if(!strcmp(n1, "172")) {
125 n2 = g_match_info_fetch(match_info, 4);
128 if((i2 >= 16) && (i2 <= 31)) {
138 g_match_info_free(match_info);
146 static HeaderEntry hentry[] = {
147 {"Received:", NULL, TRUE},
148 {NULL, NULL, FALSE}};
150 static gchar* get_ip_from_msginfo(MsgInfo *msginfo)
161 file = procmsg_get_message_file_path(msginfo);
162 g_return_val_if_fail(file, NULL);
164 if(g_stat(file, &ss) < 0) {
165 FILE_OP_ERROR(file, "stat");
168 if(!S_ISREG(ss.st_mode)) {
169 g_warning("mail file is not a regular file\n");
173 if((fp = g_fopen(file, "rb")) == NULL) {
174 FILE_OP_ERROR(file, "fopen");
180 while((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry)) != -1) {
183 hp = buf + strlen(hentry[hnum].name);
184 while(*hp == ' ' || *hp == '\t') hp++;
185 new_ip = get_ip_from_received_header(hp);
202 /* inspired by GeoClue */
203 static gboolean get_lat_lon_from_ip(const gchar *ip, double *lat, double *lon)
206 GMatchInfo *match_info;
209 SoupSession *session;
212 /* fetch data from hostip.info */
213 url = g_strdup_printf("%s?ip=%s", HOSTIP_URL, ip);
214 session = soup_session_async_new();
215 msg = soup_message_new(SOUP_METHOD_GET, url);
218 soup_session_send_message(session, msg);
219 g_object_unref(session);
220 if(!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code) || !msg->response_body->data)
223 /* extract longitude and latitude from */
224 g_regex_match(lat_lon_from_hostip_response_regex, msg->response_body->data, 0, &match_info);
226 slon = g_match_info_fetch(match_info, 1);
227 slat = g_match_info_fetch(match_info, 2);
228 g_match_info_free(match_info);
233 *lat = g_ascii_strtod(slat, NULL);
234 *lon = g_ascii_strtod(slon, NULL);
239 /* try to look up country */
240 static const gchar* get_country_from_ip(const gchar *ip)
245 const gchar *val = NULL;
247 tok = g_strsplit_set(ip, ".", 4);
248 if(!(tok[0] && tok[1] && tok[2] && tok[3] && !tok[4])) {
250 debug_print("GeoLocation plugin: Error parsing ip address '%s'\n", ip);
253 uri = g_strjoin(".", tok[3], tok[2], tok[1], tok[0], COUNTRY_RESOLVER_SUFFIX, NULL);
256 addr = soup_address_new(uri, SOUP_ADDRESS_ANY_PORT);
259 if(soup_address_resolve_sync(addr, NULL) == SOUP_STATUS_OK) {
260 const gchar *physical = soup_address_get_physical(addr);
261 val = g_hash_table_lookup(iso_country_hash, physical);
264 g_object_unref(addr);
268 static GtkWidget* create_map_widget()
271 ChamplainLayer *layer;
273 ClutterActor *origin_marker;
274 ClutterColor orange = { 0xf3, 0x94, 0x07, 0xbb };
276 /* create map widget */
277 map = gtk_champlain_embed_new();
278 view = gtk_champlain_embed_get_view(GTK_CHAMPLAIN_EMBED(map));
279 g_object_set(G_OBJECT(view), "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC, "zoom-level", 8, NULL);
281 /* mail origin marker */
282 layer = champlain_layer_new();
283 origin_marker = champlain_marker_new_with_text("mail origin", "Serif 14", NULL, NULL);
284 champlain_marker_set_color(CHAMPLAIN_MARKER(origin_marker), &orange);
285 clutter_container_add(CLUTTER_CONTAINER(layer), origin_marker, NULL);
286 champlain_view_add_layer(view, layer);
288 clutter_actor_show(CLUTTER_ACTOR(view));
289 clutter_actor_show(CLUTTER_ACTOR(layer));
290 clutter_actor_show(origin_marker);
292 g_object_set_data(G_OBJECT(map), GEOLOCATION_MAP_DATA_VIEW, view);
293 g_object_set_data(G_OBJECT(map), GEOLOCATION_MAP_DATA_ORIGIN_MARKER, origin_marker);
298 static int get_notebook_map_page_num(GtkWidget *notebook)
303 wid = g_object_get_data(G_OBJECT(notebook), GEOLOCATION_NOTEBOOK_DATA_MAPWIDGET);
308 map = create_map_widget();
309 page_num = gtk_notebook_append_page(GTK_NOTEBOOK(notebook), map, NULL);
310 gtk_widget_show_all(map);
314 g_object_set_data(G_OBJECT(notebook), GEOLOCATION_NOTEBOOK_DATA_MAPWIDGET, map);
318 page_num = gtk_notebook_page_num(GTK_NOTEBOOK(notebook), wid);
326 static void geolocation_button_toggled_cb(GtkToggleButton *button, gpointer data)
328 if(gtk_toggle_button_get_active(button)) {
332 GtkWidget *container, *label;
334 ip = g_object_get_data(G_OBJECT(button), GEOLOCATION_TOGGLE_BUTTON_DATA_IP);
335 container = gtk_widget_get_parent(GTK_WIDGET(button));
336 g_return_if_fail(container);
337 label = g_object_get_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_LABEL);
338 g_return_if_fail(label);
340 if(ip && get_lat_lon_from_ip(ip, &lat, &lon)) {
343 MessageView *messageview;
345 ClutterActor *origin_marker;
348 /* TRANSLATORS: The two numbers are latitude and longitude coordinates */
349 text = g_strdup_printf(_("Found location: (%.2f,%.2f)"), lat, lon);
350 gtk_label_set_text(GTK_LABEL(label), text);
351 debug_print("%s\n", text);
353 /* switch to map widget, and center on found location */
355 notebook = messageview->mimeview->mime_notebook;
356 page_num = get_notebook_map_page_num(notebook);
357 map = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num);
358 view = g_object_get_data(G_OBJECT(map), GEOLOCATION_MAP_DATA_VIEW);
359 origin_marker = g_object_get_data(G_OBJECT(map), GEOLOCATION_MAP_DATA_ORIGIN_MARKER);
360 champlain_view_center_on(CHAMPLAIN_VIEW(view), lat, lon);
361 champlain_base_marker_set_position(CHAMPLAIN_BASE_MARKER(origin_marker), lat, lon);
362 gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), page_num);
367 if(ip && ((country = get_country_from_ip(ip)) != NULL)) {
368 /* TRANSLATORS: The country name is appended to the string */
369 text = g_strconcat(_("Alleged country of origin: "), country, NULL);
372 /* TRANSLATORS: The IP address is appended to the string */
373 text = g_strconcat(_("Could not resolve location of IP address "), ip, NULL);
375 gtk_label_set_text(GTK_LABEL(label), text);
376 debug_print("%s\n", text);
377 gtk_toggle_button_set_active(button, FALSE);
382 /* swich to first notebook page */
383 MessageView *messageview;
385 gtk_notebook_set_current_page(GTK_NOTEBOOK(messageview->mimeview->mime_notebook), 0);
389 static void container_finalize_cb(gpointer data, GObject *where_the_object_was)
391 container_list = g_slist_remove(container_list, where_the_object_was);
394 static GtkWidget* get_geolocation_container_from_messageview(MessageView *messageview)
396 GtkWidget *container = NULL;
398 GList *children, *walk;
402 g_return_val_if_fail(messageview && messageview->mimeview && messageview->mimeview->paned, NULL);
404 vbox = gtk_paned_get_child2(GTK_PANED(messageview->mimeview->paned));
406 /* check if there is already a geolocation container */
407 children = gtk_container_get_children(GTK_CONTAINER(vbox));
408 for(walk = children; walk; walk = walk->next) {
409 GtkWidget *child = walk->data;
410 if(!strcmp(gtk_widget_get_name(child), GEOLOCATION_CONTAINER_NAME)) {
415 g_list_free(children);
417 /* if not, create one */
421 GdkPixbuf *pixbuf, *scaled_pixbuf;
423 container = gtk_hbox_new(FALSE, 0);
424 gtk_widget_set_name(container, GEOLOCATION_CONTAINER_NAME);
427 pixbuf = gdk_pixbuf_new_from_inline(-1, pixmap_earth, FALSE, NULL);
428 scaled_pixbuf = gdk_pixbuf_scale_simple(pixbuf, 16, 16, GDK_INTERP_BILINEAR);
429 g_object_unref(pixbuf);
430 image = gtk_image_new_from_pixbuf(scaled_pixbuf);
431 gtk_widget_show(image);
432 g_object_unref(scaled_pixbuf);
433 button = gtk_toggle_button_new();
434 gtk_widget_show(button);
435 gtk_container_add(GTK_CONTAINER(button), image);
436 gtk_box_pack_start(GTK_BOX(container), button, FALSE, FALSE, 0);
437 g_object_set_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_TOGGLEBUTTON, button);
440 label = gtk_label_new("");
441 gtk_widget_show(label);
442 gtk_box_pack_start(GTK_BOX(container), label, FALSE, FALSE, 2);
443 g_object_set_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_LABEL, label);
445 gtk_box_pack_start(GTK_BOX(vbox), container, FALSE, FALSE, 0);
447 /* add new container to list */
448 container_list = g_slist_prepend(container_list, container);
449 g_object_weak_ref(G_OBJECT(container), container_finalize_cb, NULL);
451 /* if it existed, changed the button callback data */
453 /* disconnect old signal handler from button */
455 button = g_object_get_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_TOGGLEBUTTON);
456 g_return_val_if_fail(button, NULL);
457 id = g_object_get_data(G_OBJECT(button), GEOLOCATION_TOGGLE_BUTTON_DATA_CB_ID);
458 g_signal_handler_disconnect(button, *id);
461 /* connect new signal handler to button, and store its id */
462 cb_id = g_new(gulong, 1);
463 *cb_id = g_signal_connect(button, "toggled", G_CALLBACK(geolocation_button_toggled_cb), messageview);
464 g_object_set_data_full(G_OBJECT(button), GEOLOCATION_TOGGLE_BUTTON_DATA_CB_ID, cb_id, g_free);
469 static gboolean my_messageview_show_hook(gpointer source, gpointer data)
471 MessageView *messageview;
472 GtkWidget *container;
476 messageview = source;
478 g_return_val_if_fail(messageview, FALSE);
480 container = get_geolocation_container_from_messageview(messageview);
481 g_return_val_if_fail(container, FALSE);
483 /* make sure toggle button is turned off */
484 button = g_object_get_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_TOGGLEBUTTON);
485 g_return_val_if_fail(button, FALSE);
486 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) {
487 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
490 /* do nothing except hiding the button if claws mail is in offline mode */
491 if(prefs_common.work_offline) {
492 gtk_widget_hide(container);
496 /* try to get ip address from message */
497 if(!messageview->msginfo) {
498 debug_print("Warning: Messageview does not have a MsgInfo\n");
501 ip = get_ip_from_msginfo(messageview->msginfo);
503 debug_print("Could not get IP address from message\n");
504 gtk_widget_hide(container);
508 debug_print("Found sender ip address: %s\n", ip);
509 label = g_object_get_data(G_OBJECT(container), GEOLOCATION_CONTAINER_DATA_LABEL);
512 text = g_strconcat(_("Try to locate sender"), NULL);
513 gtk_label_set_text(GTK_LABEL(label), text);
516 /* button takes ownership of ip string */
517 g_object_set_data_full(G_OBJECT(button), GEOLOCATION_TOGGLE_BUTTON_DATA_IP, ip, g_free);
518 gtk_widget_show(container);
524 #define ISO_IN(k,v) g_hash_table_insert(iso_country_hash, (k), (v))
525 static void fill_iso_country_hash(void)
527 iso_country_hash = g_hash_table_new(g_str_hash, g_str_equal);
528 ISO_IN("127.0.0.20", _("Andorra"));
529 ISO_IN("127.0.3.16", _("United Arab Emirates"));
530 ISO_IN("127.0.0.4", _("Afghanistan"));
531 ISO_IN("127.0.0.28", _("Antigua And Barbuda"));
532 ISO_IN("127.0.2.148", _("Anguilla"));
533 ISO_IN("127.0.0.8", _("Albania"));
534 ISO_IN("127.0.0.51", _("Armenia"));
535 ISO_IN("127.0.2.18", _("Netherlands Antilles"));
536 ISO_IN("127.0.0.24", _("Angola"));
537 ISO_IN("127.0.0.10", _("Antarctica"));
538 ISO_IN("127.0.0.32", _("Argentina"));
539 ISO_IN("127.0.0.16", _("American Samoa"));
540 ISO_IN("127.0.0.40", _("Austria"));
541 ISO_IN("127.0.0.36", _("Australia"));
542 ISO_IN("127.0.2.21", _("Aruba"));
543 ISO_IN("127.0.0.31", _("Azerbaijan"));
544 ISO_IN("127.0.0.70", _("Bosnia And Herzegovina"));
545 ISO_IN("127.0.0.52", _("Barbados"));
546 ISO_IN("127.0.0.50", _("Bangladesh"));
547 ISO_IN("127.0.0.56", _("Belgium"));
548 ISO_IN("127.0.3.86", _("Burkina Faso"));
549 ISO_IN("127.0.0.100", _("Bulgaria"));
550 ISO_IN("127.0.0.48", _("Bahrain"));
551 ISO_IN("127.0.0.108", _("Burundi"));
552 ISO_IN("127.0.0.204", _("Benin"));
553 ISO_IN("127.0.0.60", _("Bermuda"));
554 ISO_IN("127.0.0.96", _("Brunei Darussalam"));
555 ISO_IN("127.0.0.68", _("Bolivia"));
556 ISO_IN("127.0.0.76", _("Brazil"));
557 ISO_IN("127.0.0.44", _("Bahamas"));
558 ISO_IN("127.0.0.64", _("Bhutan"));
559 ISO_IN("127.0.0.74", _("Bouvet Island"));
560 ISO_IN("127.0.0.72", _("Botswana"));
561 ISO_IN("127.0.0.112", _("Belarus"));
562 ISO_IN("127.0.0.84", _("Belize"));
563 ISO_IN("127.0.0.124", _("Canada"));
564 ISO_IN("127.0.0.166", _("Cocos (Keeling) Islands"));
565 ISO_IN("127.0.0.140", _("Central African Republic"));
566 ISO_IN("127.0.0.178", _("Congo"));
567 ISO_IN("127.0.2.244", _("Switzerland"));
568 ISO_IN("127.0.1.128", _("Cote D'Ivoire"));
569 ISO_IN("127.0.0.184", _("Cook Islands"));
570 ISO_IN("127.0.0.152", _("Chile"));
571 ISO_IN("127.0.0.120", _("Cameroon"));
572 ISO_IN("127.0.0.156", _("China"));
573 ISO_IN("127.0.0.170", _("Colombia"));
574 ISO_IN("127.0.0.188", _("Costa Rica"));
575 ISO_IN("127.0.0.192", _("Cuba"));
576 ISO_IN("127.0.0.132", _("Cape Verde"));
577 ISO_IN("127.0.0.162", _("Christmas Island"));
578 ISO_IN("127.0.0.196", _("Cyprus"));
579 ISO_IN("127.0.0.203", _("Czech Republic"));
580 ISO_IN("127.0.1.20", _("Germany"));
581 ISO_IN("127.0.1.6", _("Djibouti"));
582 ISO_IN("127.0.0.208", _("Denmark"));
583 ISO_IN("127.0.0.212", _("Dominica"));
584 ISO_IN("127.0.0.214", _("Dominican Republic"));
585 ISO_IN("127.0.0.12", _("Algeria"));
586 ISO_IN("127.0.0.218", _("Ecuador"));
587 ISO_IN("127.0.0.233", _("Estonia"));
588 ISO_IN("127.0.3.50", _("Egypt"));
589 ISO_IN("127.0.2.220", _("Western Sahara"));
590 ISO_IN("127.0.0.232", _("Eritrea"));
591 ISO_IN("127.0.2.212", _("Spain"));
592 ISO_IN("127.0.0.231", _("Ethiopia"));
593 ISO_IN("127.0.0.246", _("Finland"));
594 ISO_IN("127.0.0.242", _("Fiji"));
595 ISO_IN("127.0.0.238", _("Falkland Islands (Malvinas)"));
596 ISO_IN("127.0.2.71", _("Micronesia, Federated States Of"));
597 ISO_IN("127.0.0.234", _("Faroe Islands"));
598 ISO_IN("127.0.0.250", _("France"));
599 ISO_IN("127.0.0.249", _("France, Metropolitan"));
600 ISO_IN("127.0.1.10", _("Gabon"));
601 ISO_IN("127.0.3.58", _("United Kingdom"));
602 ISO_IN("127.0.1.52", _("Grenada"));
603 ISO_IN("127.0.1.12", _("Georgia"));
604 ISO_IN("127.0.0.254", _("French Guiana"));
605 ISO_IN("127.0.1.32", _("Ghana"));
606 ISO_IN("127.0.1.36", _("Gibraltar"));
607 ISO_IN("127.0.1.48", _("Greenland"));
608 ISO_IN("127.0.1.14", _("Gambia"));
609 ISO_IN("127.0.1.68", _("Guinea"));
610 ISO_IN("127.0.1.56", _("Guadeloupe"));
611 ISO_IN("127.0.0.226", _("Equatorial Guinea"));
612 ISO_IN("127.0.1.44", _("Greece"));
613 ISO_IN("127.0.0.239", _("South Georgia And The South Sandwich Islands"));
614 ISO_IN("127.0.1.64", _("Guatemala"));
615 ISO_IN("127.0.1.60", _("Guam"));
616 ISO_IN("127.0.2.112", _("Guinea-Bissau"));
617 ISO_IN("127.0.1.72", _("Guyana"));
618 ISO_IN("127.0.1.88", _("Hong Kong"));
619 ISO_IN("127.0.1.78", _("Heard Island And Mcdonald Islands"));
620 ISO_IN("127.0.1.84", _("Honduras"));
621 ISO_IN("127.0.0.191", _("Croatia"));
622 ISO_IN("127.0.1.76", _("Haiti"));
623 ISO_IN("127.0.1.92", _("Hungary"));
624 ISO_IN("127.0.1.104", _("Indonesia"));
625 ISO_IN("127.0.1.116", _("Ireland"));
626 ISO_IN("127.0.1.120", _("Israel"));
627 ISO_IN("127.0.1.100", _("India"));
628 ISO_IN("127.0.0.86", _("British Indian Ocean Territory"));
629 ISO_IN("127.0.1.112", _("Iraq"));
630 ISO_IN("127.0.1.108", _("Iran, Islamic Republic Of"));
631 ISO_IN("127.0.1.96", _("Iceland"));
632 ISO_IN("127.0.1.124", _("Italy"));
633 ISO_IN("127.0.1.132", _("Jamaica"));
634 ISO_IN("127.0.1.144", _("Jordan"));
635 ISO_IN("127.0.1.136", _("Japan"));
636 ISO_IN("127.0.1.148", _("Kenya"));
637 ISO_IN("127.0.1.161", _("Kyrgyzstan"));
638 ISO_IN("127.0.0.116", _("Cambodia"));
639 ISO_IN("127.0.1.40", _("Kiribati"));
640 ISO_IN("127.0.0.174", _("Comoros"));
641 ISO_IN("127.0.2.147", _("Saint Kitts And Nevis"));
642 ISO_IN("127.0.1.152", _("Korea, Democratic People'S Republic Of"));
643 ISO_IN("127.0.1.154", _("Korea, Republic Of"));
644 ISO_IN("127.0.1.158", _("Kuwait"));
645 ISO_IN("127.0.0.136", _("Cayman Islands"));
646 ISO_IN("127.0.1.142", _("Kazakhstan"));
647 ISO_IN("127.0.1.162", _("Lao People'S Democratic Republic"));
648 ISO_IN("127.0.1.166", _("Lebanon"));
649 ISO_IN("127.0.2.150", _("Saint Lucia"));
650 ISO_IN("127.0.1.182", _("Liechtenstein"));
651 ISO_IN("127.0.0.144", _("Sri Lanka"));
652 ISO_IN("127.0.1.174", _("Liberia"));
653 ISO_IN("127.0.1.170", _("Lesotho"));
654 ISO_IN("127.0.1.184", _("Lithuania"));
655 ISO_IN("127.0.1.186", _("Luxembourg"));
656 ISO_IN("127.0.1.172", _("Latvia"));
657 ISO_IN("127.0.1.178", _("Libyan Arab Jamahiriya"));
658 ISO_IN("127.0.1.248", _("Morocco"));
659 ISO_IN("127.0.1.236", _("Monaco"));
660 ISO_IN("127.0.1.242", _("Moldova, Republic Of"));
661 ISO_IN("127.0.1.194", _("Madagascar"));
662 ISO_IN("127.0.2.72", _("Marshall Islands"));
663 ISO_IN("127.0.3.39", _("Macedonia, The Former Yugoslav Republic Of"));
664 ISO_IN("127.0.1.210", _("Mali"));
665 ISO_IN("127.0.0.104", _("Myanmar"));
666 ISO_IN("127.0.1.240", _("Mongolia"));
667 ISO_IN("127.0.1.190", _("Macao"));
668 ISO_IN("127.0.2.68", _("Northern Mariana Islands"));
669 ISO_IN("127.0.1.218", _("Martinique"));
670 ISO_IN("127.0.1.222", _("Mauritania"));
671 ISO_IN("127.0.1.244", _("Montserrat"));
672 ISO_IN("127.0.1.214", _("Malta"));
673 ISO_IN("127.0.1.224", _("Mauritius"));
674 ISO_IN("127.0.1.206", _("Maldives"));
675 ISO_IN("127.0.1.198", _("Malawi"));
676 ISO_IN("127.0.1.228", _("Mexico"));
677 ISO_IN("127.0.1.202", _("Malaysia"));
678 ISO_IN("127.0.1.252", _("Mozambique"));
679 ISO_IN("127.0.2.4", _("Namibia"));
680 ISO_IN("127.0.2.28", _("New Caledonia"));
681 ISO_IN("127.0.2.50", _("Niger"));
682 ISO_IN("127.0.2.62", _("Norfolk Island"));
683 ISO_IN("127.0.2.54", _("Nigeria"));
684 ISO_IN("127.0.2.46", _("Nicaragua"));
685 ISO_IN("127.0.2.16", _("Netherlands"));
686 ISO_IN("127.0.2.66", _("Norway"));
687 ISO_IN("127.0.2.12", _("Nepal"));
688 ISO_IN("127.0.2.8", _("Nauru"));
689 ISO_IN("127.0.2.58", _("Niue"));
690 ISO_IN("127.0.2.42", _("New Zealand"));
691 ISO_IN("127.0.2.0", _("Oman"));
692 ISO_IN("127.0.2.79", _("Panama"));
693 ISO_IN("127.0.2.92", _("Peru"));
694 ISO_IN("127.0.1.2", _("French Polynesia"));
695 ISO_IN("127.0.2.86", _("Papua New Guinea"));
696 ISO_IN("127.0.2.96", _("Philippines"));
697 ISO_IN("127.0.2.74", _("Pakistan"));
698 ISO_IN("127.0.2.104", _("Poland"));
699 ISO_IN("127.0.2.154", _("Saint Pierre And Miquelon"));
700 ISO_IN("127.0.2.100", _("Pitcairn"));
701 ISO_IN("127.0.2.118", _("Puerto Rico"));
702 ISO_IN("127.0.2.108", _("Portugal"));
703 ISO_IN("127.0.2.73", _("Palau"));
704 ISO_IN("127.0.2.88", _("Paraguay"));
705 ISO_IN("127.0.2.122", _("Qatar"));
706 ISO_IN("127.0.2.126", _("Reunion"));
707 ISO_IN("127.0.2.130", _("Romania"));
708 ISO_IN("127.0.2.131", _("Russian Federation"));
709 ISO_IN("127.0.2.134", _("Rwanda"));
710 ISO_IN("127.0.2.170", _("Saudi Arabia"));
711 ISO_IN("127.0.0.90", _("Solomon Islands"));
712 ISO_IN("127.0.2.178", _("Seychelles"));
713 ISO_IN("127.0.2.224", _("Sudan"));
714 ISO_IN("127.0.2.240", _("Sweden"));
715 ISO_IN("127.0.2.190", _("Singapore"));
716 ISO_IN("127.0.2.142", _("Saint Helena"));
717 ISO_IN("127.0.2.193", _("Slovenia"));
718 ISO_IN("127.0.2.232", _("Svalbard And Jan Mayen"));
719 ISO_IN("127.0.2.191", _("Slovakia"));
720 ISO_IN("127.0.2.182", _("Sierra Leone"));
721 ISO_IN("127.0.2.162", _("San Marino"));
722 ISO_IN("127.0.2.174", _("Senegal"));
723 ISO_IN("127.0.2.194", _("Somalia"));
724 ISO_IN("127.0.2.228", _("Suriname"));
725 ISO_IN("127.0.2.166", _("Sao Tome And Principe"));
726 ISO_IN("127.0.0.222", _("El Salvador"));
727 ISO_IN("127.0.2.248", _("Syrian Arab Republic"));
728 ISO_IN("127.0.2.236", _("Swaziland"));
729 ISO_IN("127.0.3.28", _("Turks And Caicos Islands"));
730 ISO_IN("127.0.0.148", _("Chad"));
731 ISO_IN("127.0.1.4", _("French Southern Territories"));
732 ISO_IN("127.0.3.0", _("Togo"));
733 ISO_IN("127.0.2.252", _("Thailand"));
734 ISO_IN("127.0.2.250", _("Tajikistan"));
735 ISO_IN("127.0.3.4", _("Tokelau"));
736 ISO_IN("127.0.3.27", _("Turkmenistan"));
737 ISO_IN("127.0.3.20", _("Tunisia"));
738 ISO_IN("127.0.3.8", _("Tonga"));
739 ISO_IN("127.0.2.114", _("East Timor"));
740 ISO_IN("127.0.3.24", _("Turkey"));
741 ISO_IN("127.0.3.12", _("Trinidad And Tobago"));
742 ISO_IN("127.0.3.30", _("Tuvalu"));
743 ISO_IN("127.0.0.158", _("Taiwan, Province Of China"));
744 ISO_IN("127.0.3.66", _("Tanzania, United Republic Of"));
745 ISO_IN("127.0.3.36", _("Ukraine"));
746 ISO_IN("127.0.3.32", _("Uganda"));
747 ISO_IN("127.0.3.58", _("United Kingdom"));
748 ISO_IN("127.0.2.69", _("United States Minor Outlying Islands"));
749 ISO_IN("127.0.3.72", _("United States"));
750 ISO_IN("127.0.3.90", _("Uruguay"));
751 ISO_IN("127.0.3.92", _("Uzbekistan"));
752 ISO_IN("127.0.1.80", _("Holy See (Vatican City State)"));
753 ISO_IN("127.0.2.158", _("Saint Vincent And The Grenadines"));
754 ISO_IN("127.0.3.94", _("Venezuela"));
755 ISO_IN("127.0.0.92", _("Virgin Islands, British"));
756 ISO_IN("127.0.3.82", _("Virgin Islands, U.S."));
757 ISO_IN("127.0.2.192", _("Viet Nam"));
758 ISO_IN("127.0.2.36", _("Vanuatu"));
759 ISO_IN("127.0.3.108", _("Wallis And Futuna"));
760 ISO_IN("127.0.3.114", _("Samoa"));
761 ISO_IN("127.0.3.119", _("Yemen"));
762 ISO_IN("127.0.0.175", _("Mayotte"));
763 ISO_IN("127.0.3.123", _("Serbia And Montenegro"));
764 ISO_IN("127.0.2.198", _("South Africa"));
765 ISO_IN("127.0.3.126", _("Zambia"));
766 ISO_IN("127.0.0.180", _("Democratic Republic Of The Congo"));
767 ISO_IN("127.0.2.204", _("Zimbabwe"));
771 gint plugin_init(gchar **error)
776 if(!check_plugin_version(MAKE_NUMERIC_VERSION(3,7,1,55),
777 VERSION_NUMERIC, _("GeoLocation"), error))
780 if(gtk_clutter_init(0, NULL) != CLUTTER_INIT_SUCCESS)
782 *error = g_strdup_printf(_("Could not initialize clutter"));
786 ip_from_received_header_regex = g_regex_new(EXTRACT_IP_FROM_RECEIVED_HEADER_REGEX, 0, 0, &err);
788 *error = g_strdup_printf(_("Could not create regular expression: %s\n"), err->message);
793 lat_lon_from_hostip_response_regex = g_regex_new(EXTRACT_LAT_LON_FROM_HOSTIP_RESPONSE_REGEX, 0, 0, &err);
795 *error = g_strdup_printf(_("Could not create regular expression: %s\n"), err->message);
797 g_regex_unref(ip_from_received_header_regex);
801 hook_messageview_show = hooks_register_hook(MESSAGE_VIEW_SHOW_DONE_HOOKLIST, my_messageview_show_hook, NULL);
802 if(hook_messageview_show == (guint) -1) {
803 *error = g_strdup(_("Failed to register messageview_show hook in the GeoLocation plugin"));
804 g_regex_unref(ip_from_received_header_regex);
805 g_regex_unref(lat_lon_from_hostip_response_regex);
809 fill_iso_country_hash();
811 debug_print("GeoLocation plugin loaded\n");
816 gboolean plugin_done(void)
819 hooks_unregister_hook(MESSAGE_VIEW_SHOW_DONE_HOOKLIST, hook_messageview_show);
821 if(!claws_is_exiting()) {
824 copy = g_slist_copy(container_list);
825 for(walk = copy; walk; walk = walk->next) {
826 GtkWidget *wid = walk->data;
827 GtkWidget *button = g_object_get_data(G_OBJECT(wid), GEOLOCATION_CONTAINER_DATA_TOGGLEBUTTON);
828 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)))
829 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
830 gtk_widget_destroy(GTK_WIDGET(wid));
834 g_slist_free(container_list);
835 container_list = NULL;
838 if(ip_from_received_header_regex) {
839 g_regex_unref(ip_from_received_header_regex);
840 ip_from_received_header_regex = NULL;
843 if(lat_lon_from_hostip_response_regex) {
844 g_regex_unref(lat_lon_from_hostip_response_regex);
845 lat_lon_from_hostip_response_regex = NULL;
848 if(iso_country_hash) {
849 g_hash_table_destroy(iso_country_hash);
850 iso_country_hash = NULL;
854 debug_print("GeoLocation plugin unloaded\n");
856 /* returning FALSE because dependant libraries may not be unload-safe. */
860 const gchar *plugin_name(void)
862 return _("GeoLocation");
865 const gchar *plugin_desc(void)
867 return _("This plugin provides GeoLocation functionality "
868 "for Claws Mail.\n\n"
869 "Warning: It is technically impossible to derive the geographic "
870 "location of senders from their E-Mails with any amount of certainty. "
871 "The results presented by "
872 "this plugin are only rough estimates. In particular, mailing list managers "
873 "often strip sender information from the mails, so mails from mailing lists "
874 "may be assigned to the location of the mailing list server instead of the "
876 "When in doubt, don't trust the results of this plugin, and don't rely on this "
877 "information to divorce your spouse.\n"
878 "\nFeedback to <berndth@gmx.de> is welcome "
879 "(but only if it's not about marital quarrels).");
882 const gchar *plugin_type(void)
887 const gchar *plugin_licence(void)
892 const gchar *plugin_version(void)
897 struct PluginFeature *plugin_provides(void)
899 static struct PluginFeature features[] =
900 { {PLUGIN_UTILITY, N_("GeoLocation integration")},
901 {PLUGIN_NOTHING, NULL}};