2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
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 2 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <glib/gi18n.h>
28 #include <gtk/gtkmain.h>
29 #include <gtk/gtkrc.h>
38 #include <sys/types.h>
43 #include <X11/SM/SMlib.h>
48 #ifdef HAVE_STARTUP_NOTIFICATION
49 # define SN_API_NOT_YET_FROZEN
50 # include <libsn/sn-launchee.h>
51 # include <gdk/gdkx.h>
56 #include "mainwindow.h"
57 #include "folderview.h"
58 #include "image_viewer.h"
59 #include "summaryview.h"
60 #include "prefs_common.h"
61 #include "prefs_account.h"
62 #include "prefs_actions.h"
63 #include "prefs_ext_prog.h"
64 #include "prefs_fonts.h"
65 #include "prefs_image_viewer.h"
66 #include "prefs_message.h"
67 #include "prefs_receive.h"
68 #include "prefs_msg_colors.h"
69 #include "prefs_quote.h"
70 #include "prefs_spelling.h"
71 #include "prefs_summaries.h"
72 #include "prefs_themes.h"
73 #include "prefs_other.h"
74 #include "prefs_send.h"
75 #include "prefs_wrapping.h"
76 #include "prefs_compose_writing.h"
77 #include "prefs_display_header.h"
82 #include "manage_window.h"
83 #include "alertpanel.h"
84 #include "statusbar.h"
85 #include "addressbook.h"
93 #include "prefs_toolbar.h"
100 #include "imap-thread.h"
102 #include "stock_pixmap.h"
104 #include "valgrind.h"
119 #ifdef HAVE_STARTUP_NOTIFICATION
120 static SnLauncheeContext *sn_context = NULL;
121 static SnDisplay *sn_display = NULL;
124 static gint lock_socket = -1;
125 static gint lock_socket_tag = 0;
126 static gchar *x_display = NULL;
129 ONLINE_MODE_DONT_CHANGE,
134 static struct RemoteCmd {
136 gboolean receive_all;
138 const gchar *compose_mailto;
139 GPtrArray *attach_files;
141 gboolean status_full;
142 GPtrArray *status_folders;
143 GPtrArray *status_full_folders;
150 const gchar *subscribe_uri;
154 static void parse_cmd_opt(int argc, char *argv[]);
156 static gint prohibit_duplicate_launch (void);
157 static gchar * get_crashfile_name (void);
158 static gint lock_socket_remove (void);
159 static void lock_socket_input_cb (gpointer data,
161 GdkInputCondition condition);
163 static void open_compose_new (const gchar *address,
164 GPtrArray *attach_files);
166 static void send_queue (void);
167 static void initial_processing (FolderItem *item, gpointer data);
168 static void quit_signal_handler (int sig);
169 static void install_basic_sighandlers (void);
170 static void exit_claws (MainWindow *mainwin);
172 #define MAKE_DIR_IF_NOT_EXIST(dir) \
174 if (!is_dir_exist(dir)) { \
175 if (is_file_exist(dir)) { \
177 (_("File '%s' already exists.\n" \
178 "Can't create folder."), \
182 if (make_dir(dir) < 0) \
187 static MainWindow *static_mainwindow;
188 static gboolean emergency_exit = FALSE;
190 #ifdef HAVE_STARTUP_NOTIFICATION
191 static void sn_error_trap_push(SnDisplay *display, Display *xdisplay)
193 gdk_error_trap_push();
196 static void sn_error_trap_pop(SnDisplay *display, Display *xdisplay)
198 gdk_error_trap_pop();
201 static void startup_notification_complete(gboolean with_window)
204 GtkWidget *hack = NULL;
207 /* this is needed to make the startup notification leave,
208 * if we have been launched from a menu.
209 * We have to display a window, so let it be very little */
210 hack = gtk_window_new(GTK_WINDOW_POPUP);
211 gtk_widget_set_uposition(hack, 0, 0);
212 gtk_widget_set_size_request(hack, 1, 1);
213 gtk_widget_show(hack);
216 xdisplay = GDK_DISPLAY();
217 sn_display = sn_display_new(xdisplay,
220 sn_context = sn_launchee_context_new_from_environment(sn_display,
221 DefaultScreen(xdisplay));
223 if (sn_context != NULL) {
224 sn_launchee_context_complete(sn_context);
225 sn_launchee_context_unref(sn_context);
226 sn_display_unref(sn_display);
229 gtk_widget_destroy(hack);
232 #endif /* HAVE_STARTUP_NOTIFICATION */
234 static void claws_gtk_idle(void)
236 while(gtk_events_pending()) {
237 gtk_main_iteration();
242 static gboolean defer_check_all(void *data)
244 gboolean autochk = GPOINTER_TO_INT(data);
246 inc_all_account_mail(static_mainwindow, autochk,
247 prefs_common.newmail_notify_manu);
252 static gboolean defer_check(void *data)
254 inc_mail(static_mainwindow, prefs_common.newmail_notify_manu);
259 static gboolean defer_jump(void *data)
261 mainwindow_jump_to(data);
265 static void chk_update_val(GtkWidget *widget, gpointer data)
267 gboolean *val = (gboolean *)data;
268 *val = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
271 static gboolean migrate_old_config(const gchar *old_cfg_dir, const gchar *new_cfg_dir, const gchar *oldversion)
273 gchar *message = g_strdup_printf(_("Configuration for %s (or previous) found.\n"
274 "Do you want to migrate this configuration?"), oldversion);
276 GtkWidget *window = NULL;
277 GtkWidget *keep_backup_chk;
278 GtkTooltips *tips = gtk_tooltips_new();
279 gboolean backup = TRUE;
281 keep_backup_chk = gtk_check_button_new_with_label (_("Keep old configuration"));
282 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(keep_backup_chk), TRUE);
283 gtk_tooltips_set_tip(GTK_TOOLTIPS(tips), keep_backup_chk,
284 _("Keeping a backup will allow you to go back to an "
285 "older version, but may take a while if you have "
286 "cached IMAP or News data, and will take some extra "
287 "room on your disk."),
290 g_signal_connect(G_OBJECT(keep_backup_chk), "toggled",
291 G_CALLBACK(chk_update_val), &backup);
293 if (alertpanel_full(_("Migration of configuration"), message,
294 GTK_STOCK_NO, "+" GTK_STOCK_YES, NULL, FALSE,
295 keep_backup_chk, ALERT_QUESTION, G_ALERTDEFAULT) != G_ALERTALTERNATE) {
299 /* we can either do a fast migration requiring not any extra disk
300 * space, or a slow one that copies the old configuration and leaves
304 window = label_window_create(_("Copying configuration... This may take a while..."));
307 r = copy_dir(old_cfg_dir, new_cfg_dir);
308 gtk_widget_destroy(window);
310 /* if copy failed, we'll remove the partially copied
313 alertpanel_error(_("Migration failed!"));
314 remove_dir_recursive(new_cfg_dir);
317 /* fast mode failed, but we don't want backup */
318 remove_dir_recursive(old_cfg_dir);
322 window = label_window_create(_("Migrating configuration..."));
325 r = g_rename(old_cfg_dir, new_cfg_dir);
326 gtk_widget_destroy(window);
328 /* if g_rename failed, we'll try to copy */
330 FILE_OP_ERROR(new_cfg_dir, "g_rename failed, trying copy\n");
337 static void migrate_common_rc(const gchar *old_rc, const gchar *new_rc)
340 gchar *plugin_path, *old_plugin_path, *new_plugin_path;
342 oldfp = g_fopen(old_rc, "r");
345 newfp = g_fopen(new_rc, "w");
351 plugin_path = g_strdup(get_plugin_dir());
352 new_plugin_path = g_strdup(plugin_path);
354 if (strstr(plugin_path, "/claws-mail/")) {
355 gchar *end = g_strdup(strstr(plugin_path, "/claws-mail/")+strlen("/claws-mail/"));
356 *(strstr(plugin_path, "/claws-mail/")) = '\0';
357 old_plugin_path = g_strconcat(plugin_path, "/sylpheed-claws/", end, NULL);
360 old_plugin_path = g_strdup(new_plugin_path);
362 debug_print("replacing %s with %s\n", old_plugin_path, new_plugin_path);
363 while (fgets(buf, sizeof(buf), oldfp)) {
364 if (strncmp(buf, old_plugin_path, strlen(old_plugin_path))) {
367 debug_print("->replacing %s", buf);
368 debug_print(" with %s%s", new_plugin_path, buf+strlen(old_plugin_path));
369 fputs(new_plugin_path, newfp);
370 fputs(buf+strlen(old_plugin_path), newfp);
374 g_free(new_plugin_path);
375 g_free(old_plugin_path);
382 sc_client_set_value (MainWindow *mainwin,
393 prop.num_vals = num_vals;
397 if (mainwin->smc_conn)
398 SmcSetProperties ((SmcConn) mainwin->smc_conn, 1, proplist);
401 static void sc_die_callback (SmcConn smc_conn, SmPointer client_data)
406 static void sc_save_complete_callback(SmcConn smc_conn, SmPointer client_data)
410 static void sc_shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data)
412 MainWindow *mainwin = (MainWindow *)client_data;
413 if (mainwin->smc_conn)
414 SmcSaveYourselfDone ((SmcConn) mainwin->smc_conn, TRUE);
417 static void sc_save_yourself_callback (SmcConn smc_conn,
418 SmPointer client_data,
424 MainWindow *mainwin = (MainWindow *)client_data;
425 if (mainwin->smc_conn)
426 SmcSaveYourselfDone ((SmcConn) mainwin->smc_conn, TRUE);
429 static IceIOErrorHandler sc_ice_installed_handler;
431 static void sc_ice_io_error_handler (IceConn connection)
433 if (sc_ice_installed_handler)
434 (*sc_ice_installed_handler) (connection);
436 static gboolean sc_process_ice_messages (GIOChannel *source,
437 GIOCondition condition,
440 IceConn connection = (IceConn) data;
441 IceProcessMessagesStatus status;
443 status = IceProcessMessages (connection, NULL, NULL);
445 if (status == IceProcessMessagesIOError) {
446 IcePointer context = IceGetConnectionContext (connection);
448 if (context && GTK_IS_OBJECT (context)) {
449 guint disconnect_id = g_signal_lookup ("disconnect", G_OBJECT_TYPE (context));
451 if (disconnect_id > 0)
452 g_signal_emit (context, disconnect_id, 0);
454 IceSetShutdownNegotiation (connection, False);
455 IceCloseConnection (connection);
462 static void new_ice_connection (IceConn connection, IcePointer client_data, Bool opening,
463 IcePointer *watch_data)
469 /* Make sure we don't pass on these file descriptors to any
471 fcntl(IceConnectionNumber(connection),F_SETFD,
472 fcntl(IceConnectionNumber(connection),F_GETFD,0) | FD_CLOEXEC);
474 channel = g_io_channel_unix_new (IceConnectionNumber (connection));
475 input_id = g_io_add_watch (channel,
476 G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
477 sc_process_ice_messages,
479 g_io_channel_unref (channel);
481 *watch_data = (IcePointer) GUINT_TO_POINTER (input_id);
483 input_id = GPOINTER_TO_UINT ((gpointer) *watch_data);
484 g_source_remove (input_id);
488 static void sc_session_manager_connect(MainWindow *mainwin)
490 static gboolean connected = FALSE;
491 SmcCallbacks callbacks;
493 IceIOErrorHandler default_handler;
500 sc_ice_installed_handler = IceSetIOErrorHandler (NULL);
501 default_handler = IceSetIOErrorHandler (sc_ice_io_error_handler);
503 if (sc_ice_installed_handler == default_handler)
504 sc_ice_installed_handler = NULL;
506 IceAddConnectionWatch (new_ice_connection, NULL);
509 callbacks.save_yourself.callback = sc_save_yourself_callback;
510 callbacks.die.callback = sc_die_callback;
511 callbacks.save_complete.callback = sc_save_complete_callback;
512 callbacks.shutdown_cancelled.callback = sc_shutdown_cancelled_callback;
514 callbacks.save_yourself.client_data =
515 callbacks.die.client_data =
516 callbacks.save_complete.client_data =
517 callbacks.shutdown_cancelled.client_data = (SmPointer) mainwin;
518 if (g_getenv ("SESSION_MANAGER")) {
519 gchar error_string_ret[256] = "";
521 mainwin->smc_conn = (gpointer)
522 SmcOpenConnection (NULL, mainwin,
523 SmProtoMajor, SmProtoMinor,
524 SmcSaveYourselfProcMask | SmcDieProcMask |
525 SmcSaveCompleteProcMask |
526 SmcShutdownCancelledProcMask,
529 256, error_string_ret);
531 if (error_string_ret[0] || mainwin->smc_conn == NULL)
532 g_warning ("While connecting to session manager:\n%s.",
536 vals = g_new (SmPropValue, 1);
537 vals[0].length = strlen(argv0);
538 vals[0].value = argv0;
539 sc_client_set_value (mainwin, SmCloneCommand, SmLISTofARRAY8, 1, vals);
540 sc_client_set_value (mainwin, SmRestartCommand, SmLISTofARRAY8, 1, vals);
541 sc_client_set_value (mainwin, SmProgram, SmARRAY8, 1, vals);
543 vals[0].length = strlen(g_get_user_name()?g_get_user_name():"");
544 vals[0].value = g_strdup(g_get_user_name()?g_get_user_name():"");
545 sc_client_set_value (mainwin, SmUserID, SmARRAY8, 1, vals);
551 static gboolean sc_exiting = FALSE;
552 static gboolean sc_starting = FALSE;
553 static gboolean show_at_startup = TRUE;
555 void main_set_show_at_startup(gboolean show)
557 show_at_startup = show;
560 int main(int argc, char *argv[])
564 FolderView *folderview;
566 gboolean crash_file_present = FALSE;
567 gint num_folder_class = 0;
568 gboolean asked_for_migration = FALSE;
570 START_TIMING("startup");
574 if (!claws_init(&argc, &argv)) {
578 prefs_prepare_cache();
579 prog_version = PROG_VERSION;
580 argv0 = g_strdup(argv[0]);
582 parse_cmd_opt(argc, argv);
587 gtk_init(&argc, &argv);
588 crash_main(cmd.crash_params);
591 crash_install_handlers();
593 install_basic_sighandlers();
596 /* check and create unix domain socket for remote operation */
598 lock_socket = prohibit_duplicate_launch();
599 if (lock_socket < 0) {
600 #ifdef HAVE_STARTUP_NOTIFICATION
601 if(gtk_init_check(&argc, &argv))
602 startup_notification_complete(TRUE);
607 if (cmd.status || cmd.status_full) {
608 puts("0 Claws Mail not running.");
609 lock_socket_remove();
616 if (!g_thread_supported())
620 gtk_init(&argc, &argv);
623 gtk_widget_set_default_colormap(gdk_rgb_get_colormap());
624 gtk_widget_set_default_visual(gdk_rgb_get_visual());
626 if (!g_thread_supported()) {
627 g_error(_("g_thread is not supported by glib.\n"));
630 /* check that we're not on a too recent/old gtk+ */
631 #if GTK_CHECK_VERSION(2, 9, 0)
632 if (gtk_check_version(2, 9, 0) != NULL) {
633 alertpanel_error(_("Claws Mail has been compiled with "
634 "a more recent GTK+ library than is "
635 "currently available. This will cause "
636 "crashes. You need to upgrade GTK+ or "
637 "recompile Claws Mail."));
641 if (gtk_check_version(2, 9, 0) == NULL) {
642 alertpanel_error(_("Claws Mail has been compiled with "
643 "an older GTK+ library than is "
644 "currently available. This will cause "
645 "crashes. You need to recompile "
650 /* parse gtkrc files */
651 userrc = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".gtkrc-2.0",
653 gtk_rc_parse(userrc);
655 userrc = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S, ".gtk",
656 G_DIR_SEPARATOR_S, "gtkrc-2.0", NULL);
657 gtk_rc_parse(userrc);
660 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), 1);
662 /* no config dir exists. See if we can migrate an old config. */
663 if (!is_dir_exist(RC_DIR)) {
664 prefs_destroy_cache();
667 /* if one of the old dirs exist, we'll ask if the user
668 * want to migrates, and r will be TRUE if he said yes
669 * and migration succeeded, and FALSE otherwise.
671 if (is_dir_exist(OLD_GTK2_RC_DIR)) {
672 r = migrate_old_config(OLD_GTK2_RC_DIR, RC_DIR, "Sylpheed-Claws 2.6.0");
673 asked_for_migration = TRUE;
674 } else if (is_dir_exist(OLDER_GTK2_RC_DIR)) {
675 r = migrate_old_config(OLDER_GTK2_RC_DIR, RC_DIR, "Sylpheed-Claws 1.9.15");
676 asked_for_migration = TRUE;
677 } else if (is_dir_exist(OLD_GTK1_RC_DIR)) {
678 r = migrate_old_config(OLD_GTK1_RC_DIR, RC_DIR, "Sylpheed-Claws 1.0.5");
679 asked_for_migration = TRUE;
682 /* If migration failed or the user didn't want to do it,
683 * we create a new one (and we'll hit wizard later).
685 if (r == FALSE && !is_dir_exist(RC_DIR) && make_dir(RC_DIR) < 0)
690 if (!is_file_exist(RC_DIR G_DIR_SEPARATOR_S COMMON_RC) &&
691 is_file_exist(RC_DIR G_DIR_SEPARATOR_S OLD_COMMON_RC)) {
692 /* post 2.6 name change */
693 migrate_common_rc(RC_DIR G_DIR_SEPARATOR_S OLD_COMMON_RC,
694 RC_DIR G_DIR_SEPARATOR_S COMMON_RC);
698 plugin_load_all("Common");
700 userrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, "gtkrc-2.0", NULL);
701 gtk_rc_parse(userrc);
704 userrc = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, MENU_RC, NULL);
705 gtk_accel_map_load (userrc);
708 CHDIR_RETURN_VAL_IF_FAIL(get_rc_dir(), 1);
710 MAKE_DIR_IF_NOT_EXIST(get_mail_base_dir());
711 MAKE_DIR_IF_NOT_EXIST(get_imap_cache_dir());
712 MAKE_DIR_IF_NOT_EXIST(get_news_cache_dir());
713 MAKE_DIR_IF_NOT_EXIST(get_mime_tmp_dir());
714 MAKE_DIR_IF_NOT_EXIST(get_tmp_dir());
715 MAKE_DIR_IF_NOT_EXIST(UIDL_DIR);
717 crash_file_present = is_file_exist(get_crashfile_name());
718 /* remove temporary files */
719 remove_all_files(get_tmp_dir());
720 remove_all_files(get_mime_tmp_dir());
722 if (is_file_exist("claws.log")) {
723 if (rename_force("claws.log", "claws.log.bak") < 0)
724 FILE_OP_ERROR("claws.log", "rename");
726 set_log_file("claws.log");
728 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), 1);
730 folder_system_init();
731 prefs_common_read_config();
735 prefs_ext_prog_init();
736 prefs_wrapping_init();
737 prefs_compose_writing_init();
738 prefs_msg_colors_init();
740 prefs_image_viewer_init();
742 prefs_summaries_init();
743 prefs_message_init();
745 prefs_receive_init();
748 gtkaspell_checkers_init();
749 prefs_spelling_init();
752 sock_set_io_timeout(prefs_common.io_timeout_secs);
754 imap_main_set_timeout(prefs_common.io_timeout_secs);
756 prefs_actions_read_config();
757 prefs_display_header_read_config();
758 /* prefs_filtering_read_config(); */
759 addressbook_read_file();
760 renderer_read_config();
763 stock_pixbuf_gdk(NULL, STOCK_PIXMAP_CLAWS_MAIL_ICON, &icon);
764 gtk_window_set_default_icon(icon);
766 folderview_initialize();
772 mainwin = main_window_create();
774 manage_window_focus_in(mainwin->window, NULL, NULL);
775 folderview = mainwin->folderview;
777 gtk_clist_freeze(GTK_CLIST(mainwin->folderview->ctree));
778 folder_item_update_freeze();
780 /* register the callback of unix domain socket input */
782 lock_socket_tag = gdk_input_add(lock_socket,
783 GDK_INPUT_READ | GDK_INPUT_EXCEPTION,
784 lock_socket_input_cb,
788 prefs_account_init();
789 account_read_config_all();
791 /* If we can't read a folder list or don't have accounts,
792 * it means the configuration's not done. Either this is
793 * a brand new install, either a failed/refused migration.
794 * So we'll start the wizard.
796 if (folder_read_list() < 0) {
797 prefs_destroy_cache();
799 /* if run_wizard returns FALSE it's because it's
800 * been cancelled. We can't do much but exit.
801 * however, if the user was asked for a migration,
802 * we remove the newly created directory so that
803 * he's asked again for migration on next launch.*/
804 if (!run_wizard(mainwin, TRUE)) {
805 if (asked_for_migration)
806 remove_dir_recursive(RC_DIR);
809 main_window_reflect_prefs_all_now();
813 if (!account_get_list()) {
814 prefs_destroy_cache();
815 if (!run_wizard(mainwin, FALSE)) {
816 if (asked_for_migration)
817 remove_dir_recursive(RC_DIR);
820 account_read_config_all();
821 if(!account_get_list()) {
828 toolbar_main_set_sensitive(mainwin);
829 main_window_set_menu_sensitive(mainwin);
831 /* if crashed, show window early so that the user
832 * sees what's happening */
833 if (!cmd.crash && crash_file_present)
834 main_window_popup(mainwin);
837 imap_main_init(prefs_common.skip_ssl_cert_check);
839 account_set_missing_folder();
840 folder_set_missing_folders();
841 folderview_set(folderview);
843 prefs_matcher_read_config();
845 /* make one all-folder processing before using claws */
846 main_window_cursor_wait(mainwin);
847 folder_func_to_all_folders(initial_processing, (gpointer *)mainwin);
849 /* if claws crashed, rebuild caches */
850 if (!cmd.crash && crash_file_present) {
852 debug_print("Claws Mail crashed, checking for new messages in local folders\n");
853 folder_item_update_thaw();
854 folderview_check_new(NULL);
855 folder_clean_cache_memory_force();
856 folder_item_update_freeze();
858 /* make the crash-indicator file */
859 str_write_to_file("foo", get_crashfile_name());
861 inc_autocheck_timer_init(mainwin);
863 /* ignore SIGPIPE signal for preventing sudden death of program */
865 signal(SIGPIPE, SIG_IGN);
867 if (cmd.online_mode == ONLINE_MODE_OFFLINE) {
868 main_window_toggle_work_offline(mainwin, TRUE, FALSE);
870 if (cmd.online_mode == ONLINE_MODE_ONLINE) {
871 main_window_toggle_work_offline(mainwin, FALSE, FALSE);
874 if (cmd.status_folders) {
875 g_ptr_array_free(cmd.status_folders, TRUE);
876 cmd.status_folders = NULL;
878 if (cmd.status_full_folders) {
879 g_ptr_array_free(cmd.status_full_folders, TRUE);
880 cmd.status_full_folders = NULL;
883 claws_register_idle_function(claws_gtk_idle);
885 prefs_toolbar_init();
887 num_folder_class = g_list_length(folder_get_list());
889 plugin_load_all("GTK2");
891 if (g_list_length(folder_get_list()) != num_folder_class) {
892 debug_print("new folders loaded, reloading processing rules\n");
893 prefs_matcher_read_config();
896 if (plugin_get_unloaded_list() != NULL) {
897 main_window_cursor_normal(mainwin);
898 alertpanel_warning(_("Some plugin(s) failed to load. "
899 "Check the Plugins configuration "
900 "for more information."));
901 main_window_cursor_wait(mainwin);
904 plugin_load_standard_plugins ();
906 /* if not crashed, show window now */
907 if (!(!cmd.crash && crash_file_present)) {
908 /* apart if something told not to show */
910 main_window_popup(mainwin);
913 if (!folder_have_mailbox()) {
914 prefs_destroy_cache();
915 main_window_cursor_normal(mainwin);
916 if (folder_get_list() != NULL) {
917 alertpanel_error(_("Claws Mail has detected a configured "
918 "mailbox, but it is incomplete. It is "
919 "possibly due to a failing IMAP account. Use "
920 "\"Rebuild folder tree\" on the mailbox parent "
921 "folder's context menu to try to fix it."));
923 alertpanel_error(_("Claws Mail has detected a configured "
924 "mailbox, but could not load it. It is "
925 "probably provided by an out-of-date "
926 "external plugin. Please reinstall the "
927 "plugin and try again."));
933 static_mainwindow = mainwin;
935 #ifdef HAVE_STARTUP_NOTIFICATION
936 startup_notification_complete(FALSE);
939 sc_session_manager_connect(mainwin);
941 folder_item_update_thaw();
942 gtk_clist_thaw(GTK_CLIST(mainwin->folderview->ctree));
943 main_window_cursor_normal(mainwin);
945 if (cmd.receive_all) {
946 g_timeout_add(1000, defer_check_all, GINT_TO_POINTER(FALSE));
947 } else if (prefs_common.chk_on_startup) {
948 g_timeout_add(1000, defer_check_all, GINT_TO_POINTER(TRUE));
949 } else if (cmd.receive) {
950 g_timeout_add(1000, defer_check, NULL);
952 gtk_widget_grab_focus(folderview->ctree);
956 open_compose_new(cmd.compose_mailto, cmd.attach_files);
958 if (cmd.attach_files) {
959 ptr_array_free_strings(cmd.attach_files);
960 g_ptr_array_free(cmd.attach_files, TRUE);
961 cmd.attach_files = NULL;
964 folder_subscribe(cmd.subscribe_uri);
972 g_timeout_add(500, defer_jump, (gpointer)cmd.target);
975 prefs_destroy_cache();
977 compose_reopen_exit_drafts();
989 static void save_all_caches(FolderItem *item, gpointer data)
996 folder_item_close(item);
998 folder_item_free_cache(item, TRUE);
1001 static void exit_claws(MainWindow *mainwin)
1007 debug_print("shutting down\n");
1008 inc_autocheck_timer_remove();
1010 if (prefs_common.clean_on_exit && !emergency_exit) {
1011 main_window_empty_trash(mainwin, prefs_common.ask_on_clean);
1014 /* save prefs for opened folder */
1015 if(mainwin->folderview->opened) {
1018 item = gtk_ctree_node_get_row_data(GTK_CTREE(mainwin->folderview->ctree), mainwin->folderview->opened);
1019 summary_save_prefs_to_folderitem(mainwin->folderview->summaryview, item);
1022 /* save all state before exiting */
1023 folder_func_to_all_folders(save_all_caches, NULL);
1024 folder_write_list();
1026 main_window_get_size(mainwin);
1027 main_window_get_position(mainwin);
1029 prefs_common_write_config();
1030 account_write_config_all();
1031 addressbook_export_to_file();
1033 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, MENU_RC, NULL);
1034 gtk_accel_map_save(filename);
1037 /* delete temporary files */
1038 remove_all_files(get_tmp_dir());
1039 remove_all_files(get_mime_tmp_dir());
1043 #ifdef HAVE_LIBETPAN
1046 /* delete crashfile */
1048 g_unlink(get_crashfile_name());
1050 lock_socket_remove();
1053 if (mainwin->smc_conn)
1054 SmcCloseConnection ((SmcConn)mainwin->smc_conn, 0, NULL);
1055 mainwin->smc_conn = NULL;
1058 main_window_destroy_all();
1060 plugin_unload_all("GTK2");
1062 prefs_toolbar_done();
1064 addressbook_destroy();
1066 prefs_themes_done();
1068 prefs_ext_prog_done();
1069 prefs_wrapping_done();
1070 prefs_compose_writing_done();
1071 prefs_msg_colors_done();
1072 prefs_image_viewer_done();
1073 image_viewer_done();
1075 prefs_summaries_done();
1076 prefs_message_done();
1078 prefs_receive_done();
1081 prefs_spelling_done();
1082 gtkaspell_checkers_quit();
1084 plugin_unload_all("Common");
1088 static void parse_cmd_opt(int argc, char *argv[])
1092 for (i = 1; i < argc; i++) {
1093 if (!strncmp(argv[i], "--receive-all", 13)) {
1094 cmd.receive_all = TRUE;
1095 } else if (!strncmp(argv[i], "--receive", 9)) {
1097 } else if (!strncmp(argv[i], "--compose", 9)) {
1098 const gchar *p = argv[i + 1];
1101 cmd.compose_mailto = NULL;
1102 if (p && *p != '\0' && *p != '-') {
1103 if (!strncmp(p, "mailto:", 7)) {
1104 cmd.compose_mailto = p + 7;
1106 cmd.compose_mailto = p;
1110 } else if (!strncmp(argv[i], "--subscribe", 11)) {
1111 const gchar *p = argv[i + 1];
1112 if (p && *p != '\0' && *p != '-') {
1113 cmd.subscribe = TRUE;
1114 cmd.subscribe_uri = p;
1116 } else if (!strncmp(argv[i], "--attach", 8)) {
1117 const gchar *p = argv[i + 1];
1120 while (p && *p != '\0' && *p != '-') {
1121 if (!cmd.attach_files) {
1122 cmd.attach_files = g_ptr_array_new();
1124 if ((file = g_filename_from_uri(p, NULL, NULL)) != NULL) {
1125 if (!is_file_exist(file)) {
1130 if (file == NULL && *p != G_DIR_SEPARATOR) {
1131 file = g_strconcat(claws_get_startup_dir(),
1134 } else if (file == NULL) {
1137 g_ptr_array_add(cmd.attach_files, file);
1141 } else if (!strncmp(argv[i], "--send", 6)) {
1143 } else if (!strncmp(argv[i], "--version", 9) ||
1144 !strncmp(argv[i], "-v", 2)) {
1145 puts("Claws Mail version " VERSION);
1147 } else if (!strncmp(argv[i], "--status-full", 13)) {
1148 const gchar *p = argv[i + 1];
1150 cmd.status_full = TRUE;
1151 while (p && *p != '\0' && *p != '-') {
1152 if (!cmd.status_full_folders) {
1153 cmd.status_full_folders =
1156 g_ptr_array_add(cmd.status_full_folders,
1161 } else if (!strncmp(argv[i], "--status", 8)) {
1162 const gchar *p = argv[i + 1];
1165 while (p && *p != '\0' && *p != '-') {
1166 if (!cmd.status_folders)
1167 cmd.status_folders = g_ptr_array_new();
1168 g_ptr_array_add(cmd.status_folders,
1173 } else if (!strncmp(argv[i], "--online", 8)) {
1174 cmd.online_mode = ONLINE_MODE_ONLINE;
1175 } else if (!strncmp(argv[i], "--offline", 9)) {
1176 cmd.online_mode = ONLINE_MODE_OFFLINE;
1177 } else if (!strncmp(argv[i], "--help", 6) ||
1178 !strncmp(argv[i], "-h", 2)) {
1179 gchar *base = g_path_get_basename(argv[0]);
1180 g_print(_("Usage: %s [OPTION]...\n"), base);
1182 g_print("%s\n", _(" --compose [address] open composition window"));
1183 g_print("%s\n", _(" --subscribe [uri] subscribe to the given URI if possible"));
1184 g_print("%s\n", _(" --attach file1 [file2]...\n"
1185 " open composition window with specified files\n"
1187 g_print("%s\n", _(" --receive receive new messages"));
1188 g_print("%s\n", _(" --receive-all receive new messages of all accounts"));
1189 g_print("%s\n", _(" --send send all queued messages"));
1190 g_print("%s\n", _(" --status [folder]... show the total number of messages"));
1191 g_print("%s\n", _(" --status-full [folder]...\n"
1192 " show the status of each folder"));
1193 g_print("%s\n", _(" --select folder[/msg] jumps to the specified folder/message\n"
1194 " folder is a folder id like 'folder/sub_folder'"));
1195 g_print("%s\n", _(" --online switch to online mode"));
1196 g_print("%s\n", _(" --offline switch to offline mode"));
1197 g_print("%s\n", _(" --exit --quit -q exit Claws Mail"));
1198 g_print("%s\n", _(" --debug debug mode"));
1199 g_print("%s\n", _(" --help -h display this help and exit"));
1200 g_print("%s\n", _(" --version -v output version information and exit"));
1201 g_print("%s\n", _(" --config-dir output configuration directory"));
1205 } else if (!strncmp(argv[i], "--crash", 7)) {
1207 cmd.crash_params = g_strdup(argv[i + 1]);
1209 } else if (!strncmp(argv[i], "--config-dir", sizeof "--config-dir" - 1)) {
1212 } else if (!strncmp(argv[i], "--exit", 6) ||
1213 !strncmp(argv[i], "--quit", 6) ||
1214 !strncmp(argv[i], "-q", 2)) {
1216 } else if (!strncmp(argv[i], "--select", 8) && i+1 < argc) {
1217 cmd.target = argv[i+1];
1218 } else if (i == 1 && argc == 2) {
1219 /* only one parameter. Do something intelligent about it */
1220 if (strstr(argv[i], "@") && !strstr(argv[i], "://")) {
1221 const gchar *p = argv[i];
1224 cmd.compose_mailto = NULL;
1225 if (p && *p != '\0' && *p != '-') {
1226 if (!strncmp(p, "mailto:", 7)) {
1227 cmd.compose_mailto = p + 7;
1229 cmd.compose_mailto = p;
1232 } else if (strstr(argv[i], "://")) {
1233 const gchar *p = argv[i];
1234 if (p && *p != '\0' && *p != '-') {
1235 cmd.subscribe = TRUE;
1236 cmd.subscribe_uri = p;
1238 } else if (!strcmp(argv[i], "--sync")) {
1241 g_print(_("Unknown option\n"));
1247 if (cmd.attach_files && cmd.compose == FALSE) {
1249 cmd.compose_mailto = NULL;
1253 static void initial_processing(FolderItem *item, gpointer data)
1255 MainWindow *mainwin = (MainWindow *)data;
1258 g_return_if_fail(item);
1259 buf = g_strdup_printf(_("Processing (%s)..."),
1262 : _("top level folder"));
1266 if (item->prefs->enable_processing) {
1267 folder_item_apply_processing(item);
1270 STATUSBAR_POP(mainwin);
1273 static void draft_all_messages(void)
1275 GList *compose_list = NULL;
1277 compose_clear_exit_drafts();
1278 while ((compose_list = compose_get_compose_list()) != NULL) {
1279 Compose *c = (Compose*)compose_list->data;
1280 compose_draft(c, COMPOSE_DRAFT_FOR_EXIT);
1283 gboolean clean_quit(gpointer data)
1285 static gboolean firstrun = TRUE;
1292 /*!< Good idea to have the main window stored in a
1293 * static variable so we can check that variable
1294 * to see if we're really allowed to do things
1295 * that actually the spawner is supposed to
1296 * do (like: sending mail, composing messages).
1297 * Because, really, if we're the spawnee, and
1298 * we touch GTK stuff, we're hosed. See the
1301 /* FIXME: Use something else to signal that we're
1302 * in the original spawner, and not in a spawned
1304 if (!static_mainwindow) {
1308 draft_all_messages();
1309 emergency_exit = TRUE;
1310 exit_claws(static_mainwindow);
1316 void app_will_exit(GtkWidget *widget, gpointer data)
1318 MainWindow *mainwin = data;
1320 if (sc_exiting == TRUE) {
1321 debug_print("exit pending\n");
1325 debug_print("exiting\n");
1326 if (compose_get_compose_list()) {
1327 draft_all_messages();
1330 if (prefs_common.warn_queued_on_exit && procmsg_have_queued_mails_fast()) {
1331 if (alertpanel(_("Queued messages"),
1332 _("Some unsent messages are queued. Exit now?"),
1333 GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL)
1334 != G_ALERTALTERNATE) {
1335 main_window_popup(mainwin);
1339 manage_window_focus_in(mainwin->window, NULL, NULL);
1343 #ifdef HAVE_VALGRIND
1344 if (RUNNING_ON_VALGRIND) {
1345 summary_clear_list(mainwin->summaryview);
1348 if (folderview_get_selected_item(mainwin->folderview))
1349 folder_item_close(folderview_get_selected_item(mainwin->folderview));
1353 gboolean claws_is_exiting(void)
1358 gboolean claws_is_starting(void)
1364 * CLAWS: want this public so crash dialog can delete the
1367 gchar *claws_get_socket_name(void)
1369 static gchar *filename = NULL;
1371 if (filename == NULL) {
1372 filename = g_strdup_printf("%s%cclaws-mail-%d",
1373 g_get_tmp_dir(), G_DIR_SEPARATOR,
1384 static gchar *get_crashfile_name(void)
1386 static gchar *filename = NULL;
1388 if (filename == NULL) {
1389 filename = g_strdup_printf("%s%cclaws-crashed",
1390 get_tmp_dir(), G_DIR_SEPARATOR);
1396 static gint prohibit_duplicate_launch(void)
1401 path = claws_get_socket_name();
1402 uxsock = fd_connect_unix(path);
1404 if (x_display == NULL)
1405 x_display = g_strdup(g_getenv("DISPLAY"));
1409 return fd_open_unix(path);
1412 /* remote command mode */
1414 debug_print("another Claws Mail instance is already running.\n");
1416 if (cmd.receive_all) {
1417 fd_write_all(uxsock, "receive_all\n", 12);
1418 } else if (cmd.receive) {
1419 fd_write_all(uxsock, "receive\n", 8);
1420 } else if (cmd.compose && cmd.attach_files) {
1421 gchar *str, *compose_str;
1424 if (cmd.compose_mailto) {
1425 compose_str = g_strdup_printf("compose_attach %s\n",
1426 cmd.compose_mailto);
1428 compose_str = g_strdup("compose_attach\n");
1431 fd_write_all(uxsock, compose_str, strlen(compose_str));
1432 g_free(compose_str);
1434 for (i = 0; i < cmd.attach_files->len; i++) {
1435 str = g_ptr_array_index(cmd.attach_files, i);
1436 fd_write_all(uxsock, str, strlen(str));
1437 fd_write_all(uxsock, "\n", 1);
1440 fd_write_all(uxsock, ".\n", 2);
1441 } else if (cmd.compose) {
1444 if (cmd.compose_mailto) {
1445 compose_str = g_strdup_printf
1446 ("compose %s\n", cmd.compose_mailto);
1448 compose_str = g_strdup("compose\n");
1451 fd_write_all(uxsock, compose_str, strlen(compose_str));
1452 g_free(compose_str);
1453 } else if (cmd.subscribe) {
1454 gchar *str = g_strdup_printf("subscribe %s\n", cmd.subscribe_uri);
1455 fd_write_all(uxsock, str, strlen(str));
1457 } else if (cmd.send) {
1458 fd_write_all(uxsock, "send\n", 5);
1459 } else if (cmd.online_mode == ONLINE_MODE_ONLINE) {
1460 fd_write(uxsock, "online\n", 6);
1461 } else if (cmd.online_mode == ONLINE_MODE_OFFLINE) {
1462 fd_write(uxsock, "offline\n", 7);
1463 } else if (cmd.status || cmd.status_full) {
1464 gchar buf[BUFFSIZE];
1466 const gchar *command;
1470 command = cmd.status_full ? "status-full\n" : "status\n";
1471 folders = cmd.status_full ? cmd.status_full_folders :
1474 fd_write_all(uxsock, command, strlen(command));
1475 for (i = 0; folders && i < folders->len; ++i) {
1476 folder = g_ptr_array_index(folders, i);
1477 fd_write_all(uxsock, folder, strlen(folder));
1478 fd_write_all(uxsock, "\n", 1);
1480 fd_write_all(uxsock, ".\n", 2);
1482 fd_gets(uxsock, buf, sizeof(buf));
1483 if (!strncmp(buf, ".\n", 2)) break;
1486 } else if (cmd.exit) {
1487 fd_write_all(uxsock, "exit\n", 5);
1488 } else if (cmd.target) {
1489 gchar *str = g_strdup_printf("select %s\n", cmd.target);
1490 fd_write_all(uxsock, str, strlen(str));
1494 fd_write_all(uxsock, "get_display\n", 12);
1495 fd_gets(uxsock, buf, sizeof(buf));
1496 if (strcmp2(buf, x_display)) {
1497 printf("Claws Mail is already running on display %s.\n",
1501 uxsock = fd_connect_unix(path);
1502 fd_write_all(uxsock, "popup\n", 6);
1510 static gint lock_socket_remove(void)
1515 if (lock_socket < 0) {
1519 if (lock_socket_tag > 0) {
1520 gdk_input_remove(lock_socket_tag);
1522 fd_close(lock_socket);
1523 filename = claws_get_socket_name();
1530 static GPtrArray *get_folder_item_list(gint sock)
1532 gchar buf[BUFFSIZE];
1534 GPtrArray *folders = NULL;
1537 fd_gets(sock, buf, sizeof(buf));
1538 if (!strncmp(buf, ".\n", 2)) {
1543 folders = g_ptr_array_new();
1545 item = folder_find_item_from_identifier(buf);
1547 g_ptr_array_add(folders, item);
1549 g_warning("no such folder: %s\n", buf);
1556 static void lock_socket_input_cb(gpointer data,
1558 GdkInputCondition condition)
1560 MainWindow *mainwin = (MainWindow *)data;
1562 gchar buf[BUFFSIZE];
1564 sock = fd_accept(source);
1565 fd_gets(sock, buf, sizeof(buf));
1567 if (!strncmp(buf, "popup", 5)) {
1568 main_window_popup(mainwin);
1569 } else if (!strncmp(buf, "get_display", 11)) {
1570 fd_write_all(sock, x_display, strlen(x_display));
1571 } else if (!strncmp(buf, "receive_all", 11)) {
1572 inc_all_account_mail(mainwin, FALSE,
1573 prefs_common.newmail_notify_manu);
1574 } else if (!strncmp(buf, "receive", 7)) {
1575 inc_mail(mainwin, prefs_common.newmail_notify_manu);
1576 } else if (!strncmp(buf, "compose_attach", 14)) {
1580 mailto = g_strdup(buf + strlen("compose_attach") + 1);
1581 files = g_ptr_array_new();
1582 while (fd_gets(sock, buf, sizeof(buf)) > 0) {
1583 if (buf[0] == '.' && buf[1] == '\n') {
1587 g_ptr_array_add(files, g_strdup(buf));
1589 open_compose_new(mailto, files);
1590 ptr_array_free_strings(files);
1591 g_ptr_array_free(files, TRUE);
1593 } else if (!strncmp(buf, "compose", 7)) {
1594 open_compose_new(buf + strlen("compose") + 1, NULL);
1595 } else if (!strncmp(buf, "subscribe", 9)) {
1596 main_window_popup(mainwin);
1597 folder_subscribe(buf + strlen("subscribe") + 1);
1598 } else if (!strncmp(buf, "send", 4)) {
1600 } else if (!strncmp(buf, "online", 6)) {
1601 main_window_toggle_work_offline(mainwin, FALSE, FALSE);
1602 } else if (!strncmp(buf, "offline", 7)) {
1603 main_window_toggle_work_offline(mainwin, TRUE, FALSE);
1604 } else if (!strncmp(buf, "status-full", 11) ||
1605 !strncmp(buf, "status", 6)) {
1609 folders = get_folder_item_list(sock);
1610 status = folder_get_status
1611 (folders, !strncmp(buf, "status-full", 11));
1612 fd_write_all(sock, status, strlen(status));
1613 fd_write_all(sock, ".\n", 2);
1615 if (folders) g_ptr_array_free(folders, TRUE);
1616 } else if (!strncmp(buf, "select ", 7)) {
1617 const gchar *target = buf+7;
1618 mainwindow_jump_to(target);
1619 } else if (!strncmp(buf, "exit", 4)) {
1620 app_will_exit(NULL, mainwin);
1626 static void open_compose_new(const gchar *address, GPtrArray *attach_files)
1631 Xstrdup_a(addr, address, return);
1635 compose_new(NULL, addr, attach_files);
1638 static void send_queue(void)
1641 gchar *errstr = NULL;
1642 for (list = folder_get_list(); list != NULL; list = list->next) {
1643 Folder *folder = list->data;
1645 if (folder->queue) {
1646 gint res = procmsg_send_queue
1647 (folder->queue, prefs_common.savemsg,
1651 folder_item_scan(folder->queue);
1656 gchar *tmp = g_strdup_printf(_("Some errors occurred "
1657 "while sending queued messages:\n%s"), errstr);
1659 alertpanel_error_log(tmp);
1662 alertpanel_error_log("Some errors occurred "
1663 "while sending queued messages.");
1667 static void quit_signal_handler(int sig)
1669 debug_print("Quitting on signal %d\n", sig);
1671 g_timeout_add(0, clean_quit, NULL);
1674 static void install_basic_sighandlers()
1678 struct sigaction act;
1683 sigaddset(&mask, SIGTERM);
1686 sigaddset(&mask, SIGINT);
1689 sigaddset(&mask, SIGHUP);
1692 act.sa_handler = quit_signal_handler;
1697 sigaction(SIGTERM, &act, 0);
1700 sigaction(SIGINT, &act, 0);
1703 sigaction(SIGHUP, &act, 0);
1706 sigprocmask(SIG_UNBLOCK, &mask, 0);
1707 #endif /* !G_OS_WIN32 */