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 }