2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2016 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 3 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, see <http://www.gnu.org/licenses/>.
21 #include "claws-features.h"
27 #include <glib/gi18n.h>
37 #include <sys/types.h>
44 #include <X11/SM/SMlib.h>
51 #include "file_checker.h"
53 #ifdef HAVE_STARTUP_NOTIFICATION
54 # define SN_API_NOT_YET_FROZEN
55 # include <libsn/sn-launchee.h>
56 # include <gdk/gdkx.h>
60 #include <dbus/dbus-glib.h>
62 #ifdef HAVE_NETWORKMANAGER_SUPPORT
63 #include <NetworkManager.h>
69 #include <librsvg/rsvg.h>
74 #include "mainwindow.h"
75 #include "folderview.h"
76 #include "image_viewer.h"
77 #include "summaryview.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
80 #include "prefs_actions.h"
81 #include "prefs_ext_prog.h"
82 #include "prefs_fonts.h"
83 #include "prefs_image_viewer.h"
84 #include "prefs_message.h"
85 #include "prefs_migration.h"
86 #include "prefs_receive.h"
87 #include "prefs_msg_colors.h"
88 #include "prefs_quote.h"
89 #include "prefs_spelling.h"
90 #include "prefs_summaries.h"
91 #include "prefs_themes.h"
92 #include "prefs_other.h"
93 #include "prefs_proxy.h"
94 #include "prefs_logging.h"
95 #include "prefs_send.h"
96 #include "prefs_wrapping.h"
97 #include "prefs_compose_writing.h"
98 #include "prefs_display_header.h"
103 #include "send_message.h"
106 #include "manage_window.h"
107 #include "alertpanel.h"
108 #include "statusbar.h"
109 #ifndef USE_ALT_ADDRBOOK
110 #include "addressbook.h"
112 #include "addressbook-dbus.h"
116 #include "folder_item_prefs.h"
119 #include "gtkutils.h"
122 #include "prefs_toolbar.h"
125 #include "imap_gtk.h"
126 #include "news_gtk.h"
131 #include "quicksearch.h"
132 #include "advsearch.h"
134 #include "passwordstore.h"
135 #include "file-utils.h"
138 #include "imap-thread.h"
139 #include "nntp-thread.h"
141 #include "stock_pixmap.h"
152 #ifdef HAVE_NETWORKMANAGER_SUPPORT
153 /* Went offline due to NetworkManager */
154 static gboolean went_offline_nm
;
158 #ifdef HAVE_DBUS_GLIB
159 static DBusGProxy
*awn_proxy
= NULL
;
163 #if (defined HAVE_LIBSM || defined CRASH_DIALOG)
167 #ifdef HAVE_STARTUP_NOTIFICATION
168 static SnLauncheeContext
*sn_context
= NULL
;
169 static SnDisplay
*sn_display
= NULL
;
172 static gint lock_socket
= -1;
173 static gint lock_socket_tag
= 0;
175 static gchar
*x_display
= NULL
;
179 ONLINE_MODE_DONT_CHANGE
,
184 static struct RemoteCmd
{
186 gboolean receive_all
;
187 gboolean cancel_receiving
;
188 gboolean cancel_sending
;
190 const gchar
*compose_mailto
;
193 const gchar
*search_folder
;
194 const gchar
*search_type
;
195 const gchar
*search_request
;
196 gboolean search_recursive
;
198 gboolean status_full
;
200 gboolean reset_statistics
;
201 GPtrArray
*status_folders
;
202 GPtrArray
*status_full_folders
;
209 const gchar
*subscribe_uri
;
212 const gchar
*geometry
;
215 SessionStats session_stats
;
217 static void reset_statistics(void);
219 static void parse_cmd_opt(int argc
, char *argv
[]);
221 static gint
prohibit_duplicate_launch (void);
222 static gchar
* get_crashfile_name (void);
223 static gint
lock_socket_remove (void);
224 static void lock_socket_input_cb (gpointer data
,
226 GIOCondition condition
);
228 static void open_compose_new (const gchar
*address
,
229 GList
*attach_files
);
231 static void send_queue (void);
232 static void initial_processing (FolderItem
*item
, gpointer data
);
233 static void quit_signal_handler (int sig
);
234 static void install_basic_sighandlers (void);
235 #if (defined linux && defined SIGIO)
236 static void install_memory_sighandler (void);
238 static void exit_claws (MainWindow
*mainwin
);
240 #ifdef HAVE_NETWORKMANAGER_SUPPORT
241 static void networkmanager_state_change_cb(DBusGProxy
*proxy
, gchar
*dev
,
245 #define MAKE_DIR_IF_NOT_EXIST(dir) \
247 if (!is_dir_exist(dir)) { \
248 if (is_file_exist(dir)) { \
250 (_("File '%s' already exists.\n" \
251 "Can't create folder."), \
255 if (make_dir(dir) < 0) \
260 #define STRNCMP(S1, S2) (strncmp((S1), (S2), sizeof((S2)) - 1))
262 #define CM_FD_WRITE(S) fd_write(sock, (S), strlen((S)))
263 #define CM_FD_WRITE_ALL(S) fd_write_all(sock, (S), strlen((S)))
265 static MainWindow
*static_mainwindow
;
267 static gboolean emergency_exit
= FALSE
;
269 #ifdef HAVE_STARTUP_NOTIFICATION
270 static void sn_error_trap_push(SnDisplay
*display
, Display
*xdisplay
)
272 gdk_error_trap_push();
275 static void sn_error_trap_pop(SnDisplay
*display
, Display
*xdisplay
)
277 gdk_error_trap_pop();
280 static void startup_notification_complete(gboolean with_window
)
283 GtkWidget
*hack
= NULL
;
286 /* this is needed to make the startup notification leave,
287 * if we have been launched from a menu.
288 * We have to display a window, so let it be very little */
289 hack
= gtk_window_new(GTK_WINDOW_POPUP
);
290 gtk_window_move(GTK_WINDOW(hack
), 0, 0);
291 gtk_widget_set_size_request(hack
, 1, 1);
292 gtk_widget_show(hack
);
295 xdisplay
= GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
296 sn_display
= sn_display_new(xdisplay
,
299 sn_context
= sn_launchee_context_new_from_environment(sn_display
,
300 DefaultScreen(xdisplay
));
302 if (sn_context
!= NULL
) {
303 sn_launchee_context_complete(sn_context
);
304 sn_launchee_context_unref(sn_context
);
305 sn_display_unref(sn_display
);
308 gtk_widget_destroy(hack
);
311 #endif /* HAVE_STARTUP_NOTIFICATION */
313 static void claws_gtk_idle(void)
315 while(gtk_events_pending()) {
316 gtk_main_iteration();
321 static gboolean sc_starting
= FALSE
;
323 static gboolean
defer_check_all(void *data
)
325 gboolean autochk
= GPOINTER_TO_INT(data
);
328 inc_all_account_mail(static_mainwindow
, autochk
, FALSE
,
329 prefs_common
.newmail_notify_manu
);
332 inc_all_account_mail(static_mainwindow
, FALSE
,
333 prefs_common
.chk_on_startup
,
334 prefs_common
.newmail_notify_manu
);
336 main_window_set_menu_sensitive(static_mainwindow
);
337 toolbar_main_set_sensitive(static_mainwindow
);
342 static gboolean
defer_check(void *data
)
344 inc_mail(static_mainwindow
, prefs_common
.newmail_notify_manu
);
348 main_window_set_menu_sensitive(static_mainwindow
);
349 toolbar_main_set_sensitive(static_mainwindow
);
354 static gboolean
defer_jump(void *data
)
356 if (cmd
.receive_all
) {
357 defer_check_all(GINT_TO_POINTER(FALSE
));
358 } else if (prefs_common
.chk_on_startup
) {
359 defer_check_all(GINT_TO_POINTER(TRUE
));
360 } else if (cmd
.receive
) {
363 mainwindow_jump_to(data
, FALSE
);
366 main_window_set_menu_sensitive(static_mainwindow
);
367 toolbar_main_set_sensitive(static_mainwindow
);
372 static void chk_update_val(GtkWidget
*widget
, gpointer data
)
374 gboolean
*val
= (gboolean
*)data
;
375 *val
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget
));
378 static gboolean
migrate_old_config(const gchar
*old_cfg_dir
, const gchar
*new_cfg_dir
, const gchar
*oldversion
)
380 gchar
*message
= g_strdup_printf(_("Configuration for %s found.\n"
381 "Do you want to migrate this configuration?"), oldversion
);
382 gchar
*message2
= g_strdup_printf(_("\n\nYour Sylpheed filtering rules can be converted by a\n"
383 "script available at %s."), TOOLS_URI
);
385 if (!strcmp(oldversion
, "Sylpheed"))
386 message
= g_strconcat(message
, message2
, NULL
);
390 GtkWidget
*window
= NULL
;
391 GtkWidget
*keep_backup_chk
;
392 gboolean backup
= TRUE
;
394 keep_backup_chk
= gtk_check_button_new_with_label (_("Keep old configuration"));
395 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(keep_backup_chk
), TRUE
);
396 CLAWS_SET_TIP(keep_backup_chk
,
397 _("Keeping a backup will allow you to go back to an "
398 "older version, but may take a while if you have "
399 "cached IMAP or News data, and will take some extra "
400 "room on your disk."));
402 g_signal_connect(G_OBJECT(keep_backup_chk
), "toggled",
403 G_CALLBACK(chk_update_val
), &backup
);
405 if (alertpanel_full(_("Migration of configuration"), message
,
406 GTK_STOCK_NO
, GTK_STOCK_YES
, NULL
, ALERTFOCUS_SECOND
, FALSE
,
407 keep_backup_chk
, ALERT_QUESTION
) != G_ALERTALTERNATE
) {
411 /* we can either do a fast migration requiring not any extra disk
412 * space, or a slow one that copies the old configuration and leaves
416 window
= label_window_create(_("Copying configuration... This may take a while..."));
419 r
= copy_dir(old_cfg_dir
, new_cfg_dir
);
420 label_window_destroy(window
);
422 /* if copy failed, we'll remove the partially copied
425 alertpanel_error(_("Migration failed!"));
426 remove_dir_recursive(new_cfg_dir
);
429 /* fast mode failed, but we don't want backup */
430 remove_dir_recursive(old_cfg_dir
);
434 window
= label_window_create(_("Migrating configuration..."));
437 r
= g_rename(old_cfg_dir
, new_cfg_dir
);
438 label_window_destroy(window
);
440 /* if g_rename failed, we'll try to copy */
442 FILE_OP_ERROR(new_cfg_dir
, "g_rename");
443 debug_print("rename failed, trying copy\n");
450 static int migrate_common_rc(const gchar
*old_rc
, const gchar
*new_rc
)
453 gchar
*plugin_path
, *old_plugin_path
, *new_plugin_path
;
455 gboolean err
= FALSE
;
457 oldfp
= claws_fopen(old_rc
, "r");
460 newfp
= claws_fopen(new_rc
, "w");
466 plugin_path
= g_strdup(get_plugin_dir());
467 new_plugin_path
= g_strdup(plugin_path
);
469 if (strstr(plugin_path
, "/claws-mail/")) {
470 gchar
*end
= g_strdup(strstr(plugin_path
, "/claws-mail/")+strlen("/claws-mail/"));
471 *(strstr(plugin_path
, "/claws-mail/")) = '\0';
472 old_plugin_path
= g_strconcat(plugin_path
, "/sylpheed-claws/", end
, NULL
);
475 old_plugin_path
= g_strdup(new_plugin_path
);
477 debug_print("replacing %s with %s\n", old_plugin_path
, new_plugin_path
);
478 while (claws_fgets(buf
, sizeof(buf
), oldfp
)) {
479 if (STRNCMP(buf
, old_plugin_path
)) {
480 err
|= (claws_fputs(buf
, newfp
) == EOF
);
482 debug_print("->replacing %s\n", buf
);
483 debug_print(" with %s%s\n", new_plugin_path
, buf
+strlen(old_plugin_path
));
484 err
|= (claws_fputs(new_plugin_path
, newfp
) == EOF
);
485 err
|= (claws_fputs(buf
+strlen(old_plugin_path
), newfp
) == EOF
);
489 g_free(new_plugin_path
);
490 g_free(old_plugin_path
);
492 if (claws_safe_fclose(newfp
) == EOF
)
500 sc_client_set_value (MainWindow
*mainwin
,
511 prop
.num_vals
= num_vals
;
515 if (mainwin
->smc_conn
)
516 SmcSetProperties ((SmcConn
) mainwin
->smc_conn
, 1, proplist
);
519 static void sc_die_callback (SmcConn smc_conn
, SmPointer client_data
)
524 static void sc_save_complete_callback(SmcConn smc_conn
, SmPointer client_data
)
528 static void sc_shutdown_cancelled_callback (SmcConn smc_conn
, SmPointer client_data
)
530 MainWindow
*mainwin
= (MainWindow
*)client_data
;
531 if (mainwin
->smc_conn
)
532 SmcSaveYourselfDone ((SmcConn
) mainwin
->smc_conn
, TRUE
);
535 static void sc_save_yourself_callback (SmcConn smc_conn
,
536 SmPointer client_data
,
542 MainWindow
*mainwin
= (MainWindow
*)client_data
;
543 if (mainwin
->smc_conn
)
544 SmcSaveYourselfDone ((SmcConn
) mainwin
->smc_conn
, TRUE
);
547 static IceIOErrorHandler sc_ice_installed_handler
;
549 static void sc_ice_io_error_handler (IceConn connection
)
551 if (sc_ice_installed_handler
)
552 (*sc_ice_installed_handler
) (connection
);
554 static gboolean
sc_process_ice_messages (GIOChannel
*source
,
555 GIOCondition condition
,
558 IceConn connection
= (IceConn
) data
;
559 IceProcessMessagesStatus status
;
561 status
= IceProcessMessages (connection
, NULL
, NULL
);
563 if (status
== IceProcessMessagesIOError
) {
564 IcePointer context
= IceGetConnectionContext (connection
);
566 if (context
&& G_IS_OBJECT(context
)) {
567 guint disconnect_id
= g_signal_lookup ("disconnect", G_OBJECT_TYPE (context
));
569 if (disconnect_id
> 0)
570 g_signal_emit (context
, disconnect_id
, 0);
572 IceSetShutdownNegotiation (connection
, False
);
573 IceCloseConnection (connection
);
580 static void new_ice_connection (IceConn connection
, IcePointer client_data
, Bool opening
,
581 IcePointer
*watch_data
)
587 /* Make sure we don't pass on these file descriptors to any
589 fcntl(IceConnectionNumber(connection
),F_SETFD
,
590 fcntl(IceConnectionNumber(connection
),F_GETFD
,0) | FD_CLOEXEC
);
592 channel
= g_io_channel_unix_new (IceConnectionNumber (connection
));
593 input_id
= g_io_add_watch (channel
,
594 G_IO_IN
| G_IO_HUP
| G_IO_ERR
| G_IO_PRI
,
595 sc_process_ice_messages
,
597 g_io_channel_unref (channel
);
599 *watch_data
= (IcePointer
) GUINT_TO_POINTER (input_id
);
601 input_id
= GPOINTER_TO_UINT ((gpointer
) *watch_data
);
602 g_source_remove (input_id
);
606 static void sc_session_manager_connect(MainWindow
*mainwin
)
608 static gboolean connected
= FALSE
;
609 SmcCallbacks callbacks
;
611 IceIOErrorHandler default_handler
;
618 sc_ice_installed_handler
= IceSetIOErrorHandler (NULL
);
619 default_handler
= IceSetIOErrorHandler (sc_ice_io_error_handler
);
621 if (sc_ice_installed_handler
== default_handler
)
622 sc_ice_installed_handler
= NULL
;
624 IceAddConnectionWatch (new_ice_connection
, NULL
);
627 callbacks
.save_yourself
.callback
= sc_save_yourself_callback
;
628 callbacks
.die
.callback
= sc_die_callback
;
629 callbacks
.save_complete
.callback
= sc_save_complete_callback
;
630 callbacks
.shutdown_cancelled
.callback
= sc_shutdown_cancelled_callback
;
632 callbacks
.save_yourself
.client_data
=
633 callbacks
.die
.client_data
=
634 callbacks
.save_complete
.client_data
=
635 callbacks
.shutdown_cancelled
.client_data
= (SmPointer
) mainwin
;
636 if (g_getenv ("SESSION_MANAGER")) {
637 gchar error_string_ret
[256] = "";
639 mainwin
->smc_conn
= (gpointer
)
640 SmcOpenConnection (NULL
, mainwin
,
641 SmProtoMajor
, SmProtoMinor
,
642 SmcSaveYourselfProcMask
| SmcDieProcMask
|
643 SmcSaveCompleteProcMask
|
644 SmcShutdownCancelledProcMask
,
647 256, error_string_ret
);
649 /* From https://www.x.org/releases/X11R7.7/doc/libSM/SMlib.txt:
650 * If SmcOpenConnection succeeds, it returns an opaque connection
651 * pointer of type SmcConn and the client_id_ret argument contains
652 * the client ID to be used for this session. The client_id_ret
653 * should be freed with a call to free when no longer needed. On
654 * failure, SmcOpenConnection returns NULL, and the reason for
655 * failure is returned in error_string_ret. */
656 if (mainwin
->smc_conn
!= NULL
)
659 if (error_string_ret
[0] || mainwin
->smc_conn
== NULL
)
660 g_warning ("While connecting to session manager: %s.",
664 vals
= g_new (SmPropValue
, 1);
665 vals
[0].length
= strlen(argv0
);
666 vals
[0].value
= argv0
;
667 sc_client_set_value (mainwin
, SmCloneCommand
, SmLISTofARRAY8
, 1, vals
);
668 sc_client_set_value (mainwin
, SmRestartCommand
, SmLISTofARRAY8
, 1, vals
);
669 sc_client_set_value (mainwin
, SmProgram
, SmARRAY8
, 1, vals
);
671 vals
[0].length
= strlen(g_get_user_name()?g_get_user_name():"");
672 vals
[0].value
= g_strdup(g_get_user_name()?g_get_user_name():"");
673 sc_client_set_value (mainwin
, SmUserID
, SmARRAY8
, 1, vals
);
675 g_free(vals
[0].value
);
682 static gboolean sc_exiting
= FALSE
;
683 static gboolean show_at_startup
= TRUE
;
684 static gboolean claws_crashed_bool
= FALSE
;
686 gboolean
claws_crashed(void) {
687 return claws_crashed_bool
;
690 void main_set_show_at_startup(gboolean show
)
692 show_at_startup
= show
;
696 static FILE* win32_debug_fp
=NULL
;
697 static guint win32_log_handler_app_id
;
698 static guint win32_log_handler_glib_id
;
699 static guint win32_log_handler_gtk_id
;
701 static void win32_print_stdout(const gchar
*string
)
703 if (win32_debug_fp
) {
704 fprintf(win32_debug_fp
, "%s", string
);
705 fflush(win32_debug_fp
);
709 static void win32_print_stderr(const gchar
*string
)
711 if (win32_debug_fp
) {
712 fprintf(win32_debug_fp
, "%s", string
);
713 fflush(win32_debug_fp
);
717 static void win32_log(const gchar
*log_domain
, GLogLevelFlags log_level
, const gchar
* message
, gpointer user_data
)
719 if (win32_debug_fp
) {
722 switch(log_level
& G_LOG_LEVEL_MASK
)
724 case G_LOG_LEVEL_ERROR
:
727 case G_LOG_LEVEL_CRITICAL
:
730 case G_LOG_LEVEL_WARNING
:
733 case G_LOG_LEVEL_MESSAGE
:
736 case G_LOG_LEVEL_INFO
:
739 case G_LOG_LEVEL_DEBUG
:
746 fprintf(win32_debug_fp
, "%s: %s: %s", log_domain
, type
, message
);
748 fprintf(win32_debug_fp
, "%s: %s", type
, message
);
749 fflush(win32_debug_fp
);
753 static void win32_open_log(void)
755 gchar
*logfile
= win32_debug_log_path();
756 gchar
*oldlogfile
= g_strconcat(logfile
, ".bak", NULL
);
758 if (is_file_exist(logfile
)) {
759 if (rename_force(logfile
, oldlogfile
) < 0)
760 FILE_OP_ERROR(logfile
, "rename");
762 win32_debug_fp
= claws_fopen(logfile
, "w");
767 g_set_print_handler(win32_print_stdout
);
768 g_set_printerr_handler(win32_print_stdout
);
769 win32_log_handler_app_id
= g_log_set_handler(NULL
, G_LOG_LEVEL_MASK
| G_LOG_FLAG_FATAL
770 | G_LOG_FLAG_RECURSION
, win32_log
, NULL
);
771 win32_log_handler_glib_id
= g_log_set_handler("GLib", G_LOG_LEVEL_MASK
| G_LOG_FLAG_FATAL
772 | G_LOG_FLAG_RECURSION
, win32_log
, NULL
);
773 win32_log_handler_gtk_id
= g_log_set_handler("Gtk", G_LOG_LEVEL_MASK
| G_LOG_FLAG_FATAL
774 | G_LOG_FLAG_RECURSION
, win32_log
, NULL
);
778 static void win32_close_log(void)
782 g_log_remove_handler("", win32_log_handler_app_id
);
783 g_log_remove_handler("GLib", win32_log_handler_glib_id
);
784 g_log_remove_handler("Gtk", win32_log_handler_gtk_id
);
785 claws_fclose(win32_debug_fp
);
791 static void main_dump_features_list(gboolean show_debug_only
)
792 /* display compiled-in features list */
794 if (show_debug_only
&& !debug_get_mode())
798 debug_print("runtime GTK+ %d.%d.%d / GLib %d.%d.%d\n",
799 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
800 glib_major_version
, glib_minor_version
, glib_micro_version
);
802 g_print("runtime GTK+ %d.%d.%d / GLib %d.%d.%d\n",
803 gtk_major_version
, gtk_minor_version
, gtk_micro_version
,
804 glib_major_version
, glib_minor_version
, glib_micro_version
);
806 debug_print("buildtime GTK+ %d.%d.%d / GLib %d.%d.%d\n",
807 GTK_MAJOR_VERSION
, GTK_MINOR_VERSION
, GTK_MICRO_VERSION
,
808 GLIB_MAJOR_VERSION
, GLIB_MINOR_VERSION
, GLIB_MICRO_VERSION
);
810 g_print("buildtime GTK+ %d.%d.%d / GLib %d.%d.%d\n",
811 GTK_MAJOR_VERSION
, GTK_MINOR_VERSION
, GTK_MICRO_VERSION
,
812 GLIB_MAJOR_VERSION
, GLIB_MINOR_VERSION
, GLIB_MICRO_VERSION
);
815 debug_print("Compiled-in features:\n");
817 g_print("Compiled-in features:\n");
820 debug_print(" compface\n");
822 g_print(" compface\n");
826 debug_print(" Enchant\n");
828 g_print(" Enchant\n");
832 debug_print(" GnuTLS\n");
834 g_print(" GnuTLS\n");
838 debug_print(" IPv6\n");
844 debug_print(" iconv\n");
850 debug_print(" JPilot\n");
852 g_print(" JPilot\n");
856 debug_print(" LDAP\n");
862 debug_print(" libetpan %d.%d\n", LIBETPAN_VERSION_MAJOR
, LIBETPAN_VERSION_MINOR
);
864 g_print(" libetpan %d.%d\n", LIBETPAN_VERSION_MAJOR
, LIBETPAN_VERSION_MINOR
);
868 debug_print(" libSM\n");
872 #if HAVE_NETWORKMANAGER_SUPPORT
874 debug_print(" NetworkManager\n");
876 g_print(" NetworkManager\n");
880 debug_print(" librSVG " LIBRSVG_VERSION
"\n");
882 g_print(" librSVG " LIBRSVG_VERSION
"\n");
886 #ifdef HAVE_DBUS_GLIB
887 static gulong dbus_item_hook_id
= HOOK_NONE
;
888 static gulong dbus_folder_hook_id
= HOOK_NONE
;
890 static void uninstall_dbus_status_handler(void)
893 g_object_unref(awn_proxy
);
895 if (dbus_item_hook_id
!= HOOK_NONE
)
896 hooks_unregister_hook(FOLDER_ITEM_UPDATE_HOOKLIST
, dbus_item_hook_id
);
897 if (dbus_folder_hook_id
!= HOOK_NONE
)
898 hooks_unregister_hook(FOLDER_UPDATE_HOOKLIST
, dbus_folder_hook_id
);
901 static void dbus_update(FolderItem
*removed_item
)
903 guint
new, unread
, unreadmarked
, marked
, total
;
904 guint replied
, forwarded
, locked
, ignored
, watched
;
906 GError
*error
= NULL
;
908 folder_count_total_msgs(&new, &unread
, &unreadmarked
, &marked
, &total
,
909 &replied
, &forwarded
, &locked
, &ignored
,
912 total
-= removed_item
->total_msgs
;
913 new -= removed_item
->new_msgs
;
914 unread
-= removed_item
->unread_msgs
;
918 buf
= g_strdup_printf("%d", new);
919 dbus_g_proxy_call(awn_proxy
, "SetInfoByName", &error
,
920 G_TYPE_STRING
, "claws-mail",
922 G_TYPE_INVALID
, G_TYPE_INVALID
);
926 dbus_g_proxy_call(awn_proxy
, "UnsetInfoByName", &error
, G_TYPE_STRING
,
927 "claws-mail", G_TYPE_INVALID
, G_TYPE_INVALID
);
930 debug_print("%s\n", error
->message
);
935 static gboolean
dbus_status_update_folder_hook(gpointer source
, gpointer data
)
937 FolderUpdateData
*hookdata
;
939 if (hookdata
->update_flags
& FOLDER_REMOVE_FOLDERITEM
)
940 dbus_update(hookdata
->item
);
947 static gboolean
dbus_status_update_item_hook(gpointer source
, gpointer data
)
954 static void install_dbus_status_handler(void)
956 GError
*tmp_error
= NULL
;
957 DBusGConnection
*connection
= dbus_g_bus_get(DBUS_BUS_SESSION
, &tmp_error
);
960 /* If calling code doesn't do error checking, at least print some debug */
961 debug_print("Failed to open connection to session bus: %s\n",
963 g_error_free(tmp_error
);
966 awn_proxy
= dbus_g_proxy_new_for_name(connection
,
967 "com.google.code.Awn",
968 "/com/google/code/Awn",
969 "com.google.code.Awn");
970 dbus_item_hook_id
= hooks_register_hook (FOLDER_ITEM_UPDATE_HOOKLIST
, dbus_status_update_item_hook
, NULL
);
971 if (dbus_item_hook_id
== HOOK_NONE
) {
972 g_warning("Failed to register folder item update hook");
973 uninstall_dbus_status_handler();
977 dbus_folder_hook_id
= hooks_register_hook (FOLDER_UPDATE_HOOKLIST
, dbus_status_update_folder_hook
, NULL
);
978 if (dbus_folder_hook_id
== HOOK_NONE
) {
979 g_warning("Failed to register folder update hook");
980 uninstall_dbus_status_handler();
986 static void reset_statistics(void)
988 /* (re-)initialize session statistics */
989 session_stats
.received
= 0;
990 session_stats
.sent
= 0;
991 session_stats
.replied
= 0;
992 session_stats
.forwarded
= 0;
993 session_stats
.time_started
= time(NULL
);
996 int main(int argc
, char *argv
[])
998 #ifdef HAVE_DBUS_GLIB
999 DBusGConnection
*connection
;
1002 #ifdef HAVE_NETWORKMANAGER_SUPPORT
1003 DBusGProxy
*nm_proxy
;
1006 MainWindow
*mainwin
;
1007 FolderView
*folderview
;
1009 gboolean crash_file_present
= FALSE
;
1010 guint num_folder_class
= 0;
1011 gboolean asked_for_migration
= FALSE
;
1012 gboolean start_done
= TRUE
;
1013 GSList
*plug_list
= NULL
;
1014 gboolean never_ran
= FALSE
;
1015 gboolean mainwin_shown
= FALSE
;
1018 START_TIMING("startup");
1025 if (!claws_init(&argc
, &argv
)) {
1032 prog_version
= PROG_VERSION
;
1033 #if (defined HAVE_LIBSM || defined CRASH_DIALOG)
1034 argv0
= g_strdup(argv
[0]);
1037 parse_cmd_opt(argc
, argv
);
1041 /* check and create unix domain socket for remote operation */
1042 lock_socket
= prohibit_duplicate_launch();
1043 if (lock_socket
< 0) {
1044 #ifdef HAVE_STARTUP_NOTIFICATION
1045 if(gtk_init_check(&argc
, &argv
))
1046 startup_notification_complete(TRUE
);
1051 main_dump_features_list(TRUE
);
1052 prefs_prepare_cache();
1057 gtk_init(&argc
, &argv
);
1058 crash_main(cmd
.crash_params
);
1064 crash_install_handlers();
1066 install_basic_sighandlers();
1067 #if (defined linux && defined SIGIO)
1068 install_memory_sighandler();
1071 if (cmd
.status
|| cmd
.status_full
|| cmd
.search
||
1072 cmd
.statistics
|| cmd
.reset_statistics
||
1073 cmd
.cancel_receiving
|| cmd
.cancel_sending
||
1075 puts("0 Claws Mail not running.");
1076 lock_socket_remove();
1082 #if !GLIB_CHECK_VERSION(2,32,0)
1083 if (!g_thread_supported())
1084 g_thread_init(NULL
);
1090 gtk_init(&argc
, &argv
);
1093 gtk_settings_set_string_property(gtk_settings_get_default(),
1097 gtk_settings_set_long_property(gtk_settings_get_default(),
1098 "gtk-auto-mnemonics",
1101 gtk_settings_set_long_property(gtk_settings_get_default(),
1102 "gtk-button-images",
1107 #ifdef HAVE_NETWORKMANAGER_SUPPORT
1108 went_offline_nm
= FALSE
;
1111 #ifdef HAVE_DBUS_GLIB
1113 connection
= dbus_g_bus_get(DBUS_BUS_SYSTEM
, &error
);
1116 debug_print("Failed to open connection to system bus: %s\n", error
->message
);
1117 g_error_free(error
);
1120 #ifdef HAVE_NETWORKMANAGER_SUPPORT
1121 nm_proxy
= dbus_g_proxy_new_for_name(connection
,
1122 "org.freedesktop.NetworkManager",
1123 "/org/freedesktop/NetworkManager",
1124 "org.freedesktop.NetworkManager");
1126 dbus_g_proxy_add_signal(nm_proxy
, "StateChanged", G_TYPE_UINT
, G_TYPE_INVALID
);
1127 dbus_g_proxy_connect_signal(nm_proxy
, "StateChanged",
1128 G_CALLBACK(networkmanager_state_change_cb
),
1132 install_dbus_status_handler();
1136 gtk_widget_set_default_colormap(
1137 gdk_screen_get_system_colormap(
1138 gdk_screen_get_default()));
1140 gtkut_create_ui_manager();
1142 /* Create container for all the menus we will be adding */
1143 MENUITEM_ADDUI("/", "Menus", NULL
, GTK_UI_MANAGER_MENUBAR
);
1145 if (!g_thread_supported()) {
1146 g_error(_("g_thread is not supported by glib.\n"));
1150 CHDIR_EXEC_CODE_RETURN_VAL_IF_FAIL(get_home_dir(), 1, win32_close_log(););
1152 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), 1);
1155 /* no config dir exists. See if we can migrate an old config. */
1156 if (!is_dir_exist(get_rc_dir())) {
1157 prefs_destroy_cache();
1160 /* if one of the old dirs exist, we'll ask if the user
1161 * want to migrates, and r will be TRUE if he said yes
1162 * and migration succeeded, and FALSE otherwise.
1164 if (is_dir_exist(OLD_GTK2_RC_DIR
)) {
1165 r
= migrate_old_config(OLD_GTK2_RC_DIR
, get_rc_dir(),
1166 g_strconcat("Sylpheed-Claws 2.6.0 ", _("(or older)"), NULL
));
1167 asked_for_migration
= TRUE
;
1168 } else if (is_dir_exist(OLDER_GTK2_RC_DIR
)) {
1169 r
= migrate_old_config(OLDER_GTK2_RC_DIR
, get_rc_dir(),
1170 g_strconcat("Sylpheed-Claws 1.9.15 ",_("(or older)"), NULL
));
1171 asked_for_migration
= TRUE
;
1172 } else if (is_dir_exist(OLD_GTK1_RC_DIR
)) {
1173 r
= migrate_old_config(OLD_GTK1_RC_DIR
, get_rc_dir(),
1174 g_strconcat("Sylpheed-Claws 1.0.5 ",_("(or older)"), NULL
));
1175 asked_for_migration
= TRUE
;
1176 } else if (is_dir_exist(SYLPHEED_RC_DIR
)) {
1177 r
= migrate_old_config(SYLPHEED_RC_DIR
, get_rc_dir(), "Sylpheed");
1178 asked_for_migration
= TRUE
;
1181 /* If migration failed or the user didn't want to do it,
1182 * we create a new one (and we'll hit wizard later).
1184 if (r
== FALSE
&& !is_dir_exist(get_rc_dir())) {
1186 if (copy_dir(SYSCONFDIR
"/skel/.claws-mail", get_rc_dir()) < 0) {
1188 if (!is_dir_exist(get_rc_dir()) && make_dir(get_rc_dir()) < 0) {
1201 if (!is_file_exist(RC_DIR G_DIR_SEPARATOR_S COMMON_RC
) &&
1202 is_file_exist(RC_DIR G_DIR_SEPARATOR_S OLD_COMMON_RC
)) {
1203 /* post 2.6 name change */
1204 migrate_common_rc(RC_DIR G_DIR_SEPARATOR_S OLD_COMMON_RC
,
1205 RC_DIR G_DIR_SEPARATOR_S COMMON_RC
);
1209 plugin_load_all("Common");
1211 userrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, "gtkrc-2.0", NULL
);
1212 gtk_rc_parse(userrc
);
1215 userrc
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, MENU_RC
, NULL
);
1216 gtk_accel_map_load (userrc
);
1220 CHDIR_EXEC_CODE_RETURN_VAL_IF_FAIL(get_rc_dir(), 1, win32_close_log(););
1222 CHDIR_RETURN_VAL_IF_FAIL(get_rc_dir(), 1);
1225 MAKE_DIR_IF_NOT_EXIST(get_mail_base_dir());
1226 MAKE_DIR_IF_NOT_EXIST(get_imap_cache_dir());
1227 MAKE_DIR_IF_NOT_EXIST(get_news_cache_dir());
1228 MAKE_DIR_IF_NOT_EXIST(get_mime_tmp_dir());
1229 MAKE_DIR_IF_NOT_EXIST(get_tmp_dir());
1230 MAKE_DIR_IF_NOT_EXIST(UIDL_DIR
);
1232 crash_file_present
= is_file_exist(get_crashfile_name());
1233 /* remove temporary files */
1234 remove_all_files(get_tmp_dir());
1235 remove_all_files(get_mime_tmp_dir());
1237 if (!cmd
.crash
&& crash_file_present
)
1238 claws_crashed_bool
= TRUE
;
1240 if (is_file_exist("claws.log")) {
1241 if (rename_force("claws.log", "claws.log.bak") < 0)
1242 FILE_OP_ERROR("claws.log", "rename");
1244 set_log_file(LOG_PROTOCOL
, "claws.log");
1246 if (is_file_exist("filtering.log")) {
1247 if (rename_force("filtering.log", "filtering.log.bak") < 0)
1248 FILE_OP_ERROR("filtering.log", "rename");
1250 set_log_file(LOG_DEBUG_FILTERING
, "filtering.log");
1253 CHDIR_EXEC_CODE_RETURN_VAL_IF_FAIL(get_home_dir(), 1, win32_close_log(););
1255 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), 1);
1258 folder_system_init();
1259 prefs_common_read_config();
1261 if (prefs_update_config_version_common() < 0) {
1262 debug_print("Main configuration file version upgrade failed, exiting\n");
1269 prefs_themes_init();
1271 prefs_ext_prog_init();
1272 prefs_wrapping_init();
1273 prefs_compose_writing_init();
1274 prefs_msg_colors_init();
1275 image_viewer_init();
1276 prefs_image_viewer_init();
1278 prefs_summaries_init();
1279 prefs_message_init();
1282 prefs_logging_init();
1283 prefs_receive_init();
1288 gtkaspell_checkers_init();
1289 prefs_spelling_init();
1292 codeconv_set_allow_jisx0201_kana(prefs_common
.allow_jisx0201_kana
);
1293 codeconv_set_broken_are_utf8(prefs_common
.broken_are_utf8
);
1296 if(prefs_common
.gtk_theme
&& strcmp(prefs_common
.gtk_theme
, DEFAULT_W32_GTK_THEME
))
1297 gtk_settings_set_string_property(gtk_settings_get_default(),
1299 prefs_common
.gtk_theme
,
1304 sock_set_io_timeout(prefs_common
.io_timeout_secs
);
1305 prefs_actions_read_config();
1306 prefs_display_header_read_config();
1307 /* prefs_filtering_read_config(); */
1308 #ifndef USE_ALT_ADDRBOOK
1309 addressbook_read_file();
1311 g_clear_error(&error
);
1312 if (! addressbook_start_service(&error
)) {
1313 g_warning("%s", error
->message
);
1314 g_clear_error(&error
);
1317 addressbook_install_hooks(&error
);
1320 gtkut_widget_init();
1321 stock_pixbuf_gdk(STOCK_PIXMAP_CLAWS_MAIL_ICON
, &icon
);
1322 gtk_window_set_default_icon(icon
);
1324 folderview_initialize();
1330 mainwin
= main_window_create();
1332 if (!check_file_integrity())
1335 #ifdef HAVE_NETWORKMANAGER_SUPPORT
1336 networkmanager_state_change_cb(nm_proxy
,NULL
,mainwin
);
1339 manage_window_focus_in(mainwin
->window
, NULL
, NULL
);
1340 folderview
= mainwin
->folderview
;
1342 folderview_freeze(mainwin
->folderview
);
1343 folder_item_update_freeze();
1345 if ((ret
= passwd_store_read_config()) < 0) {
1346 debug_print("Password store configuration file version upgrade failed (%d), exiting\n", ret
);
1353 prefs_account_init();
1354 account_read_config_all();
1356 if (prefs_update_config_version_accounts() < 0) {
1357 debug_print("Accounts configuration file version upgrade failed, exiting\n");
1364 #ifdef HAVE_LIBETPAN
1365 imap_main_init(prefs_common
.skip_ssl_cert_check
);
1366 imap_main_set_timeout(prefs_common
.io_timeout_secs
);
1367 nntp_main_init(prefs_common
.skip_ssl_cert_check
);
1369 /* If we can't read a folder list or don't have accounts,
1370 * it means the configuration's not done. Either this is
1371 * a brand new install, a failed/refused migration,
1372 * or a failed config_version upgrade.
1374 if ((ret
= folder_read_list()) < 0) {
1375 debug_print("Folderlist read failed (%d)\n", ret
);
1376 prefs_destroy_cache();
1379 /* config_version update failed in folder_read_list(). We
1380 * do not want to run the wizard, just exit. */
1381 debug_print("Folderlist version upgrade failed, exiting\n");
1388 /* if run_wizard returns FALSE it's because it's
1389 * been cancelled. We can't do much but exit.
1390 * however, if the user was asked for a migration,
1391 * we remove the newly created directory so that
1392 * he's asked again for migration on next launch.*/
1393 if (!run_wizard(mainwin
, TRUE
)) {
1394 if (asked_for_migration
)
1395 remove_dir_recursive(RC_DIR
);
1401 main_window_reflect_prefs_all_now();
1402 folder_write_list();
1406 if (!account_get_list()) {
1407 prefs_destroy_cache();
1408 if (!run_wizard(mainwin
, FALSE
)) {
1409 if (asked_for_migration
)
1410 remove_dir_recursive(RC_DIR
);
1416 if(!account_get_list()) {
1417 exit_claws(mainwin
);
1427 toolbar_main_set_sensitive(mainwin
);
1428 main_window_set_menu_sensitive(mainwin
);
1430 /* if crashed, show window early so that the user
1431 * sees what's happening */
1432 if (claws_crashed()) {
1433 main_window_popup(mainwin
);
1434 mainwin_shown
= TRUE
;
1437 account_set_missing_folder();
1438 folder_set_missing_folders();
1439 folderview_set(folderview
);
1441 prefs_matcher_read_config();
1442 quicksearch_set_search_strings(mainwin
->summaryview
->quicksearch
);
1444 /* make one all-folder processing before using claws */
1445 main_window_cursor_wait(mainwin
);
1446 folder_func_to_all_folders(initial_processing
, (gpointer
*)mainwin
);
1448 /* if claws crashed, rebuild caches */
1449 if (claws_crashed()) {
1451 debug_print("Claws Mail crashed, checking for new messages in local folders\n");
1452 folder_item_update_thaw();
1453 folderview_check_new(NULL
);
1454 folder_clean_cache_memory_force();
1455 folder_item_update_freeze();
1457 /* make the crash-indicator file */
1458 if (str_write_to_file("foo", get_crashfile_name(), FALSE
) < 0) {
1459 g_warning("Can't create the crash-indicator file.");
1462 inc_autocheck_timer_init(mainwin
);
1464 /* ignore SIGPIPE signal for preventing sudden death of program */
1466 signal(SIGPIPE
, SIG_IGN
);
1468 if (cmd
.online_mode
== ONLINE_MODE_OFFLINE
) {
1469 main_window_toggle_work_offline(mainwin
, TRUE
, FALSE
);
1471 if (cmd
.online_mode
== ONLINE_MODE_ONLINE
) {
1472 main_window_toggle_work_offline(mainwin
, FALSE
, FALSE
);
1475 if (cmd
.status_folders
) {
1476 g_ptr_array_free(cmd
.status_folders
, TRUE
);
1477 cmd
.status_folders
= NULL
;
1479 if (cmd
.status_full_folders
) {
1480 g_ptr_array_free(cmd
.status_full_folders
, TRUE
);
1481 cmd
.status_full_folders
= NULL
;
1484 claws_register_idle_function(claws_gtk_idle
);
1487 prefs_toolbar_init();
1489 num_folder_class
= g_list_length(folder_get_list());
1491 plugin_load_all("GTK2");
1493 if (g_list_length(folder_get_list()) != num_folder_class
) {
1494 debug_print("new folders loaded, reloading processing rules\n");
1495 prefs_matcher_read_config();
1498 if ((plug_list
= plugin_get_unloaded_list()) != NULL
) {
1501 gint num_plugins
= 0;
1502 for (cur
= plug_list
; cur
; cur
= cur
->next
) {
1503 Plugin
*plugin
= (Plugin
*)cur
->data
;
1504 gchar
*tmp
= g_strdup_printf("%s\n%s",
1506 plugin_get_name(plugin
));
1511 main_window_cursor_normal(mainwin
);
1512 main_window_popup(mainwin
);
1513 mainwin_shown
= TRUE
;
1514 alertpanel_warning(ngettext(
1515 "The following plugin failed to load. "
1516 "Check the Plugins configuration "
1517 "for more information:\n%s",
1518 "The following plugins failed to load. "
1519 "Check the Plugins configuration "
1520 "for more information:\n%s",
1523 main_window_cursor_wait(mainwin
);
1525 g_slist_free(plug_list
);
1529 prefs_common_write_config();
1530 plugin_load_standard_plugins ();
1533 /* if not crashed, show window now */
1534 if (!mainwin_shown
) {
1535 /* apart if something told not to show */
1536 if (show_at_startup
)
1537 main_window_popup(mainwin
);
1540 if (cmd
.geometry
!= NULL
) {
1541 if (!gtk_window_parse_geometry(GTK_WINDOW(mainwin
->window
), cmd
.geometry
))
1542 g_warning("failed to parse geometry '%s'", cmd
.geometry
);
1546 if (sscanf(cmd
.geometry
, "%ux%u+", &width
, &height
) == 2)
1547 gtk_window_resize(GTK_WINDOW(mainwin
->window
), width
, height
);
1549 g_warning("failed to parse geometry's width/height");
1553 if (!folder_have_mailbox()) {
1554 prefs_destroy_cache();
1555 main_window_cursor_normal(mainwin
);
1556 if (folder_get_list() != NULL
) {
1557 alertpanel_error(_("Claws Mail has detected a configured "
1558 "mailbox, but it is incomplete. It is "
1559 "possibly due to a failing IMAP account. Use "
1560 "\"Rebuild folder tree\" on the mailbox parent "
1561 "folder's context menu to try to fix it."));
1563 alertpanel_error(_("Claws Mail has detected a configured "
1564 "mailbox, but could not load it. It is "
1565 "probably provided by an out-of-date "
1566 "external plugin. Please reinstall the "
1567 "plugin and try again."));
1568 exit_claws(mainwin
);
1576 static_mainwindow
= mainwin
;
1578 #ifdef HAVE_STARTUP_NOTIFICATION
1579 startup_notification_complete(FALSE
);
1582 sc_session_manager_connect(mainwin
);
1585 folder_item_update_thaw();
1586 folderview_thaw(mainwin
->folderview
);
1587 main_window_cursor_normal(mainwin
);
1589 if (!cmd
.target
&& prefs_common
.goto_last_folder_on_startup
&&
1590 folder_find_item_from_identifier(prefs_common
.last_opened_folder
) != NULL
&&
1592 cmd
.target
= prefs_common
.last_opened_folder
;
1595 if (cmd
.receive_all
&& !cmd
.target
) {
1597 g_timeout_add(1000, defer_check_all
, GINT_TO_POINTER(FALSE
));
1598 } else if (prefs_common
.chk_on_startup
&& !cmd
.target
) {
1600 g_timeout_add(1000, defer_check_all
, GINT_TO_POINTER(TRUE
));
1601 } else if (cmd
.receive
&& !cmd
.target
) {
1603 g_timeout_add(1000, defer_check
, NULL
);
1605 folderview_grab_focus(folderview
);
1608 open_compose_new(cmd
.compose_mailto
, cmd
.attach_files
);
1610 if (cmd
.attach_files
) {
1611 list_free_strings_full(cmd
.attach_files
);
1612 cmd
.attach_files
= NULL
;
1614 if (cmd
.subscribe
) {
1615 folder_subscribe(cmd
.subscribe_uri
);
1624 g_timeout_add(500, defer_jump
, (gpointer
)cmd
.target
);
1627 prefs_destroy_cache();
1629 compose_reopen_exit_drafts();
1632 sc_starting
= FALSE
;
1633 main_window_set_menu_sensitive(mainwin
);
1634 toolbar_main_set_sensitive(mainwin
);
1637 /* register the callback of unix domain socket input */
1638 lock_socket_tag
= claws_input_add(lock_socket
,
1639 G_IO_IN
| G_IO_HUP
| G_IO_ERR
| G_IO_PRI
,
1640 lock_socket_input_cb
,
1647 #ifdef HAVE_NETWORKMANAGER_SUPPORT
1649 g_object_unref(nm_proxy
);
1651 #ifdef HAVE_DBUS_GLIB
1652 uninstall_dbus_status_handler();
1654 dbus_g_connection_unref(connection
);
1660 exit_claws(mainwin
);
1665 static void save_all_caches(FolderItem
*item
, gpointer data
)
1672 folder_item_close(item
);
1675 folder_item_free_cache(item
, TRUE
);
1678 static void exit_claws(MainWindow
*mainwin
)
1681 gboolean have_connectivity
;
1686 debug_print("shutting down\n");
1687 inc_autocheck_timer_remove();
1689 #ifdef HAVE_NETWORKMANAGER_SUPPORT
1690 if (prefs_common
.work_offline
&& went_offline_nm
)
1691 prefs_common
.work_offline
= FALSE
;
1694 /* save prefs for opened folder */
1695 if((item
= folderview_get_opened_item(mainwin
->folderview
)) != NULL
) {
1696 summary_save_prefs_to_folderitem(
1697 mainwin
->summaryview
, item
);
1698 if (prefs_common
.last_opened_folder
!= NULL
)
1699 g_free(prefs_common
.last_opened_folder
);
1700 prefs_common
.last_opened_folder
=
1701 !prefs_common
.goto_last_folder_on_startup
? NULL
:
1702 folder_item_get_identifier(item
);
1705 /* save all state before exiting */
1706 folder_func_to_all_folders(save_all_caches
, NULL
);
1707 folder_write_list();
1709 main_window_get_size(mainwin
);
1710 main_window_get_position(mainwin
);
1712 prefs_common_write_config();
1713 account_write_config_all();
1714 passwd_store_write_config();
1715 #ifndef USE_ALT_ADDRBOOK
1716 addressbook_export_to_file();
1718 filename
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, MENU_RC
, NULL
);
1719 gtk_accel_map_save(filename
);
1722 /* delete temporary files */
1723 remove_all_files(get_tmp_dir());
1724 remove_all_files(get_mime_tmp_dir());
1726 close_log_file(LOG_PROTOCOL
);
1727 close_log_file(LOG_DEBUG_FILTERING
);
1729 #ifdef HAVE_NETWORKMANAGER_SUPPORT
1730 have_connectivity
= networkmanager_is_online(NULL
);
1732 have_connectivity
= TRUE
;
1734 #ifdef HAVE_LIBETPAN
1735 imap_main_done(have_connectivity
);
1736 nntp_main_done(have_connectivity
);
1738 /* delete crashfile */
1740 claws_unlink(get_crashfile_name());
1742 lock_socket_remove();
1745 if (mainwin
->smc_conn
)
1746 SmcCloseConnection ((SmcConn
)mainwin
->smc_conn
, 0, NULL
);
1747 mainwin
->smc_conn
= NULL
;
1750 main_window_destroy_all();
1752 plugin_unload_all("GTK2");
1755 prefs_toolbar_done();
1758 #ifndef USE_ALT_ADDRBOOK
1759 addressbook_destroy();
1761 prefs_themes_done();
1763 prefs_ext_prog_done();
1764 prefs_wrapping_done();
1765 prefs_compose_writing_done();
1766 prefs_msg_colors_done();
1767 prefs_image_viewer_done();
1768 image_viewer_done();
1770 prefs_summaries_done();
1771 prefs_message_done();
1774 prefs_receive_done();
1775 prefs_logging_done();
1779 prefs_spelling_done();
1780 gtkaspell_checkers_quit();
1782 plugin_unload_all("Common");
1786 #define G_PRINT_EXIT(msg) \
1792 static GString
* parse_cmd_compose_from_file(const gchar
*fn
)
1794 GString
*headers
= g_string_new(NULL
);
1795 GString
*body
= g_string_new(NULL
);
1803 if (fn
== NULL
|| *fn
== '\0')
1804 G_PRINT_EXIT(_("Missing filename\n"));
1805 isstdin
= (*fn
== '-' && *(fn
+ 1) == '\0');
1809 fp
= claws_fopen(fn
, "r");
1811 G_PRINT_EXIT(_("Cannot open filename for reading\n"));
1814 while (claws_fgets(fb
, sizeof(fb
), fp
)) {
1820 while (*h
&& *h
!= ':') { ++h
; } /* search colon */
1822 G_PRINT_EXIT(_("Malformed header\n"));
1824 while (*v
&& *v
== ' ') { ++v
; } /* trim value start */
1826 tmp
= g_ascii_strdown(fb
, -1); /* get header name */
1827 if (!strcmp(tmp
, "to")) {
1829 G_PRINT_EXIT(_("Duplicated 'To:' header\n"));
1832 g_string_append_c(headers
, '&');
1833 g_string_append(headers
, tmp
);
1834 g_string_append_c(headers
, '=');
1835 g_string_append_uri_escaped(headers
, v
, NULL
, TRUE
);
1840 G_PRINT_EXIT(_("Missing required 'To:' header\n"));
1841 g_string_append(body
, to
);
1843 g_string_append(body
, "?body=");
1844 while (claws_fgets(fb
, sizeof(fb
), fp
)) {
1845 g_string_append_uri_escaped(body
, fb
, NULL
, TRUE
);
1849 /* append the remaining headers */
1850 g_string_append(body
, headers
->str
);
1851 g_string_free(headers
, TRUE
);
1858 static void parse_cmd_opt_error(char *errstr
, char* optstr
)
1862 cm_return_if_fail(errstr
!= NULL
);
1863 cm_return_if_fail(optstr
!= NULL
);
1865 g_snprintf(tmp
, sizeof(tmp
), errstr
, optstr
);
1866 g_print(_("%s. Try -h or --help for usage.\n"), tmp
);
1870 static void parse_cmd_opt(int argc
, char *argv
[])
1875 for (i
= 1; i
< argc
; i
++) {
1876 if (!strcmp(argv
[i
], "--receive-all")) {
1877 cmd
.receive_all
= TRUE
;
1878 } else if (!strcmp(argv
[i
], "--receive")) {
1880 } else if (!strcmp(argv
[i
], "--cancel-receiving")) {
1881 cmd
.cancel_receiving
= TRUE
;
1882 } else if (!strcmp(argv
[i
], "--cancel-sending")) {
1883 cmd
.cancel_sending
= TRUE
;
1884 } else if (!strcmp(argv
[i
], "--compose-from-file")) {
1886 const gchar
*p
= argv
[i
+1];
1888 GString
*mailto
= parse_cmd_compose_from_file(p
);
1890 cmd
.compose_mailto
= mailto
->str
;
1893 parse_cmd_opt_error(_("Missing file argument for option %s"), argv
[i
]);
1895 } else if (!strcmp(argv
[i
], "--compose")) {
1896 const gchar
*p
= (i
+1 < argc
)?argv
[i
+1]:NULL
;
1899 cmd
.compose_mailto
= NULL
;
1900 if (p
&& *p
!= '\0' && *p
!= '-') {
1901 if (!STRNCMP(p
, "mailto:")) {
1902 cmd
.compose_mailto
= p
+ 7;
1904 cmd
.compose_mailto
= p
;
1908 } else if (!strcmp(argv
[i
], "--subscribe")) {
1910 const gchar
*p
= argv
[i
+1];
1911 if (p
&& *p
!= '\0' && *p
!= '-') {
1912 cmd
.subscribe
= TRUE
;
1913 cmd
.subscribe_uri
= p
;
1915 parse_cmd_opt_error(_("Missing or empty uri argument for option %s"), argv
[i
]);
1918 parse_cmd_opt_error(_("Missing uri argument for option %s"), argv
[i
]);
1920 } else if (!strcmp(argv
[i
], "--attach") ||
1921 !strcmp(argv
[i
], "--insert")) {
1923 const gchar
*p
= argv
[i
+1];
1926 gboolean insert
= !strcmp(argv
[i
], "--insert");
1928 while (p
&& *p
!= '\0' && *p
!= '-') {
1929 if ((file
= g_filename_from_uri(p
, NULL
, NULL
)) != NULL
) {
1930 if (!is_file_exist(file
)) {
1935 if (file
== NULL
&& *p
!= G_DIR_SEPARATOR
) {
1936 file
= g_strconcat(claws_get_startup_dir(),
1939 } else if (file
== NULL
) {
1943 ainfo
= g_new0(AttachInfo
, 1);
1945 ainfo
->insert
= insert
;
1946 cmd
.attach_files
= g_list_append(cmd
.attach_files
, ainfo
);
1948 p
= (ii
+1 < argc
)?argv
[ii
+1]:NULL
;
1951 parse_cmd_opt_error(_("Missing at least one non-empty file argument for option %s"), argv
[i
]);
1956 parse_cmd_opt_error(_("Missing file argument for option %s"), argv
[i
]);
1958 } else if (!strcmp(argv
[i
], "--send")) {
1960 } else if (!strcmp(argv
[i
], "--version-full") ||
1961 !strcmp(argv
[i
], "-V")) {
1962 g_print("Claws Mail version " VERSION_GIT_FULL
"\n");
1963 main_dump_features_list(FALSE
);
1965 } else if (!strcmp(argv
[i
], "--version") ||
1966 !strcmp(argv
[i
], "-v")) {
1967 g_print("Claws Mail version " VERSION
"\n");
1969 } else if (!strcmp(argv
[i
], "--status-full")) {
1970 const gchar
*p
= (i
+1 < argc
)?argv
[i
+1]:NULL
;
1972 cmd
.status_full
= TRUE
;
1973 while (p
&& *p
!= '\0' && *p
!= '-') {
1974 if (!cmd
.status_full_folders
) {
1975 cmd
.status_full_folders
=
1978 g_ptr_array_add(cmd
.status_full_folders
,
1981 p
= (i
+1 < argc
)?argv
[i
+1]:NULL
;
1983 } else if (!strcmp(argv
[i
], "--status")) {
1984 const gchar
*p
= (i
+1 < argc
)?argv
[i
+1]:NULL
;
1987 while (p
&& *p
!= '\0' && *p
!= '-') {
1988 if (!cmd
.status_folders
)
1989 cmd
.status_folders
= g_ptr_array_new();
1990 g_ptr_array_add(cmd
.status_folders
,
1993 p
= (i
+1 < argc
)?argv
[i
+1]:NULL
;
1995 } else if (!strcmp(argv
[i
], "--search")) {
1996 if (i
+3 < argc
) { /* 3 first arguments are mandatory */
1998 /* only set search parameters if they are valid */
2000 cmd
.search_folder
= (p
&& *p
!= '\0' && *p
!= '-')?p
:NULL
;
2002 cmd
.search_type
= (p
&& *p
!= '\0' && *p
!= '-')?p
:NULL
;
2004 cmd
.search_request
= (p
&& *p
!= '\0' && *p
!= '-')?p
:NULL
;
2005 p
= (i
+4 < argc
)?argv
[i
+4]:NULL
;
2006 const char* rec
= (p
&& *p
!= '\0' && *p
!= '-')?p
:NULL
;
2007 cmd
.search_recursive
= TRUE
;
2010 if (tolower(*rec
)=='n' || tolower(*rec
)=='f' || *rec
=='0')
2011 cmd
.search_recursive
= FALSE
;
2013 if (cmd
.search_folder
&& cmd
.search_type
&& cmd
.search_request
) {
2020 parse_cmd_opt_error(_("Missing folder, type and request arguments for option %s"), argv
[i
]);
2023 parse_cmd_opt_error(_("Missing type and request arguments for option %s"), argv
[i
]);
2026 parse_cmd_opt_error(_("Missing request argument for option %s"), argv
[i
]);
2029 } else if (!strcmp(argv
[i
], "--online")) {
2030 cmd
.online_mode
= ONLINE_MODE_ONLINE
;
2031 } else if (!strcmp(argv
[i
], "--offline")) {
2032 cmd
.online_mode
= ONLINE_MODE_OFFLINE
;
2033 } else if (!strcmp(argv
[i
], "--toggle-debug")) {
2035 } else if (!strcmp(argv
[i
], "--statistics")) {
2036 cmd
.statistics
= TRUE
;
2037 } else if (!strcmp(argv
[i
], "--reset-statistics")) {
2038 cmd
.reset_statistics
= TRUE
;
2039 } else if (!strcmp(argv
[i
], "--help") ||
2040 !strcmp(argv
[i
], "-h")) {
2041 gchar
*base
= g_path_get_basename(argv
[0]);
2042 g_print(_("Usage: %s [OPTION]...\n"), base
);
2044 g_print("%s\n", _(" --compose [address] open composition window"));
2045 g_print("%s\n", _(" --compose-from-file file\n"
2046 " open composition window with data from given file;\n"
2047 " use - as file name for reading from standard input;\n"
2048 " content format: headers first (To: required) until an\n"
2049 " empty line, then mail body until end of file."));
2050 g_print("%s\n", _(" --subscribe uri subscribe to the given URI if possible"));
2051 g_print("%s\n", _(" --attach file1 [file2]...\n"
2052 " open composition window with specified files\n"
2054 g_print("%s\n", _(" --insert file1 [file2]...\n"
2055 " open composition window with specified files\n"
2057 g_print("%s\n", _(" --receive receive new messages"));
2058 g_print("%s\n", _(" --receive-all receive new messages of all accounts"));
2059 g_print("%s\n", _(" --cancel-receiving cancel receiving of messages"));
2060 g_print("%s\n", _(" --cancel-sending cancel sending of messages"));
2061 g_print("%s\n", _(" --search folder type request [recursive]\n"
2063 " folder ex.: \"#mh/Mailbox/inbox\" or \"Mail\"\n"
2064 " type: s[ubject],f[rom],t[o],e[xtended],m[ixed] or g: tag\n"
2065 " request: search string\n"
2066 " recursive: false if arg. starts with 0, n, N, f or F"));
2068 g_print("%s\n", _(" --send send all queued messages"));
2069 g_print("%s\n", _(" --status [folder]... show the total number of messages"));
2070 g_print("%s\n", _(" --status-full [folder]...\n"
2071 " show the status of each folder"));
2072 g_print("%s\n", _(" --statistics show session statistics"));
2073 g_print("%s\n", _(" --reset-statistics reset session statistics"));
2074 g_print("%s\n", _(" --select folder[/msg] jump to the specified folder/message\n"
2075 " folder is a folder id like 'folder/sub_folder', a file:// uri or an absolute path"));
2076 g_print("%s\n", _(" --online switch to online mode"));
2077 g_print("%s\n", _(" --offline switch to offline mode"));
2078 g_print("%s\n", _(" --exit --quit -q exit Claws Mail"));
2079 g_print("%s\n", _(" --debug -d debug mode"));
2080 g_print("%s\n", _(" --toggle-debug toggle debug mode"));
2081 g_print("%s\n", _(" --help -h display this help"));
2082 g_print("%s\n", _(" --version -v output version information"));
2083 g_print("%s\n", _(" --version-full -V output version and built-in features information"));
2084 g_print("%s\n", _(" --config-dir output configuration directory"));
2085 g_print("%s\n", _(" --alternate-config-dir directory\n"
2086 " use specified configuration directory"));
2087 g_print("%s\n", _(" --geometry -geometry [WxH][+X+Y]\n"
2088 " set geometry for main window"));
2092 } else if (!strcmp(argv
[i
], "--crash")) {
2094 cmd
.crash_params
= g_strdup((i
+1 < argc
)?argv
[i
+1]:NULL
);
2096 } else if (!strcmp(argv
[i
], "--config-dir")) {
2097 g_print(RC_DIR
"\n");
2099 } else if (!strcmp(argv
[i
], "--alternate-config-dir")) {
2101 set_rc_dir(argv
[i
+1]);
2104 parse_cmd_opt_error(_("Missing directory argument for option %s"), argv
[i
]);
2106 } else if (!strcmp(argv
[i
], "--geometry") ||
2107 !strcmp(argv
[i
], "-geometry")) {
2109 cmd
.geometry
= argv
[i
+1];
2112 parse_cmd_opt_error(_("Missing geometry argument for option %s"), argv
[i
]);
2114 } else if (!strcmp(argv
[i
], "--exit") ||
2115 !strcmp(argv
[i
], "--quit") ||
2116 !strcmp(argv
[i
], "-q")) {
2118 } else if (!strcmp(argv
[i
], "--select")) {
2120 cmd
.target
= argv
[i
+1];
2123 parse_cmd_opt_error(_("Missing folder argument for option %s"), argv
[i
]);
2125 } else if (i
== 1 && argc
== 2) {
2126 /* only one parameter. Do something intelligent about it */
2127 if ((strstr(argv
[i
], "@") || !STRNCMP(argv
[i
], "mailto:")) && !strstr(argv
[i
], "://")) {
2128 const gchar
*p
= argv
[i
];
2131 cmd
.compose_mailto
= NULL
;
2132 if (p
&& *p
!= '\0' && *p
!= '-') {
2133 if (!STRNCMP(p
, "mailto:")) {
2134 cmd
.compose_mailto
= p
+ 7;
2136 cmd
.compose_mailto
= p
;
2139 } else if (!STRNCMP(argv
[i
], "file://")) {
2140 cmd
.target
= argv
[i
];
2141 } else if (!STRNCMP(argv
[i
], "?attach=file://")) {
2142 /* Thunar support as per 3.3.0cvs19 */
2144 cmd
.compose_mailto
= argv
[i
];
2145 } else if (strstr(argv
[i
], "://")) {
2146 const gchar
*p
= argv
[i
];
2147 if (p
&& *p
!= '\0' && *p
!= '-') {
2148 cmd
.subscribe
= TRUE
;
2149 cmd
.subscribe_uri
= p
;
2151 } else if (!strcmp(argv
[i
], "--sync")) {
2153 } else if (is_dir_exist(argv
[i
]) || is_file_exist(argv
[i
])) {
2154 cmd
.target
= argv
[i
];
2156 parse_cmd_opt_error(_("Unknown option %s"), argv
[i
]);
2159 parse_cmd_opt_error(_("Unknown option %s"), argv
[i
]);
2163 if (cmd
.attach_files
&& cmd
.compose
== FALSE
) {
2165 cmd
.compose_mailto
= NULL
;
2169 static void initial_processing(FolderItem
*item
, gpointer data
)
2171 MainWindow
*mainwin
= (MainWindow
*)data
;
2174 cm_return_if_fail(item
);
2175 buf
= g_strdup_printf(_("Processing (%s)..."),
2178 : _("top level folder"));
2181 if (folder_item_parent(item
) != NULL
&& item
->prefs
->enable_processing
) {
2182 item
->processing_pending
= TRUE
;
2183 folder_item_apply_processing(item
);
2184 item
->processing_pending
= FALSE
;
2187 STATUSBAR_POP(mainwin
);
2190 static gboolean
draft_all_messages(void)
2192 const GList
*compose_list
= NULL
;
2194 compose_clear_exit_drafts();
2195 compose_list
= compose_get_compose_list();
2196 while (compose_list
!= NULL
) {
2197 Compose
*c
= (Compose
*)compose_list
->data
;
2198 if (!compose_draft(c
, COMPOSE_DRAFT_FOR_EXIT
))
2200 compose_list
= compose_get_compose_list();
2204 gboolean
clean_quit(gpointer data
)
2206 static gboolean firstrun
= TRUE
;
2213 /*!< Good idea to have the main window stored in a
2214 * static variable so we can check that variable
2215 * to see if we're really allowed to do things
2216 * that actually the spawner is supposed to
2217 * do (like: sending mail, composing messages).
2218 * Because, really, if we're the spawnee, and
2219 * we touch GTK stuff, we're hosed. See the
2222 /* FIXME: Use something else to signal that we're
2223 * in the original spawner, and not in a spawned
2225 if (!static_mainwindow
) {
2229 draft_all_messages();
2230 emergency_exit
= TRUE
;
2231 exit_claws(static_mainwindow
);
2237 void app_will_exit(GtkWidget
*widget
, gpointer data
)
2239 MainWindow
*mainwin
= data
;
2241 if (gtk_main_level() == 0) {
2242 debug_print("not even started\n");
2245 if (sc_exiting
== TRUE
) {
2246 debug_print("exit pending\n");
2250 debug_print("exiting\n");
2251 if (compose_get_compose_list()) {
2252 if (!draft_all_messages()) {
2253 main_window_popup(mainwin
);
2259 if (prefs_common
.warn_queued_on_exit
&& procmsg_have_queued_mails_fast()) {
2260 if (alertpanel(_("Queued messages"),
2261 _("Some unsent messages are queued. Exit now?"),
2262 GTK_STOCK_CANCEL
, GTK_STOCK_OK
, NULL
, ALERTFOCUS_FIRST
)
2263 != G_ALERTALTERNATE
) {
2264 main_window_popup(mainwin
);
2268 manage_window_focus_in(mainwin
->window
, NULL
, NULL
);
2272 #ifdef HAVE_VALGRIND
2273 if (RUNNING_ON_VALGRIND
) {
2274 summary_clear_list(mainwin
->summaryview
);
2277 if (folderview_get_selected_item(mainwin
->folderview
))
2278 folder_item_close(folderview_get_selected_item(mainwin
->folderview
));
2282 gboolean
claws_is_exiting(void)
2287 gboolean
claws_is_starting(void)
2294 * CLAWS: want this public so crash dialog can delete the
2297 gchar
*claws_get_socket_name(void)
2299 static gchar
*filename
= NULL
;
2300 gchar
*socket_dir
= NULL
;
2303 if (filename
== NULL
) {
2307 socket_dir
= g_strdup_printf("%s%cclaws-mail",
2308 g_get_user_runtime_dir(), G_DIR_SEPARATOR
);
2309 stat_ok
= g_stat(socket_dir
, &st
);
2310 if (stat_ok
< 0 && errno
!= ENOENT
) {
2311 g_print("Error stat'ing socket_dir %s: %s\n",
2312 socket_dir
, g_strerror(errno
));
2313 } else if (stat_ok
== 0 && S_ISSOCK(st
.st_mode
)) {
2314 /* old versions used a sock in $TMPDIR/claws-mail-$UID */
2315 debug_print("Using legacy socket %s\n", socket_dir
);
2316 filename
= g_strdup(socket_dir
);
2320 if (!is_dir_exist(socket_dir
) && make_dir(socket_dir
) < 0) {
2321 g_print("Error creating socket_dir %s: %s\n",
2322 socket_dir
, g_strerror(errno
));
2325 md5_hex_digest(md5sum
, get_rc_dir());
2327 filename
= g_strdup_printf("%s%c%s", socket_dir
, G_DIR_SEPARATOR
,
2330 debug_print("Using control socket %s\n", filename
);
2337 static gchar
*get_crashfile_name(void)
2339 static gchar
*filename
= NULL
;
2341 if (filename
== NULL
) {
2342 filename
= g_strdup_printf("%s%cclaws-crashed",
2343 get_tmp_dir(), G_DIR_SEPARATOR
);
2349 static gint
prohibit_duplicate_launch(void)
2356 path
= claws_get_socket_name();
2357 /* Try to connect to the control socket */
2358 sock
= fd_connect_unix(path
);
2360 if (x_display
== NULL
)
2361 x_display
= g_strdup(g_getenv("DISPLAY"));
2368 /* If connect failed, no other process is running.
2369 * Unlink the potentially existing socket, then
2370 * open it. This has to be done locking a temporary
2371 * file to avoid the race condition where another
2372 * process could have created the socket just in
2375 socket_lock
= g_strconcat(path
, ".lock",
2377 lock_fd
= g_open(socket_lock
, O_RDWR
|O_CREAT
, 0);
2379 debug_print("Couldn't open %s: %s (%d)\n", socket_lock
,
2380 g_strerror(errno
), errno
);
2381 g_free(socket_lock
);
2384 if (flock(lock_fd
, LOCK_EX
) < 0) {
2385 debug_print("Couldn't lock %s: %s (%d)\n", socket_lock
,
2386 g_strerror(errno
), errno
);
2388 g_free(socket_lock
);
2394 debug_print("Opening socket %s\n", path
);
2395 ret
= fd_open_unix(path
);
2397 flock(lock_fd
, LOCK_UN
);
2399 claws_unlink(socket_lock
);
2400 g_free(socket_lock
);
2407 hmutex
= CreateMutexA(NULL
, FALSE
, "ClawsMail");
2409 debug_print("cannot create Mutex\n");
2412 if (GetLastError() != ERROR_ALREADY_EXISTS
) {
2413 sock
= fd_open_inet(50216);
2419 sock
= fd_connect_inet(50216);
2423 /* remote command mode */
2425 debug_print("another Claws Mail instance is already running.\n");
2427 if (cmd
.receive_all
) {
2428 CM_FD_WRITE_ALL("receive_all\n");
2429 } else if (cmd
.receive
) {
2430 CM_FD_WRITE_ALL("receive\n");
2431 } else if (cmd
.cancel_receiving
) {
2432 CM_FD_WRITE_ALL("cancel_receiving\n");
2433 } else if (cmd
.cancel_sending
) {
2434 CM_FD_WRITE_ALL("cancel_sending\n");
2435 } else if (cmd
.compose
&& cmd
.attach_files
) {
2436 gchar
*str
, *compose_str
;
2438 if (cmd
.compose_mailto
) {
2439 compose_str
= g_strdup_printf("compose_attach %s\n",
2440 cmd
.compose_mailto
);
2442 compose_str
= g_strdup("compose_attach\n");
2445 CM_FD_WRITE_ALL(compose_str
);
2446 g_free(compose_str
);
2448 for (curr
= cmd
.attach_files
; curr
!= NULL
; curr
= curr
->next
) {
2449 str
= (gchar
*) ((AttachInfo
*)curr
->data
)->file
;
2450 if (((AttachInfo
*)curr
->data
)->insert
)
2451 CM_FD_WRITE_ALL("insert ");
2453 CM_FD_WRITE_ALL("attach ");
2454 CM_FD_WRITE_ALL(str
);
2455 CM_FD_WRITE_ALL("\n");
2458 CM_FD_WRITE_ALL(".\n");
2459 } else if (cmd
.compose
) {
2462 if (cmd
.compose_mailto
) {
2463 compose_str
= g_strdup_printf
2464 ("compose %s\n", cmd
.compose_mailto
);
2466 compose_str
= g_strdup("compose\n");
2469 CM_FD_WRITE_ALL(compose_str
);
2470 g_free(compose_str
);
2471 } else if (cmd
.subscribe
) {
2472 gchar
*str
= g_strdup_printf("subscribe %s\n", cmd
.subscribe_uri
);
2473 CM_FD_WRITE_ALL(str
);
2475 } else if (cmd
.send
) {
2476 CM_FD_WRITE_ALL("send\n");
2477 } else if (cmd
.online_mode
== ONLINE_MODE_ONLINE
) {
2478 CM_FD_WRITE("online\n");
2479 } else if (cmd
.online_mode
== ONLINE_MODE_OFFLINE
) {
2480 CM_FD_WRITE("offline\n");
2481 } else if (cmd
.debug
) {
2482 CM_FD_WRITE("debug\n");
2483 } else if (cmd
.status
|| cmd
.status_full
) {
2484 gchar buf
[BUFFSIZE
];
2486 const gchar
*command
;
2490 command
= cmd
.status_full
? "status-full\n" : "status\n";
2491 folders
= cmd
.status_full
? cmd
.status_full_folders
:
2494 CM_FD_WRITE_ALL(command
);
2495 for (i
= 0; folders
&& i
< folders
->len
; ++i
) {
2496 folder
= g_ptr_array_index(folders
, i
);
2497 CM_FD_WRITE_ALL(folder
);
2498 CM_FD_WRITE_ALL("\n");
2500 CM_FD_WRITE_ALL(".\n");
2502 fd_gets(sock
, buf
, sizeof(buf
) - 1);
2503 buf
[sizeof(buf
) - 1] = '\0';
2504 if (!STRNCMP(buf
, ".\n")) break;
2505 if (claws_fputs(buf
, stdout
) == EOF
) {
2506 g_warning("writing to stdout failed.");
2510 } else if (cmd
.exit
) {
2511 CM_FD_WRITE_ALL("exit\n");
2512 } else if (cmd
.statistics
) {
2514 CM_FD_WRITE("statistics\n");
2516 fd_gets(sock
, buf
, sizeof(buf
) - 1);
2517 buf
[sizeof(buf
) - 1] = '\0';
2518 if (!STRNCMP(buf
, ".\n")) break;
2519 if (claws_fputs(buf
, stdout
) == EOF
) {
2520 g_warning("writing to stdout failed.");
2524 } else if (cmd
.reset_statistics
) {
2525 CM_FD_WRITE("reset_statistics\n");
2526 } else if (cmd
.target
) {
2527 gchar
*str
= g_strdup_printf("select %s\n", cmd
.target
);
2528 CM_FD_WRITE_ALL(str
);
2530 } else if (cmd
.search
) {
2531 gchar buf
[BUFFSIZE
];
2533 g_strdup_printf("search %s\n%s\n%s\n%c\n",
2534 cmd
.search_folder
, cmd
.search_type
, cmd
.search_request
,
2535 (cmd
.search_recursive
==TRUE
)?'1':'0');
2536 CM_FD_WRITE_ALL(str
);
2539 fd_gets(sock
, buf
, sizeof(buf
) - 1);
2540 buf
[sizeof(buf
) - 1] = '\0';
2541 if (!STRNCMP(buf
, ".\n")) break;
2542 if (claws_fputs(buf
, stdout
) == EOF
) {
2543 g_warning("writing to stdout failed.");
2550 CM_FD_WRITE_ALL("get_display\n");
2551 memset(buf
, 0, sizeof(buf
));
2552 fd_gets(sock
, buf
, sizeof(buf
) - 1);
2553 buf
[sizeof(buf
) - 1] = '\0';
2554 if (g_strcmp0(buf
, x_display
)) {
2555 g_print("Claws Mail is already running on display %s.\n",
2559 sock
= fd_connect_unix(path
);
2560 CM_FD_WRITE_ALL("popup\n");
2563 CM_FD_WRITE_ALL("popup\n");
2571 static gint
lock_socket_remove(void)
2574 gchar
*filename
, *dirname
;
2576 if (lock_socket
< 0) {
2580 if (lock_socket_tag
> 0) {
2581 g_source_remove(lock_socket_tag
);
2583 fd_close(lock_socket
);
2586 filename
= claws_get_socket_name();
2587 dirname
= g_path_get_dirname(filename
);
2588 claws_unlink(filename
);
2596 static GPtrArray
*get_folder_item_list(gint sock
)
2598 gchar buf
[BUFFSIZE
];
2600 GPtrArray
*folders
= NULL
;
2603 fd_gets(sock
, buf
, sizeof(buf
) - 1);
2604 buf
[sizeof(buf
) - 1] = '\0';
2605 if (!STRNCMP(buf
, ".\n")) {
2610 folders
= g_ptr_array_new();
2612 item
= folder_find_item_from_identifier(buf
);
2614 g_ptr_array_add(folders
, item
);
2616 g_warning("no such folder: %s", buf
);
2623 static void lock_socket_input_cb(gpointer data
,
2625 GIOCondition condition
)
2627 MainWindow
*mainwin
= (MainWindow
*)data
;
2629 gchar buf
[BUFFSIZE
];
2631 sock
= fd_accept(source
);
2635 fd_gets(sock
, buf
, sizeof(buf
) - 1);
2636 buf
[sizeof(buf
) - 1] = '\0';
2638 if (!STRNCMP(buf
, "popup")) {
2639 main_window_popup(mainwin
);
2641 } else if (!STRNCMP(buf
, "get_display")) {
2642 CM_FD_WRITE_ALL(x_display
);
2644 } else if (!STRNCMP(buf
, "receive_all")) {
2645 inc_all_account_mail(mainwin
, FALSE
, FALSE
,
2646 prefs_common
.newmail_notify_manu
);
2647 } else if (!STRNCMP(buf
, "receive")) {
2648 inc_mail(mainwin
, prefs_common
.newmail_notify_manu
);
2649 } else if (!STRNCMP(buf
, "cancel_receiving")) {
2652 } else if (!STRNCMP(buf
, "cancel_sending")) {
2654 } else if (!STRNCMP(buf
, "compose_attach")) {
2655 GList
*files
= NULL
, *curr
;
2659 mailto
= g_strdup(buf
+ strlen("compose_attach") + 1);
2660 while (fd_gets(sock
, buf
, sizeof(buf
) - 1) > 0) {
2661 buf
[sizeof(buf
) - 1] = '\0';
2663 if (!g_strcmp0(buf
, "."))
2666 ainfo
= g_new0(AttachInfo
, 1);
2667 ainfo
->file
= g_strdup(strstr(buf
, " ") + 1);
2668 ainfo
->insert
= !STRNCMP(buf
, "insert ");
2669 files
= g_list_append(files
, ainfo
);
2671 open_compose_new(mailto
, files
);
2673 curr
= g_list_first(files
);
2674 while (curr
!= NULL
) {
2675 ainfo
= (AttachInfo
*)curr
->data
;
2676 g_free(ainfo
->file
);
2682 } else if (!STRNCMP(buf
, "compose")) {
2683 open_compose_new(buf
+ strlen("compose") + 1, NULL
);
2684 } else if (!STRNCMP(buf
, "subscribe")) {
2685 main_window_popup(mainwin
);
2686 folder_subscribe(buf
+ strlen("subscribe") + 1);
2687 } else if (!STRNCMP(buf
, "send")) {
2689 } else if (!STRNCMP(buf
, "online")) {
2690 main_window_toggle_work_offline(mainwin
, FALSE
, FALSE
);
2691 } else if (!STRNCMP(buf
, "offline")) {
2692 main_window_toggle_work_offline(mainwin
, TRUE
, FALSE
);
2693 } else if (!STRNCMP(buf
, "debug")) {
2694 debug_set_mode(debug_get_mode() ? FALSE
: TRUE
);
2695 } else if (!STRNCMP(buf
, "status-full") ||
2696 !STRNCMP(buf
, "status")) {
2700 folders
= get_folder_item_list(sock
);
2701 status
= folder_get_status
2702 (folders
, !STRNCMP(buf
, "status-full"));
2703 CM_FD_WRITE_ALL(status
);
2704 CM_FD_WRITE_ALL(".\n");
2706 if (folders
) g_ptr_array_free(folders
, TRUE
);
2707 } else if (!STRNCMP(buf
, "statistics")) {
2710 g_snprintf(tmp
, sizeof(tmp
), _("Session statistics\n"));
2711 CM_FD_WRITE_ALL(tmp
);
2713 if (prefs_common
.date_format
) {
2718 lt
= localtime(&session_stats
.time_started
);
2719 fast_strftime(date
, len
, prefs_common
.date_format
, lt
);
2720 g_snprintf(tmp
, sizeof(tmp
), _("Started: %s\n"),
2721 lt
? date
: ctime(&session_stats
.time_started
));
2723 g_snprintf(tmp
, sizeof(tmp
), _("Started: %s\n"),
2724 ctime(&session_stats
.time_started
));
2725 CM_FD_WRITE_ALL(tmp
);
2727 CM_FD_WRITE_ALL("\n");
2729 g_snprintf(tmp
, sizeof(tmp
), _("Incoming traffic\n"));
2730 CM_FD_WRITE_ALL(tmp
);
2732 g_snprintf(tmp
, sizeof(tmp
), _("Received messages: %d\n"),
2733 session_stats
.received
);
2734 CM_FD_WRITE_ALL(tmp
);
2736 CM_FD_WRITE_ALL("\n");
2738 g_snprintf(tmp
, sizeof(tmp
), _("Outgoing traffic\n"));
2739 CM_FD_WRITE_ALL(tmp
);
2741 g_snprintf(tmp
, sizeof(tmp
), _("New/redirected messages: %d\n"),
2742 session_stats
.sent
);
2743 CM_FD_WRITE_ALL(tmp
);
2745 g_snprintf(tmp
, sizeof(tmp
), _("Replied messages: %d\n"),
2746 session_stats
.replied
);
2747 CM_FD_WRITE_ALL(tmp
);
2749 g_snprintf(tmp
, sizeof(tmp
), _("Forwarded messages: %d\n"),
2750 session_stats
.forwarded
);
2751 CM_FD_WRITE_ALL(tmp
);
2753 g_snprintf(tmp
, sizeof(tmp
), _("Total outgoing messages: %d\n"),
2754 (session_stats
.sent
+ session_stats
.replied
+
2755 session_stats
.forwarded
));
2756 CM_FD_WRITE_ALL(tmp
);
2758 CM_FD_WRITE_ALL(".\n");
2759 } else if (!STRNCMP(buf
, "reset_statistics")) {
2761 } else if (!STRNCMP(buf
, "select ")) {
2762 const gchar
*target
= buf
+7;
2763 mainwindow_jump_to(target
, TRUE
);
2764 } else if (!STRNCMP(buf
, "search ")) {
2765 FolderItem
* folderItem
= NULL
;
2766 GSList
*messages
= NULL
;
2767 gchar
*folder_name
= NULL
;
2768 gchar
*request
= NULL
;
2769 AdvancedSearch
*search
;
2771 AdvancedSearchType searchType
= ADVANCED_SEARCH_EXTENDED
;
2773 search
= advsearch_new();
2775 folder_name
= g_strdup(buf
+7);
2776 strretchomp(folder_name
);
2778 if (fd_gets(sock
, buf
, sizeof(buf
) - 1) <= 0)
2780 buf
[sizeof(buf
) - 1] = '\0';
2782 switch (toupper(buf
[0])) {
2783 case 'S': searchType
= ADVANCED_SEARCH_SUBJECT
; break;
2784 case 'F': searchType
= ADVANCED_SEARCH_FROM
; break;
2785 case 'T': searchType
= ADVANCED_SEARCH_TO
; break;
2786 case 'M': searchType
= ADVANCED_SEARCH_MIXED
; break;
2787 case 'G': searchType
= ADVANCED_SEARCH_TAG
; break;
2788 case 'E': searchType
= ADVANCED_SEARCH_EXTENDED
; break;
2791 if (fd_gets(sock
, buf
, sizeof(buf
) - 1) <= 0)
2794 buf
[sizeof(buf
) - 1] = '\0';
2795 request
= g_strdup(buf
);
2796 strretchomp(request
);
2799 if (fd_gets(sock
, buf
, sizeof(buf
) - 1) > 0)
2800 recursive
= buf
[0] != '0';
2802 buf
[sizeof(buf
) - 1] = '\0';
2804 debug_print("search: %s %i %s %i\n", folder_name
, searchType
, request
, recursive
);
2806 folderItem
= folder_find_item_from_identifier(folder_name
);
2808 if (folderItem
== NULL
) {
2809 debug_print("Unknown folder item : '%s', searching folder\n",folder_name
);
2810 Folder
* folder
= folder_find_from_path(folder_name
);
2812 folderItem
= FOLDER_ITEM(folder
->node
->data
);
2814 debug_print("Unknown folder: '%s'\n",folder_name
);
2816 debug_print("%s %s\n",folderItem
->name
, folderItem
->path
);
2819 if (folderItem
!= NULL
) {
2820 advsearch_set(search
, searchType
, request
);
2821 advsearch_search_msgs_in_folders(search
, &messages
, folderItem
, recursive
);
2823 g_print("Folder '%s' not found.\n'", folder_name
);
2827 for (cur
= messages
; cur
!= NULL
; cur
= cur
->next
) {
2828 MsgInfo
* msg
= (MsgInfo
*)cur
->data
;
2829 gchar
*file
= procmsg_get_message_file_path(msg
);
2830 CM_FD_WRITE_ALL(file
);
2831 CM_FD_WRITE_ALL("\n");
2834 CM_FD_WRITE_ALL(".\n");
2837 g_free(folder_name
);
2839 advsearch_free(search
);
2840 if (messages
!= NULL
)
2841 procmsg_msg_list_free(messages
);
2842 } else if (!STRNCMP(buf
, "exit")) {
2843 if (prefs_common
.clean_on_exit
&& !prefs_common
.ask_on_clean
) {
2844 procmsg_empty_all_trash();
2846 app_will_exit(NULL
, mainwin
);
2852 static void open_compose_new(const gchar
*address
, GList
*attach_files
)
2857 Xstrdup_a(addr
, address
, return);
2861 compose_new(NULL
, addr
, attach_files
);
2864 static void send_queue(void)
2867 gchar
*errstr
= NULL
;
2868 gboolean error
= FALSE
;
2869 for (list
= folder_get_list(); list
!= NULL
; list
= list
->next
) {
2870 Folder
*folder
= list
->data
;
2872 if (folder
->queue
) {
2873 gint res
= procmsg_send_queue
2874 (folder
->queue
, prefs_common
.savemsg
,
2878 folder_item_scan(folder
->queue
);
2886 alertpanel_error_log(_("Some errors occurred "
2887 "while sending queued messages:\n%s"), errstr
);
2890 alertpanel_error_log("Some errors occurred "
2891 "while sending queued messages.");
2895 static void quit_signal_handler(int sig
)
2897 debug_print("Quitting on signal %d\n", sig
);
2899 g_timeout_add(0, clean_quit
, NULL
);
2902 static void install_basic_sighandlers()
2906 struct sigaction act
;
2911 sigaddset(&mask
, SIGTERM
);
2914 sigaddset(&mask
, SIGINT
);
2917 sigaddset(&mask
, SIGHUP
);
2920 act
.sa_handler
= quit_signal_handler
;
2925 sigaction(SIGTERM
, &act
, 0);
2928 sigaction(SIGINT
, &act
, 0);
2931 sigaction(SIGHUP
, &act
, 0);
2934 sigprocmask(SIG_UNBLOCK
, &mask
, 0);
2935 #endif /* !G_OS_WIN32 */
2938 #if (defined linux && defined SIGIO)
2939 static int mem_notify_fd
= 0;
2941 static gboolean
clean_caches(gpointer unused
)
2943 if (static_mainwindow
&& static_mainwindow
->lock_count
> 0)
2945 debug_print("/dev/mem_notify: callback: Freeing some memory now!\n");
2946 folder_clean_cache_memory_force();
2950 static void memory_signal_handler(int sig
)
2952 debug_print("/dev/mem_notify: Kernel says we should free up some memory!\n");
2953 g_timeout_add(10, clean_caches
, NULL
);
2956 static void install_memory_sighandler()
2959 struct sigaction act
;
2962 mem_notify_fd
= g_open("/dev/mem_notify", O_RDONLY
|O_NONBLOCK
, 0);
2963 if (mem_notify_fd
== -1) {
2964 debug_print("/dev/mem_notify not available (%s)\n",
2969 fcntl(mem_notify_fd
, F_SETOWN
, getpid());
2970 flags
= fcntl(mem_notify_fd
, F_GETFL
);
2971 fcntl(mem_notify_fd
, flags
|FASYNC
);
2975 sigaddset(&mask
, SIGIO
);
2977 act
.sa_handler
= memory_signal_handler
;
2981 sigaction(SIGIO
, &act
, 0);
2983 sigprocmask(SIG_UNBLOCK
, &mask
, 0);
2985 debug_print("/dev/mem_notify: installed handler\n");
2987 #endif /* linux && SIGIO */
2989 #ifdef HAVE_NETWORKMANAGER_SUPPORT
2990 static void networkmanager_state_change_cb(DBusGProxy
*proxy
, gchar
*dev
,
2993 MainWindow
*mainWin
;
2996 if (static_mainwindow
)
2997 mainWin
= static_mainwindow
;
2999 mainWin
= (MainWindow
*)data
;
3001 if (!prefs_common
.use_networkmanager
)
3005 GError
*error
= NULL
;
3008 online
= networkmanager_is_online(&error
);
3010 if(online
&& went_offline_nm
) {
3011 went_offline_nm
= FALSE
;
3012 main_window_toggle_work_offline(mainWin
, FALSE
, FALSE
);
3013 debug_print("NetworkManager: Went online\n");
3014 log_message(LOG_PROTOCOL
, _("NetworkManager: network is online.\n"));
3017 went_offline_nm
= TRUE
;
3018 main_window_toggle_work_offline(mainWin
, TRUE
, FALSE
);
3019 debug_print("NetworkManager: Went offline\n");
3020 log_message(LOG_PROTOCOL
, _("NetworkManager: network is offline.\n"));
3024 debug_print("Failed to get online information from NetworkManager: %s\n",
3026 g_error_free(error
);
3030 debug_print("NetworkManager: Cannot change connection state because "
3031 "main window does not exist\n");
3034 /* Returns true (and sets error appropriately, if given) in case of error */
3035 gboolean
networkmanager_is_online(GError
**error
)
3037 DBusGConnection
*connection
;
3039 GError
*tmp_error
= NULL
;
3043 if (!prefs_common
.use_networkmanager
)
3048 connection
= dbus_g_bus_get(DBUS_BUS_SYSTEM
, &tmp_error
);
3051 /* If calling code doesn't do error checking, at least print some debug */
3052 if((error
== NULL
) || (*error
== NULL
))
3053 debug_print("Failed to open connection to system bus: %s\n",
3054 tmp_error
->message
);
3055 g_propagate_error(error
, tmp_error
);
3059 proxy
= dbus_g_proxy_new_for_name(connection
,
3060 "org.freedesktop.NetworkManager",
3061 "/org/freedesktop/NetworkManager",
3062 "org.freedesktop.NetworkManager");
3064 retVal
= dbus_g_proxy_call(proxy
,"state",&tmp_error
, G_TYPE_INVALID
,
3065 G_TYPE_UINT
, &state
, G_TYPE_INVALID
);
3068 g_object_unref(proxy
);
3070 dbus_g_connection_unref(connection
);
3073 /* If calling code doesn't do error checking, at least print some debug */
3074 if((error
== NULL
) || (*error
== NULL
))
3075 debug_print("Failed to get state info from NetworkManager: %s\n",
3076 tmp_error
->message
);
3077 g_propagate_error(error
, tmp_error
);
3080 return (state
== NM_STATE_CONNECTED_LOCAL
||
3081 state
== NM_STATE_CONNECTED_SITE
||
3082 state
== NM_STATE_CONNECTED_GLOBAL
||
3083 state
== NM_STATE_UNKNOWN
);