mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

src/mn-main.c (15141B) - raw

      1 /*
      2  * Mail Notification
      3  * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License along
     16  * with this program; if not, write to the Free Software Foundation, Inc.,
     17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
     18  */
     19 
     20 #include <string.h>
     21 #include <stdlib.h>
     22 #include <signal.h>
     23 #include <glib/gi18n.h>
     24 #include <gnome.h>
     25 #include <libgnomevfs/gnome-vfs.h>
     26 #include <libnotify/notify.h>
     27 #include <dbus/dbus-glib-lowlevel.h>
     28 #if WITH_GMIME
     29 #include <gmime/gmime.h>
     30 #include "mn-gmime-stream-vfs.h"
     31 #endif
     32 #if WITH_MBOX || WITH_MOZILLA || WITH_MH || WITH_MAILDIR || WITH_SYLPHEED
     33 #include "mn-vfs-mailbox.h"
     34 #endif
     35 #if WITH_MAILDIR
     36 #include "mn-maildir-message.h"
     37 #endif
     38 #if WITH_SYLPHEED
     39 #include "mn-sylpheed-message.h"
     40 #endif
     41 #if WITH_EVOLUTION
     42 #include "mn-evolution-message.h"
     43 #endif
     44 #include "mn-conf.h"
     45 #include "mn-util.h"
     46 #include "mn-stock.h"
     47 #include "mn-shell.h"
     48 #include "mn-message.h"
     49 #include "mn-client-dbus.h"
     50 #include "mn-server.h"
     51 
     52 #define CALL_SERVER(call)		\
     53   {					\
     54     if (! (call))			\
     55       handle_server_call_error(err);	\
     56   }
     57 
     58 typedef struct
     59 {
     60   const char	*name;
     61   gboolean	enabled;
     62 } Component;
     63 
     64 static gboolean arg_enable_info = FALSE;
     65 
     66 static unsigned int bus_disconnected_timeout_id = 0;
     67 
     68 static gboolean bus_disconnected_cb (gpointer data) G_GNUC_NORETURN;
     69 
     70 static void
     71 print_components (const Component *components, int n_components)
     72 {
     73   int i;
     74 
     75   for (i = 0; i < n_components; i++)
     76     g_print("  %-30s %s\n", components[i].name, components[i].enabled ? _("yes") : _("no"));
     77 }
     78 
     79 static void
     80 print_version (void)
     81 {
     82   /*
     83    * Here and everywhere else, backends and features are sorted
     84    * alphabetically.
     85    */
     86 
     87   static const Component mailbox_backends[] = {
     88     { "Evolution",		WITH_EVOLUTION		},
     89     { "Gmail",			WITH_GMAIL		},
     90     { "IMAP",			WITH_IMAP		},
     91     { "Maildir",		WITH_MAILDIR		},
     92     { "mbox",			WITH_MBOX		},
     93     { "MH",			WITH_MH			},
     94     { "Mozilla products",	WITH_MOZILLA		},
     95     { "POP3",			WITH_POP3		},
     96     { "Sylpheed",		WITH_SYLPHEED		},
     97     { "Windows Live Hotmail",	WITH_HOTMAIL		},
     98     { "Yahoo! Mail",		WITH_YAHOO		}
     99   };
    100 
    101   static const Component pi_features[] = {
    102     { "IPv6",			WITH_IPV6		},
    103     { "SASL",			WITH_SASL		},
    104     { "SSL/TLS",		WITH_SSL		}
    105   };
    106 
    107   g_print(_("%s version %s\n"), _("Mail Notification"), VERSION);
    108   g_print("Copyright (C) 2003-2008 Jean-Yves Lefort.\n");
    109 
    110   g_print("\n");
    111 
    112   g_print(_("Mailbox backends:\n"));
    113   print_components(mailbox_backends, G_N_ELEMENTS(mailbox_backends));
    114 
    115   g_print("\n");
    116 
    117   g_print(_("IMAP and POP3 features:\n"));
    118   print_components(pi_features, G_N_ELEMENTS(pi_features));
    119 }
    120 
    121 static void
    122 info_log_cb (const char *log_domain,
    123 	     GLogLevelFlags log_level,
    124 	     const char *message,
    125 	     gpointer user_data)
    126 {
    127   if (arg_enable_info)
    128     g_log_default_handler(log_domain, log_level, message, user_data);
    129 }
    130 
    131 static void
    132 init_classes (void)
    133 {
    134   int i;
    135 
    136 #if WITH_GMIME
    137   g_type_class_ref(MN_TYPE_GMIME_STREAM_VFS);
    138   g_type_class_ref(GMIME_TYPE_PARSER);
    139   g_type_class_ref(GMIME_TYPE_STREAM_MEM);
    140   g_type_class_ref(GMIME_TYPE_MESSAGE);
    141 #endif /* WITH_GMIME */
    142 #if WITH_MBOX || WITH_MOZILLA || WITH_MH || WITH_MAILDIR || WITH_SYLPHEED
    143   for (i = 0; mn_vfs_mailbox_backend_types[i]; i++)
    144     g_type_class_ref(mn_vfs_mailbox_backend_types[i]);
    145 #endif
    146 #if WITH_MAILDIR
    147   g_type_class_ref(MN_TYPE_MAILDIR_MESSAGE);
    148 #endif
    149 #if WITH_SYLPHEED
    150   g_type_class_ref(MN_TYPE_SYLPHEED_MESSAGE);
    151 #endif
    152   g_type_class_ref(MN_TYPE_MESSAGE);
    153 }
    154 
    155 #if WITH_GMIME
    156 static void
    157 init_gmime (void)
    158 {
    159   GPtrArray *array;
    160   GSList *fallback_charsets;
    161   GSList *l;
    162 
    163   g_mime_init(0);
    164 
    165   array = g_ptr_array_new();
    166 
    167   fallback_charsets = mn_conf_get_string_list(MN_CONF_FALLBACK_CHARSETS);
    168 
    169   MN_LIST_FOREACH(l, fallback_charsets)
    170     {
    171       const char *charset = l->data;
    172 
    173       if (! strcmp(charset, "user"))
    174 	{
    175 	  const char *user_charset;
    176 
    177 	  g_get_charset(&user_charset);
    178 	  g_ptr_array_add(array, g_strdup(user_charset));
    179 	}
    180       else
    181 	g_ptr_array_add(array, g_strdup(charset));
    182     }
    183 
    184   mn_g_slist_free_deep(fallback_charsets);
    185 
    186   g_ptr_array_add(array, NULL);	/* canary */
    187 
    188   g_mime_set_user_charsets((const char **) array->pdata);
    189 
    190   /*
    191    * Note that because of
    192    * http://bugzilla.gnome.org/show_bug.cgi?id=509434, we do not free
    193    * the strings of the array (since GMime does not copy them).
    194    */
    195   g_ptr_array_free(array, TRUE);
    196 }
    197 #endif /* WITH_GMIME */
    198 
    199 static void
    200 handle_server_call_error (GError *err)
    201 {
    202   g_return_if_fail(err != NULL);
    203 
    204   mn_show_fatal_error_dialog(NULL, _("Unable to contact the running Mail Notification instance: %s."), err->message);
    205 }
    206 
    207 static gboolean
    208 has_icon_path (const char *path)
    209 {
    210   char **paths;
    211   int i;
    212   gboolean has = FALSE;
    213 
    214   gtk_icon_theme_get_search_path(gtk_icon_theme_get_default(), &paths, NULL);
    215   for (i = 0; paths[i]; i++)
    216     if (! strcmp(paths[i], path))
    217       {
    218 	has = TRUE;
    219 	break;
    220       }
    221   g_strfreev(paths);
    222 
    223   return has;
    224 }
    225 
    226 /*
    227  * This is needed when MN is not installed in the standard prefix (as
    228  * is the case for my test builds).
    229  */
    230 static void
    231 ensure_icon_path (void)
    232 {
    233   static const char *icon_path = DATADIR G_DIR_SEPARATOR_S "icons";
    234 
    235   if (! has_icon_path(icon_path))
    236     gtk_icon_theme_prepend_search_path(gtk_icon_theme_get_default(), icon_path);
    237 }
    238 
    239 static void
    240 report_option_ignored (const char *option_name)
    241 {
    242   g_return_if_fail(option_name != NULL);
    243 
    244   g_message(_("%s option ignored since Mail Notification is not already running"), option_name);
    245 }
    246 
    247 static gboolean
    248 bus_disconnected_cb (gpointer data)
    249 {
    250   mn_show_fatal_error_dialog(NULL, _("The connection to the D-Bus session bus was lost."));
    251 }
    252 
    253 static DBusHandlerResult
    254 session_bus_filter_cb (DBusConnection *conn, DBusMessage *message, void *user_data)
    255 {
    256   if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected"))
    257     {
    258       /*
    259        * If the user is logging out there is no reason to display the
    260        * error dialog, so wait a bit, hoping that we will be killed
    261        * before the timeout triggers.
    262        */
    263 
    264       if (bus_disconnected_timeout_id == 0)
    265 	bus_disconnected_timeout_id = gdk_threads_add_timeout(5000, bus_disconnected_cb, NULL);
    266 
    267       return DBUS_HANDLER_RESULT_HANDLED;
    268     }
    269 
    270   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    271 }
    272 
    273 static DBusGConnection *
    274 connect_to_session_bus (void)
    275 {
    276   GError *err = NULL;
    277   DBusGConnection *bus;
    278   DBusConnection *raw_bus;
    279 
    280   bus = dbus_g_bus_get(DBUS_BUS_SESSION, &err);
    281   if (! bus)
    282     {
    283       mn_show_fatal_error_dialog(NULL, _("Unable to connect to the D-Bus session bus: %s."), err->message);
    284       g_error_free(err);
    285     }
    286 
    287   raw_bus = dbus_g_connection_get_connection(bus);
    288 
    289   dbus_connection_set_exit_on_disconnect(raw_bus, FALSE);
    290 
    291   if (! dbus_connection_add_filter(raw_bus, session_bus_filter_cb, NULL, NULL))
    292     /* too unlikely to be worth a translation */
    293     mn_show_fatal_error_dialog(NULL, "Unable to add a D-Bus filter: not enough memory.");
    294 
    295   return bus;
    296 }
    297 
    298 /*
    299  * A memory management bug in DBusGProxy
    300  * (https://bugs.freedesktop.org/show_bug.cgi?id=14030) prevents us
    301  * from unreferencing the proxy, so provide an eternal singleton
    302  * proxy.
    303  */
    304 static DBusGProxy *
    305 get_bus_proxy (DBusGConnection *bus)
    306 {
    307   DBusGProxy *proxy;
    308 
    309   g_return_val_if_fail(bus != NULL, NULL);
    310 
    311   proxy = dbus_g_proxy_new_for_name(bus,
    312 				    DBUS_SERVICE_DBUS,
    313 				    DBUS_PATH_DBUS,
    314 				    DBUS_INTERFACE_DBUS);
    315 
    316 #if WITH_EVOLUTION
    317   /* needed by MNEvolutionClient */
    318   dbus_g_proxy_add_signal(proxy,
    319 			  "NameOwnerChanged",
    320 			  G_TYPE_STRING, /* service_name */
    321 			  G_TYPE_STRING, /* old_owner */
    322 			  G_TYPE_STRING, /* new_owner */
    323 			  G_TYPE_INVALID);
    324 #endif
    325 
    326   return proxy;
    327 }
    328 
    329 int
    330 main (int argc, char **argv)
    331 {
    332   gboolean arg_version = FALSE;
    333   gboolean arg_display_properties = FALSE;
    334   gboolean arg_display_about = FALSE;
    335   gboolean arg_consider_new_mail_as_read = FALSE;
    336   gboolean arg_update = FALSE;
    337   gboolean arg_print_summary = FALSE;
    338   gboolean arg_unset_obsolete_configuration = FALSE;
    339   gboolean arg_quit = FALSE;
    340   const GOptionEntry options[] = {
    341     {
    342       "version",
    343       'v',
    344       0,
    345       G_OPTION_ARG_NONE,
    346       &arg_version,
    347       N_("Show version information"),
    348       NULL
    349     },
    350     {
    351       "enable-info",
    352       'i',
    353       0,
    354       G_OPTION_ARG_NONE,
    355       &arg_enable_info,
    356       N_("Enable informational output"),
    357       NULL
    358     },
    359     {
    360       "display-properties",
    361       'p',
    362       0,
    363       G_OPTION_ARG_NONE,
    364       &arg_display_properties,
    365       N_("Display the properties dialog"),
    366       NULL
    367     },
    368     {
    369       "display-about",
    370       'a',
    371       0,
    372       G_OPTION_ARG_NONE,
    373       &arg_display_about,
    374       N_("Display the about dialog"),
    375       NULL
    376     },
    377     {
    378       "consider-new-mail-as-read",
    379       'r',
    380       0,
    381       G_OPTION_ARG_NONE,
    382       &arg_consider_new_mail_as_read,
    383       N_("Consider new mail as read"),
    384       NULL
    385     },
    386     {
    387       "update",
    388       'u',
    389       0,
    390       G_OPTION_ARG_NONE,
    391       &arg_update,
    392       N_("Update the mail status"),
    393       NULL
    394     },
    395     {
    396       "print-summary",
    397       's',
    398       0,
    399       G_OPTION_ARG_NONE,
    400       &arg_print_summary,
    401       N_("Print a XML mail summary"),
    402       NULL
    403     },
    404     {
    405       "unset-obsolete-configuration",
    406       '\0',
    407       0,
    408       G_OPTION_ARG_NONE,
    409       &arg_unset_obsolete_configuration,
    410       N_("Unset obsolete GConf configuration"),
    411       NULL
    412     },
    413     {
    414       "quit",
    415       'q',
    416       0,
    417       G_OPTION_ARG_NONE,
    418       &arg_quit,
    419       N_("Quit Mail Notification"),
    420       NULL
    421     },
    422     { NULL }
    423   };
    424   GOptionContext *option_context;
    425   DBusGConnection *bus;
    426   DBusGProxy *bus_proxy;
    427 
    428   g_log_set_fatal_mask(NULL, G_LOG_LEVEL_CRITICAL);
    429   g_log_set_handler(NULL, G_LOG_LEVEL_INFO, info_log_cb, NULL);
    430 
    431 #ifdef ENABLE_NLS
    432   bindtextdomain(GETTEXT_PACKAGE, GNOMELOCALEDIR);
    433   bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
    434   textdomain(GETTEXT_PACKAGE);
    435 #endif
    436 
    437   g_set_application_name(_("Mail Notification"));
    438 
    439   g_thread_init(NULL);
    440   if (! g_thread_supported())
    441     /*
    442      * We cannot use mn_fatal_error_dialog() because gtk_init() has
    443      * not been called yet.
    444      */
    445     g_critical(_("multi-threading is not available"));
    446   gdk_threads_init();
    447 
    448   GDK_THREADS_ENTER();
    449 
    450   option_context = g_option_context_new(NULL);
    451   g_option_context_add_main_entries(option_context, options, GETTEXT_PACKAGE);
    452 
    453   gnome_program_init(PACKAGE,
    454 		     VERSION,
    455 		     LIBGNOMEUI_MODULE,
    456 		     argc,
    457 		     argv,
    458 		     GNOME_PARAM_HUMAN_READABLE_NAME, _("Mail Notification"),
    459 		     GNOME_PROGRAM_STANDARD_PROPERTIES,
    460 		     GNOME_PARAM_GOPTION_CONTEXT, option_context,
    461 		     NULL);
    462 
    463   if (arg_version)
    464     {
    465       print_version();
    466       goto end;
    467     }
    468 
    469   if (arg_unset_obsolete_configuration)
    470     {
    471       mn_conf_unset_obsolete();
    472       goto end;
    473     }
    474 
    475   ensure_icon_path();
    476   gtk_window_set_default_icon_name("mail-notification");
    477 
    478   mn_stock_init();
    479 
    480   bus = connect_to_session_bus();
    481   bus_proxy = get_bus_proxy(bus);
    482 
    483   if (mn_server_start(bus, bus_proxy)) /* not already running */
    484     {
    485       if (arg_quit)
    486 	g_message(_("Mail Notification is not running"));
    487       else
    488 	{
    489 	  mn_mailbox_init_types();
    490 #if WITH_MBOX || WITH_MOZILLA || WITH_MH || WITH_MAILDIR || WITH_SYLPHEED
    491 	  mn_vfs_mailbox_init_types();
    492 #endif
    493 
    494 	  /* mn-client-session uses sockets, we don't want to die on SIGPIPE */
    495 	  signal(SIGPIPE, SIG_IGN);
    496 
    497 	  if (! gnome_vfs_init())
    498 	    mn_show_fatal_error_dialog(NULL, _("Unable to initialize the GnomeVFS library."));
    499 
    500 	  gnome_authentication_manager_init();
    501 
    502 	  /* must be called before init_gmime() */
    503 	  mn_conf_init();
    504 
    505 #if WITH_GMIME
    506 	  init_gmime();
    507 #endif
    508 
    509 	  if (! notify_init(_("Mail Notification")))
    510 	    mn_show_error_dialog(NULL,
    511 				 _("An initialization error has occurred in Mail Notification"),
    512 				 _("Unable to initialize the notification library. Message popups will not be displayed."));
    513 
    514 	  /*
    515 	   * Work around
    516 	   * http://bugzilla.gnome.org/show_bug.cgi?id=64764:
    517 	   * initialize the classes we will be using concurrently
    518 	   * before any thread is created.
    519 	   */
    520 	  init_classes();
    521 
    522 	  mn_shell_new(bus, bus_proxy);
    523 
    524 	  /* also display the properties dialog if there are no mailboxes */
    525 	  if (! mn_shell->mailboxes->list)
    526 	    arg_display_properties = TRUE;
    527 
    528 	  if (arg_display_properties)
    529 	    mn_shell_show_properties_dialog(mn_shell, 0);
    530 	  if (arg_display_about)
    531 	    mn_shell_show_about_dialog(mn_shell, 0);
    532 	  if (arg_consider_new_mail_as_read)
    533 	    report_option_ignored("--consider-new-mail-as-read");
    534 	  if (arg_update)
    535 	    report_option_ignored("--update");
    536 	  if (arg_print_summary)
    537 	    report_option_ignored("--print-summary");
    538 
    539 	  /* in case no window has been displayed */
    540 	  gdk_notify_startup_complete();
    541 
    542 	  gtk_main();
    543 	}
    544     }
    545   else				/* already running */
    546     {
    547       DBusGProxy *proxy;
    548       GError *err = NULL;
    549 
    550       proxy = dbus_g_proxy_new_for_name(bus,
    551 					MN_SERVER_SERVICE,
    552 					MN_SERVER_PATH,
    553 					MN_SERVER_INTERFACE);
    554 
    555       if (arg_quit)
    556 	{
    557 	  g_message(_("quitting Mail Notification"));
    558 	  CALL_SERVER(org_gnome_MailNotification_quit(proxy, &err));
    559 	}
    560       else
    561 	{
    562 	  /* also display the properties dialog if there are no mailboxes */
    563 	  if (! arg_display_properties)
    564 	    {
    565 	      gboolean has;
    566 	      CALL_SERVER(org_gnome_MailNotification_has_mailboxes(proxy, &has, &err));
    567 	      arg_display_properties = ! has;
    568 	    }
    569 
    570 	  if (arg_display_properties)
    571 	    CALL_SERVER(org_gnome_MailNotification_display_properties(proxy, &err));
    572 	  if (arg_display_about)
    573 	    CALL_SERVER(org_gnome_MailNotification_display_about(proxy, &err));
    574 	  if (arg_consider_new_mail_as_read)
    575 	    {
    576 	      g_message(_("considering new mail as read"));
    577 	      CALL_SERVER(org_gnome_MailNotification_consider_new_mail_as_read(proxy, &err));
    578 	    }
    579 	  if (arg_update)
    580 	    {
    581 	      g_message(_("updating the mail status"));
    582 	      CALL_SERVER(org_gnome_MailNotification_update(proxy, &err));
    583 	    }
    584 	  if (arg_print_summary)
    585 	    {
    586 	      char *summary;
    587 
    588 	      CALL_SERVER(org_gnome_MailNotification_get_summary(proxy, &summary, &err));
    589 	      g_print("%s", summary);
    590 	      g_free(summary);
    591 	    }
    592 
    593 	  if (! (arg_display_properties
    594 		 || arg_display_about
    595 		 || arg_consider_new_mail_as_read
    596 		 || arg_update
    597 		 || arg_print_summary))
    598 	    g_message(_("Mail Notification is already running"));
    599 	}
    600 
    601       /*
    602        * Do not unref the proxy, since it might break when the
    603        * DBusGProxy memory management issue is fixed
    604        * (https://bugs.freedesktop.org/show_bug.cgi?id=14030).
    605        */
    606 
    607       /* no window has been displayed by this process */
    608       gdk_notify_startup_complete();
    609     }
    610 
    611  end:
    612   GDK_THREADS_LEAVE();
    613 
    614   return 0;
    615 }