talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

main.c (42206B)


      1 /*
      2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
      3  * Copyright (C) 1999-2024 the Claws Mail team and Hiroyuki Yamamoto
      4  *
      5  * This program is free software; you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 3 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  * GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
     17  */
     18 
     19 #include "common/defs.h"
     20 
     21 #include <glib.h>
     22 #include <glib/gi18n.h>
     23 #include <gtk/gtk.h>
     24 
     25 #include <ctype.h>
     26 #include <err.h>
     27 #include <errno.h>
     28 #include <fcntl.h>
     29 #include <libgen.h>
     30 #include <signal.h>
     31 #include <stdbool.h>
     32 #include <stdio.h>
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <sys/file.h>
     36 #include <sys/stat.h>
     37 #include <sys/types.h>
     38 #include <time.h>
     39 #include <unistd.h>
     40 
     41 #include "claws.h"
     42 #include "main.h"
     43 #include "mainwindow.h"
     44 #include "folderview.h"
     45 #include "summaryview.h"
     46 #include "prefs_common.h"
     47 #include "prefs_account.h"
     48 #include "prefs_actions.h"
     49 #include "prefs_ext_prog.h"
     50 #include "prefs_message.h"
     51 #include "prefs_receive.h"
     52 #include "prefs_summaries.h"
     53 #include "prefs_other.h"
     54 #include "prefs_send.h"
     55 #include "prefs_compose_writing.h"
     56 #include "prefs_display_header.h"
     57 #include "account.h"
     58 #include "procmsg.h"
     59 #include "inc.h"
     60 #include "imap.h"
     61 #include "send_message.h"
     62 #include "manage_window.h"
     63 #include "alertpanel.h"
     64 #include "statusbar.h"
     65 #include "addressbook.h"
     66 #include "compose.h"
     67 #include "folder.h"
     68 #include "folder_item_prefs.h"
     69 #include "setup.h"
     70 #include "utils.h"
     71 #include "gtkutils.h"
     72 #include "socket.h"
     73 #include "log.h"
     74 #include "prefs_toolbar.h"
     75 #include "mh_gtk.h"
     76 #include "imap_gtk.h"
     77 #include "hooks.h"
     78 #include "menu.h"
     79 #include "passwordstore.h"
     80 #include "file-utils.h"
     81 
     82 #include "etpan/imap-thread.h"
     83 #include "stock_pixmap.h"
     84 #include "ssl.h"
     85 
     86 #include "version.h"
     87 
     88 
     89 gchar *prog_version;
     90 
     91 static gint lock_socket = -1;
     92 static gint lock_socket_tag = 0;
     93 
     94 typedef enum
     95 {
     96 	ONLINE_MODE_DONT_CHANGE,
     97  	ONLINE_MODE_ONLINE,
     98 	ONLINE_MODE_OFFLINE
     99 } OnlineMode;
    100 
    101 static struct RemoteCmd {
    102 	gboolean receive;
    103 	gboolean receive_all;
    104 	gboolean cancel_receiving;
    105 	gboolean cancel_sending;
    106 	gboolean compose;
    107 	const gchar *compose_mailto;
    108 	GList *attach_files;
    109 	gboolean search;
    110 	const gchar *search_folder;
    111 	const gchar *search_type;
    112 	const gchar *search_request;
    113 	gboolean search_recursive;
    114 	gboolean status;
    115 	gboolean status_full;
    116 	GPtrArray *status_folders;
    117 	GPtrArray *status_full_folders;
    118 	gboolean send;
    119 	int online_mode;
    120 	gboolean exit;
    121 	gboolean subscribe;
    122 	const gchar *subscribe_uri;
    123 	const gchar *target;
    124 	gboolean debug;
    125 	const gchar *geometry;
    126 } cmd;
    127 
    128 static void parse_cmd_opt(int argc, char *argv[]);
    129 
    130 static gint prohibit_duplicate_launch	(int		*argc,
    131 					 char		***argv);
    132 static gint lock_socket_remove		(void);
    133 static void lock_socket_input_cb	(gpointer	   data,
    134 					 gint		   source,
    135 					 GIOCondition      condition);
    136 
    137 static void open_compose_new		(const gchar	*address,
    138 					 GList		*attach_files);
    139 
    140 static void send_queue			(void);
    141 static void initial_processing		(FolderItem *item, gpointer data);
    142 static void quit_signal_handler         (int sig);
    143 static void install_basic_sighandlers   (void);
    144 static void exit_claws			(MainWindow *mainwin);
    145 
    146 #define STRNCMP(S1, S2) (strncmp((S1), (S2), sizeof((S2)) - 1))
    147 
    148 #define CM_FD_WRITE(S) fd_write(sock, (S), strlen((S)))
    149 #define CM_FD_WRITE_ALL(S) fd_write_all(sock, (S), strlen((S)))
    150 
    151 static MainWindow *static_mainwindow;
    152 
    153 static gboolean emergency_exit = FALSE;
    154 
    155 static void claws_gtk_idle(void)
    156 {
    157 	while(gtk_events_pending()) {
    158 		gtk_main_iteration();
    159 	}
    160 	g_usleep(50000);
    161 }
    162 
    163 static gboolean sc_starting = FALSE;
    164 
    165 static gboolean defer_check_all(void *data)
    166 {
    167 	if (!sc_starting) {
    168 		inc_all_account_mail(static_mainwindow, prefs_common.newmail_notify_manu);
    169 	} else {
    170 		inc_all_account_mail(static_mainwindow, prefs_common.newmail_notify_manu);
    171 		sc_starting = FALSE;
    172 		main_window_set_menu_sensitive(static_mainwindow);
    173 		toolbar_main_set_sensitive(static_mainwindow);
    174 	}
    175 	return FALSE;
    176 }
    177 
    178 static gboolean defer_check(void *data)
    179 {
    180 	inc_mail(static_mainwindow, prefs_common.newmail_notify_manu);
    181 
    182 	if (sc_starting) {
    183 		sc_starting = FALSE;
    184 		main_window_set_menu_sensitive(static_mainwindow);
    185 		toolbar_main_set_sensitive(static_mainwindow);
    186 	}
    187 	return FALSE;
    188 }
    189 
    190 static gboolean defer_jump(void *data)
    191 {
    192 	if (cmd.receive_all) {
    193 		defer_check_all(GINT_TO_POINTER(FALSE));
    194 	} else if (cmd.receive) {
    195 		defer_check(NULL);
    196 	}
    197 	mainwindow_jump_to(data, FALSE);
    198 	if (sc_starting) {
    199 		sc_starting = FALSE;
    200 		main_window_set_menu_sensitive(static_mainwindow);
    201 		toolbar_main_set_sensitive(static_mainwindow);
    202 	}
    203 	return FALSE;
    204 }
    205 
    206 static gboolean sc_exiting = FALSE;
    207 
    208 bool verify_folderlist_xml(char *path, char *backup)
    209 {
    210 	if (xml_parse_file(path))
    211 		return true;
    212 	bool fileexists = true;
    213 	if (errno == ENOENT)
    214 		fileexists = false;
    215 
    216  	struct stat sb;
    217  	if (stat(backup, &sb) < 0)
    218   		return false;
    219 
    220 	char baktime[BUFSIZ];
    221 	time_t mtime = sb.st_mtim.tv_sec;
    222 	strftime(baktime, sizeof(baktime), "%a %d-%b-%Y %H:%M %Z", localtime(&mtime));
    223 	char msg[BUFSIZ];
    224 	if (fileexists)
    225 		snprintf(msg, sizeof(msg), "File %s is corrupted! Restore from backup on %s?", FOLDER_LIST, baktime);
    226 	else
    227 		snprintf(msg, sizeof(msg), "File %s is missing! Restore from backup on %s?", FOLDER_LIST, baktime);
    228 
    229 	AlertValue aval = alertpanel("Warning", msg, NULL, "Cancel", NULL, "Restore", NULL, NULL, ALERTFOCUS_FIRST);
    230 	if (aval != G_ALERTALTERNATE)
    231 		return false;
    232 	if (copy_file(backup, path, FALSE) < 0) {
    233 		alertpanel_warning("Could not restore from %s to %s", backup, path);
    234 		return false;
    235 	}
    236 	return true;
    237 }
    238 
    239 static void main_dump_features_list(gboolean show_debug_only)
    240 /* display compiled-in features list */
    241 {
    242 	if (show_debug_only && !debug_get_mode())
    243 		return;
    244 
    245 	if (show_debug_only)
    246 		debug_print("runtime GTK %d.%d.%d / GLib %d.%d.%d\n",
    247 			   gtk_major_version, gtk_minor_version, gtk_micro_version,
    248 			   glib_major_version, glib_minor_version, glib_micro_version);
    249 	else
    250 		g_print("runtime GTK %d.%d.%d / GLib %d.%d.%d\n",
    251 			   gtk_major_version, gtk_minor_version, gtk_micro_version,
    252 			   glib_major_version, glib_minor_version, glib_micro_version);
    253 	if (show_debug_only)
    254 		debug_print("buildtime GTK %d.%d.%d / GLib %d.%d.%d\n",
    255 			   GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION,
    256 			   GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
    257 	else
    258 		g_print("buildtime GTK %d.%d.%d / GLib %d.%d.%d\n",
    259 			   GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION,
    260 			   GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
    261 
    262 	if (show_debug_only)
    263 		debug_print("Compiled-in features:\n");
    264 	else
    265 		g_print("Compiled-in features:\n");
    266 	if (show_debug_only)
    267 		debug_print(" GnuTLS\n");
    268 	else
    269 		g_print(" GnuTLS\n");
    270 	if (show_debug_only)
    271 		debug_print(" iconv\n");
    272 	else
    273 		g_print(" iconv\n");
    274 	if (show_debug_only)
    275 		debug_print(" libetpan %d.%d\n", LIBETPAN_VERSION_MAJOR, LIBETPAN_VERSION_MINOR);
    276 	else
    277 		g_print(" libetpan %d.%d\n", LIBETPAN_VERSION_MAJOR, LIBETPAN_VERSION_MINOR);
    278 }
    279 
    280 int main(int argc, char *argv[])
    281 {
    282 	MainWindow *mainwin;
    283 	FolderView *folderview;
    284 	GdkPixbuf *icon;
    285 	gboolean start_done = TRUE;
    286 	gboolean never_ran = FALSE;
    287 	gint ret;
    288 
    289 	sc_starting = TRUE;
    290 
    291 	if (!claws_init(&argc, &argv)) {
    292 		return 0;
    293 	}
    294 
    295 	prog_version = PROG_VERSION;
    296 
    297 	parse_cmd_opt(argc, argv);
    298 
    299 	/* check and create unix domain socket for remote operation */
    300 	lock_socket = prohibit_duplicate_launch(&argc, &argv);
    301 	if (lock_socket < 0) {
    302 		perror("prohibit_duplicate_launch");
    303 		return 0;
    304 	}
    305 
    306 	main_dump_features_list(TRUE);
    307 	prefs_prepare_cache();
    308 	install_basic_sighandlers();
    309 
    310 	if (cmd.status || cmd.status_full || cmd.search ||
    311 		cmd.cancel_receiving || cmd.cancel_sending ||
    312 		cmd.debug) {
    313 		puts("Claws Mail not running");
    314 		lock_socket_remove();
    315 		return 0;
    316 	}
    317 
    318 	if (cmd.exit)
    319 		return 0;
    320 
    321 	gtk_init(&argc, &argv);
    322 
    323 	gtkut_create_ui_manager();
    324 
    325 	/* Create container for all the menus we will be adding */
    326 	MENUITEM_ADDUI("/", "Menus", NULL, GTK_UI_MANAGER_MENUBAR);
    327 
    328 	CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), 1);
    329 
    330 	if (mkdir(get_rc_dir(), 0700) == 0) {
    331 		char *skel = "/etc/skel/.claws-mail";
    332 		if (copy_dir(skel, get_rc_dir()) < 0) {
    333 			err(1, "copy template config directory from %s to %s", skel, get_rc_dir());
    334 		}
    335 	}
    336 
    337 	char userrc[PATH_MAX];
    338 	snprintf(userrc, sizeof(userrc), "%s/%s", get_rc_dir(), MENU_RC);
    339 	if (copy_file(userrc, userrc, TRUE) < 0) {
    340 		warn("backup %s to %s.bak", userrc, userrc);
    341 	}
    342 
    343 	gtk_accel_map_load (userrc);
    344 	CHDIR_RETURN_VAL_IF_FAIL(get_rc_dir(), 1);
    345 
    346 	if (mkdir(get_mail_base_dir(), 0755) < 0 && errno != EEXIST)
    347 		err(1, "make %s", get_mail_base_dir());
    348 	if (mkdir(get_mime_tmp_dir(), 0755) < 0 && errno != EEXIST)
    349 		err(1, "make %s", get_mime_tmp_dir());
    350 	if (mkdir(get_tmp_dir(), 0755) < 0 && errno != EEXIST)
    351 		err(1, "make %s", get_tmp_dir());
    352 
    353 	remove_all_files(get_tmp_dir());
    354 	remove_all_files(get_mime_tmp_dir());
    355 
    356 	set_log_file(LOG_PROTOCOL, "claws.log");
    357 	set_log_file(LOG_DEBUG_FILTERING, "filtering.log");
    358 
    359 	CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), 1);
    360 
    361 	folder_system_init();
    362 	prefs_common_read_config();
    363 
    364 	prefs_ext_prog_init();
    365 	prefs_compose_writing_init();
    366 	prefs_summaries_init();
    367 	prefs_message_init();
    368 	prefs_other_init();
    369 	prefs_receive_init();
    370 	prefs_send_init();
    371 
    372 	codeconv_set_allow_jisx0201_kana(prefs_common.allow_jisx0201_kana);
    373 	codeconv_set_broken_are_utf8(prefs_common.broken_are_utf8);
    374 
    375 	sock_set_io_timeout(prefs_common.io_timeout_secs);
    376 	prefs_actions_read_config();
    377 	prefs_display_header_read_config();
    378 	addressbook_read_file();
    379 	gtkut_widget_init();
    380 	priv_pixbuf_gdk(PRIV_PIXMAP_CLAWS_MAIL_ICON, &icon);
    381 	gtk_window_set_default_icon(icon);
    382 
    383 	folderview_initialize();
    384 
    385 	mh_gtk_init();
    386 	imap_gtk_init();
    387 
    388 	mainwin = main_window_create();
    389 
    390 	char backup[PATH_MAX];
    391 	snprintf(backup, sizeof(backup), "%s/%s.bak", get_rc_dir(), FOLDER_LIST);
    392 	if (!verify_folderlist_xml(folder_get_list_path(), backup))
    393 		errx(1, "verify folderlist %s: %s", folder_get_list_path(), strerror(errno));
    394 
    395 	manage_window_focus_in(mainwin->window, NULL, NULL);
    396 	folderview = mainwin->folderview;
    397 
    398 	folderview_freeze(mainwin->folderview);
    399 	folder_item_update_freeze();
    400 
    401 	if ((ret = passwd_store_read_config()) < 0) {
    402 		debug_print("Password store configuration file version upgrade failed (%d), exiting\n", ret);
    403 		exit(202);
    404 	}
    405 
    406 	prefs_account_init();
    407 	account_read_config_all();
    408 
    409 	imap_main_init(prefs_common.skip_ssl_cert_check);
    410 	imap_main_set_timeout(prefs_common.io_timeout_secs);
    411 	/* If we can't read a folder list or don't have accounts,
    412 	 * it means the configuration's not done. Either this is
    413 	 * a brand new install, a failed/refused migration,
    414 	 * or a failed config_version upgrade.
    415 	 */
    416 	if ((ret = folder_read_list()) < 0) {
    417 		debug_print("Folderlist read failed (%d)\n", ret);
    418 		prefs_destroy_cache();
    419 
    420 		if (ret == -2) {
    421 			/* config_version update failed in folder_read_list(). */
    422 			debug_print("Folderlist version upgrade failed, exiting\n");
    423 			exit(203);
    424 		}
    425 
    426 		main_window_reflect_prefs_all_now();
    427 		folder_write_list();
    428 		never_ran = TRUE;
    429 	}
    430 
    431 	if (!account_get_list()) {
    432 		prefs_destroy_cache();
    433 		if(!account_get_list()) {
    434 			exit_claws(mainwin);
    435 			exit(1);
    436 		}
    437 		never_ran = TRUE;
    438 	}
    439 
    440 
    441 	toolbar_main_set_sensitive(mainwin);
    442 	main_window_set_menu_sensitive(mainwin);
    443 
    444 	account_set_missing_folder();
    445 	folder_set_missing_folders();
    446 	folderview_set(folderview);
    447 
    448 	/* make one all-folder processing before using claws */
    449 	main_window_cursor_wait(mainwin);
    450 	folder_func_to_all_folders(initial_processing, (gpointer *)mainwin);
    451 
    452 	if (cmd.online_mode == ONLINE_MODE_OFFLINE) {
    453 		main_window_toggle_work_offline(mainwin, TRUE, FALSE);
    454 	}
    455 	if (cmd.online_mode == ONLINE_MODE_ONLINE) {
    456 		main_window_toggle_work_offline(mainwin, FALSE, FALSE);
    457 	}
    458 
    459 	if (cmd.status_folders) {
    460 		g_ptr_array_free(cmd.status_folders, TRUE);
    461 		cmd.status_folders = NULL;
    462 	}
    463 	if (cmd.status_full_folders) {
    464 		g_ptr_array_free(cmd.status_full_folders, TRUE);
    465 		cmd.status_full_folders = NULL;
    466 	}
    467 
    468 	claws_register_idle_function(claws_gtk_idle);
    469 
    470 	prefs_toolbar_init();
    471 
    472 	if (never_ran) {
    473 		prefs_common_write_config();
    474 	}
    475 
    476 	main_window_popup(mainwin);
    477 
    478 	if (cmd.geometry != NULL) {
    479 		if (!gtk_window_parse_geometry(GTK_WINDOW(mainwin->window), cmd.geometry))
    480 			g_warning("failed to parse geometry '%s'", cmd.geometry);
    481 		else {
    482 			int width, height;
    483 
    484 			if (sscanf(cmd.geometry, "%ux%u+", &width, &height) == 2)
    485 				gtk_window_resize(GTK_WINDOW(mainwin->window), width, height);
    486 			else
    487 				g_warning("failed to parse geometry's width/height");
    488 		}
    489 	}
    490 
    491 	if (!folder_have_mailbox()) {
    492 		prefs_destroy_cache();
    493 		main_window_cursor_normal(mainwin);
    494 		if (folder_get_list() != NULL) {
    495 			alertpanel_error(_("Claws Mail has detected a configured "
    496 				   "mailbox, but it is incomplete. It is "
    497 				   "possibly due to a failing IMAP account. Use "
    498 				   "\"Rebuild folder tree\" on the mailbox parent "
    499 				   "folder's context menu to try to fix it."));
    500 		} else {
    501 			alertpanel_error("Could not load configured mailbox.");
    502 			exit_claws(mainwin);
    503 			exit(1);
    504 		}
    505 	}
    506 
    507 	static_mainwindow = mainwin;
    508 	folder_item_update_thaw();
    509 	folderview_thaw(mainwin->folderview);
    510 	main_window_cursor_normal(mainwin);
    511 
    512 	if (!cmd.target && prefs_common.goto_folder_on_startup &&
    513 	    folder_find_item_from_identifier(prefs_common.startup_folder) != NULL) {
    514 		cmd.target = prefs_common.startup_folder;
    515 	}
    516 
    517 	if (cmd.receive_all && !cmd.target) {
    518 		start_done = FALSE;
    519 		g_timeout_add(1000, defer_check_all, GINT_TO_POINTER(FALSE));
    520 	} else if (cmd.receive && !cmd.target) {
    521 		start_done = FALSE;
    522 		g_timeout_add(1000, defer_check, NULL);
    523 	}
    524 	folderview_grab_focus(folderview);
    525 
    526 	if (cmd.compose) {
    527 		open_compose_new(cmd.compose_mailto, cmd.attach_files);
    528 	}
    529 	if (cmd.attach_files) {
    530 		list_free_strings_full(cmd.attach_files);
    531 		cmd.attach_files = NULL;
    532 	}
    533 	if (cmd.subscribe) {
    534 		folder_subscribe(cmd.subscribe_uri);
    535 	}
    536 
    537 	if (cmd.send) {
    538 		send_queue();
    539 	}
    540 
    541 	if (cmd.target) {
    542 		start_done = FALSE;
    543 		g_timeout_add(500, defer_jump, (gpointer)cmd.target);
    544 	}
    545 
    546 	prefs_destroy_cache();
    547 
    548 	compose_reopen_exit_drafts();
    549 
    550 	if (start_done) {
    551 		sc_starting = FALSE;
    552 		main_window_set_menu_sensitive(mainwin);
    553 		toolbar_main_set_sensitive(mainwin);
    554 	}
    555 
    556 	/* register the callback of unix domain socket input */
    557 	lock_socket_tag = claws_input_add(lock_socket,
    558 					G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI,
    559 					lock_socket_input_cb,
    560 					mainwin, TRUE);
    561 
    562 
    563 	gtk_main();
    564 	utils_free_regex();
    565 	exit_claws(mainwin);
    566 
    567 	return 0;
    568 }
    569 
    570 static void save_all_caches(FolderItem *item, gpointer data)
    571 {
    572 	if (!item->cache) {
    573 		return;
    574 	}
    575 
    576 	if (item->opened) {
    577 		folder_item_close(item);
    578 	}
    579 
    580 	folder_item_free_cache(item, TRUE);
    581 }
    582 
    583 static void exit_claws(MainWindow *mainwin)
    584 {
    585 	gchar *filename;
    586 	FolderItem *item;
    587 
    588 	sc_exiting = TRUE;
    589 
    590 	debug_print("shutting down\n");
    591 
    592 	/* save prefs for opened folder */
    593 	if((item = folderview_get_opened_item(mainwin->folderview)) != NULL) {
    594 		summary_save_prefs_to_folderitem(
    595 			mainwin->summaryview, item);
    596 	}
    597 
    598 	/* save all state before exiting */
    599 	folder_func_to_all_folders(save_all_caches, NULL);
    600 	folder_write_list();
    601 
    602 	main_window_get_size(mainwin);
    603 	main_window_get_position(mainwin);
    604 
    605 	prefs_common_write_config();
    606 	account_write_config_all();
    607 	passwd_store_write_config();
    608 	addressbook_export_to_file();
    609 	filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, MENU_RC, NULL);
    610 	gtk_accel_map_save(filename);
    611 	g_free(filename);
    612 
    613 	/* delete temporary files */
    614 	remove_all_files(get_tmp_dir());
    615 	remove_all_files(get_mime_tmp_dir());
    616 
    617 	close_log_file(LOG_PROTOCOL);
    618 	close_log_file(LOG_DEBUG_FILTERING);
    619 
    620 	imap_main_done(TRUE);
    621 
    622 	lock_socket_remove();
    623 
    624 	main_window_destroy_all();
    625 
    626 	prefs_toolbar_done();
    627 
    628 	addressbook_destroy();
    629 	prefs_ext_prog_done();
    630 	prefs_compose_writing_done();
    631 	prefs_summaries_done();
    632 	prefs_message_done();
    633 	prefs_other_done();
    634 	prefs_receive_done();
    635 	prefs_send_done();
    636 	claws_done();
    637 }
    638 
    639 #define G_PRINT_EXIT(msg)	\
    640 	{			\
    641 		g_print(msg);	\
    642 		exit(1);	\
    643 	}
    644 
    645 static void parse_cmd_compose_from_file(const gchar *fn, GString *body)
    646 {
    647 	GString *headers = g_string_new(NULL);
    648 	gchar *to = NULL;
    649 	gchar *h;
    650 	gchar *v;
    651 	gchar fb[BUFFSIZE];
    652 	FILE *fp;
    653 	gboolean isstdin;
    654 
    655 	if (fn == NULL || *fn == '\0')
    656 		G_PRINT_EXIT(_("Missing filename\n"));
    657 	isstdin = (*fn == '-' && *(fn + 1) == '\0');
    658 	if (isstdin)
    659 		fp = stdin;
    660 	else {
    661 		fp = g_fopen(fn, "r");
    662 		if (!fp)
    663 			G_PRINT_EXIT(_("Cannot open filename for reading\n"));
    664 	}
    665 
    666 	while (fgets(fb, sizeof(fb), fp)) {
    667 		gchar *tmp;
    668 		strretchomp(fb);
    669 		if (*fb == '\0')
    670 			break;
    671 		h = fb;
    672 		while (*h && *h != ':') { ++h; } /* search colon */
    673         	if (*h == '\0')
    674 			G_PRINT_EXIT(_("Malformed header\n"));
    675 		v = h + 1;
    676 		while (*v && *v == ' ') { ++v; } /* trim value start */
    677 		*h = '\0';
    678 		tmp = g_ascii_strdown(fb, -1); /* get header name */
    679 		if (!strcmp(tmp, "to")) {
    680 			if (to != NULL)
    681 				G_PRINT_EXIT(_("Duplicated 'To:' header\n"));
    682 			to = g_strdup(v);
    683 		} else {
    684 			g_string_append_c(headers, '&');
    685 			g_string_append(headers, tmp);
    686 			g_string_append_c(headers, '=');
    687 			g_string_append_uri_escaped(headers, v, NULL, TRUE);
    688 		}
    689 		g_free(tmp);
    690 	}
    691 	if (to == NULL)
    692 		G_PRINT_EXIT(_("Missing required 'To:' header\n"));
    693 	g_string_append(body, to);
    694 	g_free(to);
    695 	g_string_append(body, "?body=");
    696 	while (fgets(fb, sizeof(fb), fp)) {
    697 		g_string_append_uri_escaped(body, fb, NULL, TRUE);
    698 	}
    699 	if (!isstdin)
    700 		fclose(fp);
    701 	/* append the remaining headers */
    702 	g_string_append(body, headers->str);
    703 	g_string_free(headers, TRUE);
    704 }
    705 
    706 #undef G_PRINT_EXIT
    707 
    708 static void parse_cmd_opt_error(char *errstr, char* optstr)
    709 {
    710     gchar tmp[BUFSIZ];
    711 
    712 	cm_return_if_fail(errstr != NULL);
    713 	cm_return_if_fail(optstr != NULL);
    714 
    715     g_snprintf(tmp, sizeof(tmp), errstr, optstr);
    716 	g_print(_("%s. Try -h or --help for usage.\n"), tmp);
    717 	exit(1);
    718 }
    719 
    720 static GString mailto; /* used to feed cmd.compose_mailto when --compose-from-file is used */
    721 
    722 static void parse_cmd_opt(int argc, char *argv[])
    723 {
    724 	AttachInfo *ainfo;
    725 	gint i;
    726 
    727 	for (i = 1; i < argc; i++) {
    728 		if (!strcmp(argv[i], "--receive-all")) {
    729 			cmd.receive_all = TRUE;
    730 		} else if (!strcmp(argv[i], "--receive")) {
    731 			cmd.receive = TRUE;
    732 		} else if (!strcmp(argv[i], "--cancel-receiving")) {
    733 			cmd.cancel_receiving = TRUE;
    734 		} else if (!strcmp(argv[i], "--cancel-sending")) {
    735 			cmd.cancel_sending = TRUE;
    736 		} else if (!strcmp(argv[i], "--compose-from-file")) {
    737 		    if (i+1 < argc) {
    738 				const gchar *p = argv[i+1];
    739 
    740 				parse_cmd_compose_from_file(p, &mailto);
    741 				cmd.compose = TRUE;
    742 				cmd.compose_mailto = mailto.str;
    743 				i++;
    744 		    } else {
    745                 parse_cmd_opt_error(_("Missing file argument for option %s"), argv[i]);
    746 			}
    747 		} else if (!strcmp(argv[i], "--compose")) {
    748 			const gchar *p = (i+1 < argc)?argv[i+1]:NULL;
    749 
    750 			cmd.compose = TRUE;
    751 			cmd.compose_mailto = NULL;
    752 			if (p && *p != '\0' && *p != '-') {
    753 				if (!STRNCMP(p, "mailto:")) {
    754 					cmd.compose_mailto = p + 7;
    755 				} else {
    756 					cmd.compose_mailto = p;
    757 				}
    758 				i++;
    759 			}
    760 		} else if (!strcmp(argv[i], "--subscribe")) {
    761 		    if (i+1 < argc) {
    762 				const gchar *p = argv[i+1];
    763 				if (p && *p != '\0' && *p != '-') {
    764 					cmd.subscribe = TRUE;
    765 					cmd.subscribe_uri = p;
    766 				} else {
    767                     parse_cmd_opt_error(_("Missing or empty uri argument for option %s"), argv[i]);
    768 				}
    769 		    } else {
    770                 parse_cmd_opt_error(_("Missing uri argument for option %s"), argv[i]);
    771 			}
    772 		} else if (!strcmp(argv[i], "--attach") ||
    773                 !strcmp(argv[i], "--insert")) {
    774 		    if (i+1 < argc) {
    775 				const gchar *p = argv[i+1];
    776 				gint ii = i;
    777 				gchar *file = NULL;
    778 				gboolean insert = !strcmp(argv[i], "--insert");
    779 
    780 				while (p && *p != '\0' && *p != '-') {
    781 					if ((file = g_filename_from_uri(p, NULL, NULL)) != NULL) {
    782 						if (!is_file_exist(file)) {
    783 							g_free(file);
    784 							file = NULL;
    785 						}
    786 					}
    787 					if (file == NULL && *p != G_DIR_SEPARATOR) {
    788 						file = g_strconcat(claws_get_startup_dir(),
    789 								   G_DIR_SEPARATOR_S,
    790 								   p, NULL);
    791 					} else if (file == NULL) {
    792 						file = g_strdup(p);
    793 					}
    794 
    795 					ainfo = g_new0(AttachInfo, 1);
    796 					ainfo->file = file;
    797 					ainfo->insert = insert;
    798 					cmd.attach_files = g_list_append(cmd.attach_files, ainfo);
    799 					ii++;
    800 					p = (ii+1 < argc)?argv[ii+1]:NULL;
    801 				}
    802 				if (ii==i) {
    803                     parse_cmd_opt_error(_("Missing at least one non-empty file argument for option %s"), argv[i]);
    804 				} else {
    805                     i=ii;
    806                 }
    807 		    } else {
    808                 parse_cmd_opt_error(_("Missing file argument for option %s"), argv[i]);
    809 			}
    810 		} else if (!strcmp(argv[i], "--send")) {
    811 			cmd.send = TRUE;
    812 		} else if (!strcmp(argv[i], "--version-full") ||
    813 			   !strcmp(argv[i], "-V")) {
    814 			g_print("Claws Mail version " VERSION_GIT_FULL "\n");
    815 			main_dump_features_list(FALSE);
    816 			exit(0);
    817 		} else if (!strcmp(argv[i], "--version") ||
    818 			   !strcmp(argv[i], "-v")) {
    819 			g_print("Claws Mail version " VERSION "\n");
    820 			exit(0);
    821  		} else if (!strcmp(argv[i], "--status-full")) {
    822  			const gchar *p = (i+1 < argc)?argv[i+1]:NULL;
    823 
    824  			cmd.status_full = TRUE;
    825  			while (p && *p != '\0' && *p != '-') {
    826  				if (!cmd.status_full_folders) {
    827  					cmd.status_full_folders =
    828  						g_ptr_array_new();
    829 				}
    830  				g_ptr_array_add(cmd.status_full_folders,
    831  						g_strdup(p));
    832  				i++;
    833  				p = (i+1 < argc)?argv[i+1]:NULL;
    834  			}
    835   		} else if (!strcmp(argv[i], "--status")) {
    836  			const gchar *p = (i+1 < argc)?argv[i+1]:NULL;
    837 
    838   			cmd.status = TRUE;
    839  			while (p && *p != '\0' && *p != '-') {
    840  				if (!cmd.status_folders)
    841  					cmd.status_folders = g_ptr_array_new();
    842  				g_ptr_array_add(cmd.status_folders,
    843  						g_strdup(p));
    844  				i++;
    845  				p = (i+1 < argc)?argv[i+1]:NULL;
    846  			}
    847 		} else if (!strcmp(argv[i], "--search")) {
    848 		    if (i+3 < argc) { /* 3 first arguments are mandatory */
    849 		    	const char* p;
    850 		    	/* only set search parameters if they are valid */
    851 		    	p = argv[i+1];
    852 				cmd.search_folder    = (p && *p != '\0' && *p != '-')?p:NULL;
    853 				p = argv[i+2];
    854 				cmd.search_type      = (p && *p != '\0' && *p != '-')?p:NULL;
    855 				p = argv[i+3];
    856 				cmd.search_request   = (p && *p != '\0' && *p != '-')?p:NULL;
    857 				p = (i+4 < argc)?argv[i+4]:NULL;
    858 				const char* rec      = (p && *p != '\0' && *p != '-')?p:NULL;
    859 				cmd.search_recursive = TRUE;
    860 				if (rec) {
    861                     i++;
    862                     if (tolower(*rec)=='n' || tolower(*rec)=='f' || *rec=='0')
    863     					cmd.search_recursive = FALSE;
    864                 }
    865 				if (cmd.search_folder && cmd.search_type && cmd.search_request) {
    866 					cmd.search = TRUE;
    867                     i+=3;
    868                 }
    869 		    } else {
    870                 switch (argc-i-1) {
    871                 case 0:
    872                     parse_cmd_opt_error(_("Missing folder, type and request arguments for option %s"), argv[i]);
    873                     break;
    874                 case 1:
    875                     parse_cmd_opt_error(_("Missing type and request arguments for option %s"), argv[i]);
    876                     break;
    877                 case 2:
    878                     parse_cmd_opt_error(_("Missing request argument for option %s"), argv[i]);
    879                 }
    880 			}
    881 		} else if (!strcmp(argv[i], "--online")) {
    882 			cmd.online_mode = ONLINE_MODE_ONLINE;
    883 		} else if (!strcmp(argv[i], "--offline")) {
    884 			cmd.online_mode = ONLINE_MODE_OFFLINE;
    885 		} else if (!strcmp(argv[i], "--toggle-debug")) {
    886 			cmd.debug = TRUE;
    887 		} else if (!strcmp(argv[i], "--help") ||
    888 			   !strcmp(argv[i], "-h")) {
    889 			gchar *base = g_path_get_basename(argv[0]);
    890 			g_print(_("Usage: %s [OPTION]...\n"), base);
    891 
    892 			g_print("%s\n", _("  --compose [address]    open composition window"));
    893 			g_print("%s\n", _("  --compose-from-file file\n"
    894 				  	  "                         open composition window with data from given file;\n"
    895 			  	  	  "                         use - as file name for reading from standard input;\n"
    896 			  	  	  "                         content format: headers first (To: required) until an\n"
    897 				  	  "                         empty line, then mail body until end of file."));
    898 			g_print("%s\n", _("  --subscribe uri        subscribe to the given URI if possible"));
    899 			g_print("%s\n", _("  --attach file1 [file2]...\n"
    900 					  "                         open composition window with specified files\n"
    901 					  "                         attached"));
    902 			g_print("%s\n", _("  --insert file1 [file2]...\n"
    903 					  "                         open composition window with specified files\n"
    904 					  "                         inserted"));
    905 			g_print("%s\n", _("  --receive              receive new messages"));
    906 			g_print("%s\n", _("  --receive-all          receive new messages of all accounts"));
    907 			g_print("%s\n", _("  --cancel-receiving     cancel receiving of messages"));
    908 			g_print("%s\n", _("  --cancel-sending       cancel sending of messages"));
    909 			g_print("%s\n", _("  --search folder type request [recursive]\n"
    910 					  "                         searches mail\n"
    911 					  "                         folder ex.: \"#mh/Mailbox/inbox\" or \"Mail\"\n"
    912 					  "                         type: s[ubject],f[rom],t[o],e[xtended],m[ixed] or g: tag\n"
    913 					  "                         request: search string\n"
    914 					  "                         recursive: false if arg. starts with 0, n, N, f or F"));
    915 
    916 			g_print("%s\n", _("  --send                 send all queued messages"));
    917  			g_print("%s\n", _("  --status [folder]...   show the total number of messages"));
    918  			g_print("%s\n", _("  --status-full [folder]...\n"
    919  			                  "                         show the status of each folder"));
    920 			g_print("%s\n", _("  --select folder[/msg]  jump to the specified folder/message\n"
    921 					  "                         folder is a folder id like 'folder/subfolder', a file:// uri or an absolute path"));
    922 			g_print("%s\n", _("  --online               switch to online mode"));
    923 			g_print("%s\n", _("  --offline              switch to offline mode"));
    924 			g_print("%s\n", _("  --exit --quit -q       exit Claws Mail"));
    925 			g_print("%s\n", _("  --debug -d             debug mode"));
    926 			g_print("%s\n", _("  --toggle-debug         toggle debug mode"));
    927 			g_print("%s\n", _("  --help -h              display this help"));
    928 			g_print("%s\n", _("  --version -v           output version information"));
    929 			g_print("%s\n", _("  --version-full -V      output version and built-in features information"));
    930 			g_print("%s\n", _("  --alternate-config-dir directory\n"
    931 			                  "                         use specified configuration directory"));
    932 			g_print("%s\n", _("  --geometry -geometry [WxH][+X+Y]\n"
    933 					  "                         set geometry for main window"));
    934 
    935 			g_free(base);
    936 			exit(1);
    937 		} else if (!strcmp(argv[i], "--alternate-config-dir")) {
    938 		    if (i+1 < argc) {
    939 				set_rc_dir(argv[i+1]);
    940                 i++;
    941 		    } else {
    942                 parse_cmd_opt_error(_("Missing directory argument for option %s"), argv[i]);
    943 			}
    944 		} else if (!strcmp(argv[i], "--geometry") ||
    945 		        !strcmp(argv[i], "-geometry")) {
    946 		    if (i+1 < argc) {
    947 				cmd.geometry = argv[i+1];
    948                 i++;
    949 		    } else {
    950                 parse_cmd_opt_error(_("Missing geometry argument for option %s"), argv[i]);
    951 			}
    952 		} else if (!strcmp(argv[i], "--exit") ||
    953 			   !strcmp(argv[i], "--quit") ||
    954 			   !strcmp(argv[i], "-q")) {
    955 			cmd.exit = TRUE;
    956 		} else if (!strcmp(argv[i], "--select")) {
    957 		    if (i+1 < argc) {
    958 				cmd.target = argv[i+1];
    959                 i++;
    960 		    } else {
    961                 parse_cmd_opt_error(_("Missing folder argument for option %s"), argv[i]);
    962 			}
    963 		} else if (i == 1 && argc == 2) {
    964 			/* only one parameter. Do something intelligent about it */
    965 			if ((strstr(argv[i], "@") || !STRNCMP(argv[i], "mailto:")) && !strstr(argv[i], "://")) {
    966 				const gchar *p = argv[i];
    967 
    968 				cmd.compose = TRUE;
    969 				cmd.compose_mailto = NULL;
    970 				if (p && *p != '\0' && *p != '-') {
    971 					if (!STRNCMP(p, "mailto:")) {
    972 						cmd.compose_mailto = p + 7;
    973 					} else {
    974 						cmd.compose_mailto = p;
    975 					}
    976 				}
    977 			} else if (!STRNCMP(argv[i], "file://")) {
    978 				cmd.target = argv[i];
    979 			} else if (!STRNCMP(argv[i], "?attach=file://")) {
    980                 /* Thunar support as per 3.3.0cvs19 */
    981 				cmd.compose = TRUE;
    982 				cmd.compose_mailto = argv[i];
    983 			} else if (strstr(argv[i], "://")) {
    984 				const gchar *p = argv[i];
    985 				if (p && *p != '\0' && *p != '-') {
    986 					cmd.subscribe = TRUE;
    987 					cmd.subscribe_uri = p;
    988 				}
    989 			} else if (!strcmp(argv[i], "--sync")) {
    990 				/* gtk debug */
    991 			} else if (is_dir_exist(argv[i]) || is_file_exist(argv[i])) {
    992 				cmd.target = argv[i];
    993     		} else {
    994                 parse_cmd_opt_error(_("Unknown option %s"), argv[i]);
    995             }
    996 		} else {
    997             parse_cmd_opt_error(_("Unknown option %s"), argv[i]);
    998         }
    999 	}
   1000 
   1001 	if (cmd.attach_files && cmd.compose == FALSE) {
   1002 		cmd.compose = TRUE;
   1003 		cmd.compose_mailto = NULL;
   1004 	}
   1005 }
   1006 
   1007 static void initial_processing(FolderItem *item, gpointer data)
   1008 {
   1009 	MainWindow *mainwin = (MainWindow *)data;
   1010 	gchar *buf;
   1011 
   1012 	cm_return_if_fail(item);
   1013 	buf = g_strdup_printf(_("Processing (%s)..."),
   1014 			      item->path
   1015 			      ? item->path
   1016 			      : _("top level folder"));
   1017 	g_free(buf);
   1018 
   1019 	if (folder_item_parent(item) != NULL && item->prefs->enable_processing) {
   1020 		item->processing_pending = FALSE;
   1021 	}
   1022 
   1023 	STATUSBAR_POP(mainwin);
   1024 }
   1025 
   1026 static gboolean draft_all_messages(void)
   1027 {
   1028 	const GList *compose_list = NULL;
   1029 
   1030 	compose_clear_exit_drafts();
   1031 	compose_list = compose_get_compose_list();
   1032 	while (compose_list != NULL) {
   1033 		Compose *c = (Compose*)compose_list->data;
   1034 		if (!compose_draft(c, COMPOSE_DRAFT_FOR_EXIT))
   1035 			return FALSE;
   1036 		compose_list = compose_get_compose_list();
   1037 	}
   1038 	return TRUE;
   1039 }
   1040 gboolean clean_quit(gpointer data)
   1041 {
   1042 	static gboolean firstrun = TRUE;
   1043 
   1044 	if (!firstrun) {
   1045 		return FALSE;
   1046 	}
   1047 	firstrun = FALSE;
   1048 
   1049 	/*!< Good idea to have the main window stored in a
   1050 	 *   static variable so we can check that variable
   1051 	 *   to see if we're really allowed to do things
   1052 	 *   that actually the spawner is supposed to
   1053 	 *   do (like: sending mail, composing messages).
   1054 	 *   Because, really, if we're the spawnee, and
   1055 	 *   we touch GTK stuff, we're hosed. See the
   1056 	 *   next fixme. */
   1057 
   1058 	/* FIXME: Use something else to signal that we're
   1059 	 * in the original spawner, and not in a spawned
   1060 	 * child. */
   1061 	if (!static_mainwindow) {
   1062 		return FALSE;
   1063 	}
   1064 
   1065 	draft_all_messages();
   1066 	emergency_exit = TRUE;
   1067 	exit_claws(static_mainwindow);
   1068 	exit(0);
   1069 
   1070 	return FALSE;
   1071 }
   1072 
   1073 void app_will_exit(GtkWidget *widget, gpointer data)
   1074 {
   1075 	MainWindow *mainwin = data;
   1076 
   1077 	if (gtk_main_level() == 0) {
   1078 		debug_print("not even started\n");
   1079 		return;
   1080 	}
   1081 	if (sc_exiting == TRUE) {
   1082 		debug_print("exit pending\n");
   1083 		return;
   1084 	}
   1085 	sc_exiting = TRUE;
   1086 	debug_print("exiting\n");
   1087 	if (compose_get_compose_list()) {
   1088 		if (!draft_all_messages()) {
   1089 			main_window_popup(mainwin);
   1090 			sc_exiting = FALSE;
   1091 			return;
   1092 		}
   1093 	}
   1094 
   1095 	if (prefs_common.warn_queued_on_exit && procmsg_have_queued_mails_fast()) {
   1096 		if (alertpanel(_("Queued messages"),
   1097 			       _("Some unsent messages are queued. Exit now?"),
   1098 			       NULL, _("_Cancel"), NULL, _("_OK"), NULL, NULL,
   1099 			       ALERTFOCUS_FIRST)
   1100 		    != G_ALERTALTERNATE) {
   1101 			main_window_popup(mainwin);
   1102 		    	sc_exiting = FALSE;
   1103 			return;
   1104 		}
   1105 		manage_window_focus_in(mainwin->window, NULL, NULL);
   1106 	}
   1107 
   1108 	if (folderview_get_selected_item(mainwin->folderview))
   1109 		folder_item_close(folderview_get_selected_item(mainwin->folderview));
   1110 	gtk_main_quit();
   1111 }
   1112 
   1113 gboolean claws_is_exiting(void)
   1114 {
   1115 	return sc_exiting;
   1116 }
   1117 
   1118 gboolean claws_is_starting(void)
   1119 {
   1120 	return sc_starting;
   1121 }
   1122 
   1123 size_t claws_socket_path(char *buf, size_t len) {
   1124 	size_t n = 0;
   1125 	n += strlcpy(buf, g_get_user_runtime_dir(), len);
   1126 	n += strlcat(buf, "/", len);
   1127 	n += strlcat(buf, "claws-mail/control.sock", len);
   1128 	return n;
   1129 }
   1130 
   1131 static gint prohibit_duplicate_launch(int *argc, char ***argv)
   1132 {
   1133 	gint sock;
   1134 	GList *curr;
   1135 
   1136 	char sockpath[PATH_MAX];
   1137 	claws_socket_path(sockpath, sizeof(sockpath));
   1138 	fprintf(stderr, "using %s to prohibit duplicate process\n", sockpath);
   1139 	if (mkdir(dirname(sockpath), 0755) < 0)
   1140 		return -1;
   1141 	sock = fd_connect_unix(sockpath);
   1142 
   1143 	if (sock < 0) {
   1144 		/* If connect failed, no other process is running.
   1145 		 * Unlink the potentially existing socket, then
   1146 		 * open it. This has to be done locking a temporary
   1147 		 * file to avoid the race condition where another
   1148 		 * process could have created the socket just in
   1149 		 * between.
   1150 		 */
   1151 		char socklock[PATH_MAX];
   1152 		strlcpy(socklock, sockpath, sizeof(socklock));
   1153 		strlcat(socklock, ".lock", sizeof(socklock));
   1154 		int lock_fd = open(socklock, O_RDWR|O_CREAT, 0);
   1155 		if (lock_fd < 0) {
   1156 			warn("open %s", socklock);
   1157 			return -1;
   1158 		}
   1159 		if (flock(lock_fd, LOCK_EX) < 0) {
   1160 			warn("lock %s", socklock);
   1161 			close(lock_fd);
   1162 			return -1;
   1163 		}
   1164 
   1165 		remove(sockpath);
   1166 		int ret = fd_open_unix(sockpath);
   1167 		flock(lock_fd, LOCK_UN);
   1168 		close(lock_fd);
   1169 		remove(socklock);
   1170 		return ret;
   1171 	}
   1172 	fprintf(stderr, "another Claws Mail instance is already running.\n");
   1173 
   1174 	/* remote command mode */
   1175 	if (cmd.receive_all) {
   1176 		CM_FD_WRITE_ALL("receive_all\n");
   1177 	} else if (cmd.receive) {
   1178 		CM_FD_WRITE_ALL("receive\n");
   1179 	} else if (cmd.cancel_receiving) {
   1180 		CM_FD_WRITE_ALL("cancel_receiving\n");
   1181 	} else if (cmd.cancel_sending) {
   1182 		CM_FD_WRITE_ALL("cancel_sending\n");
   1183 	} else if (cmd.compose && cmd.attach_files) {
   1184 		gchar *str, *compose_str;
   1185 
   1186 		if (cmd.compose_mailto) {
   1187 			compose_str = g_strdup_printf("compose_attach %s\n",
   1188 						      cmd.compose_mailto);
   1189 		} else {
   1190 			compose_str = g_strdup("compose_attach\n");
   1191 		}
   1192 
   1193 		CM_FD_WRITE_ALL(compose_str);
   1194 		g_free(compose_str);
   1195 
   1196 		for (curr = cmd.attach_files; curr != NULL ; curr = curr->next) {
   1197 			str = (gchar *) ((AttachInfo *)curr->data)->file;
   1198 			if (((AttachInfo *)curr->data)->insert)
   1199 				CM_FD_WRITE_ALL("insert ");
   1200 			else
   1201 				CM_FD_WRITE_ALL("attach ");
   1202 			CM_FD_WRITE_ALL(str);
   1203 			CM_FD_WRITE_ALL("\n");
   1204 		}
   1205 
   1206 		CM_FD_WRITE_ALL(".\n");
   1207 	} else if (cmd.compose) {
   1208 		gchar *compose_str;
   1209 
   1210 		if (cmd.compose_mailto) {
   1211 			compose_str = g_strdup_printf
   1212 				("compose %s\n", cmd.compose_mailto);
   1213 		} else {
   1214 			compose_str = g_strdup("compose\n");
   1215 		}
   1216 
   1217 		CM_FD_WRITE_ALL(compose_str);
   1218 		g_free(compose_str);
   1219 	} else if (cmd.subscribe) {
   1220 		gchar *str = g_strdup_printf("subscribe %s\n", cmd.subscribe_uri);
   1221 		CM_FD_WRITE_ALL(str);
   1222 		g_free(str);
   1223 	} else if (cmd.send) {
   1224 		CM_FD_WRITE_ALL("send\n");
   1225 	} else if (cmd.online_mode == ONLINE_MODE_ONLINE) {
   1226 		CM_FD_WRITE("online\n");
   1227 	} else if (cmd.online_mode == ONLINE_MODE_OFFLINE) {
   1228 		CM_FD_WRITE("offline\n");
   1229 	} else if (cmd.debug) {
   1230 		CM_FD_WRITE("debug\n");
   1231  	} else if (cmd.status || cmd.status_full) {
   1232   		gchar buf[BUFFSIZE];
   1233  		gint i;
   1234  		const gchar *command;
   1235  		GPtrArray *folders;
   1236  		gchar *folder;
   1237 
   1238  		command = cmd.status_full ? "status-full\n" : "status\n";
   1239  		folders = cmd.status_full ? cmd.status_full_folders :
   1240  			cmd.status_folders;
   1241 
   1242  		CM_FD_WRITE_ALL(command);
   1243  		for (i = 0; folders && i < folders->len; ++i) {
   1244  			folder = g_ptr_array_index(folders, i);
   1245  			CM_FD_WRITE_ALL(folder);
   1246  			CM_FD_WRITE_ALL("\n");
   1247  		}
   1248  		CM_FD_WRITE_ALL(".\n");
   1249  		for (;;) {
   1250  			fd_gets(sock, buf, sizeof(buf) - 1);
   1251 			buf[sizeof(buf) - 1] = '\0';
   1252  			if (!STRNCMP(buf, ".\n")) break;
   1253 			if (fputs(buf, stdout) == EOF) {
   1254 				g_warning("writing to stdout failed");
   1255 				break;
   1256 			}
   1257  		}
   1258 	} else if (cmd.exit) {
   1259 		CM_FD_WRITE_ALL("exit\n");
   1260 	} else if (cmd.target) {
   1261 		gchar *str = g_strdup_printf("select %s\n", cmd.target);
   1262 		CM_FD_WRITE_ALL(str);
   1263 		g_free(str);
   1264 	} else if (cmd.search) {
   1265 		gchar buf[BUFFSIZE];
   1266 		gchar *str =
   1267 			g_strdup_printf("search %s\n%s\n%s\n%c\n",
   1268 							cmd.search_folder, cmd.search_type, cmd.search_request,
   1269 							(cmd.search_recursive==TRUE)?'1':'0');
   1270 		CM_FD_WRITE_ALL(str);
   1271 		g_free(str);
   1272 		for (;;) {
   1273 			fd_gets(sock, buf, sizeof(buf) - 1);
   1274 			buf[sizeof(buf) - 1] = '\0';
   1275 			if (!STRNCMP(buf, ".\n")) break;
   1276 			if (fputs(buf, stdout) == EOF) {
   1277 				g_warning("writing to stdout failed");
   1278 				break;
   1279 			}
   1280 		}
   1281 	} else {
   1282 #ifdef G_OS_UNIX
   1283 		gchar buf[BUFSIZ];
   1284 		CM_FD_WRITE_ALL("get_display\n");
   1285 		memset(buf, 0, sizeof(buf));
   1286 		fd_gets(sock, buf, sizeof(buf) - 1);
   1287 		buf[sizeof(buf) - 1] = '\0';
   1288 
   1289 		/* Try to connect to a display; if it is the same one as
   1290 		 * the other Claws instance, then ask it to pop up. */
   1291 		int diff_display = 1;
   1292 		if (gtk_init_check(argc, argv)) {
   1293 			GdkDisplay *display = gdk_display_get_default();
   1294 			diff_display = g_strcmp0(buf, gdk_display_get_name(display));
   1295 		}
   1296 		if (diff_display) {
   1297 			g_print("Claws Mail is already running on display %s.\n",
   1298 				buf);
   1299 		} else {
   1300 			g_print("Claws Mail is already running on this display (%s).\n",
   1301 				buf);
   1302 			close(sock);
   1303 			sock = fd_connect_unix(sockpath);
   1304 			CM_FD_WRITE_ALL("popup\n");
   1305 		}
   1306 #else
   1307 		CM_FD_WRITE_ALL("popup\n");
   1308 #endif
   1309 	}
   1310 
   1311 	close(sock);
   1312 	return -1;
   1313 }
   1314 
   1315 static int lock_socket_remove(void)
   1316 {
   1317 	if (lock_socket < 0)
   1318 		return -1;
   1319 
   1320 	if (lock_socket_tag > 0)
   1321 		g_source_remove(lock_socket_tag);
   1322 	close(lock_socket);
   1323 
   1324 	char sockpath[PATH_MAX];
   1325 	claws_socket_path(sockpath, sizeof(sockpath));
   1326 	if (remove(sockpath) < 0)
   1327 		warn("remove %s", sockpath);
   1328 	return remove(dirname(sockpath));
   1329 }
   1330 
   1331 static GPtrArray *get_folder_item_list(gint sock)
   1332 {
   1333 	gchar buf[BUFFSIZE];
   1334 	FolderItem *item;
   1335 	GPtrArray *folders = NULL;
   1336 
   1337 	for (;;) {
   1338 		fd_gets(sock, buf, sizeof(buf) - 1);
   1339 		buf[sizeof(buf) - 1] = '\0';
   1340 		if (!STRNCMP(buf, ".\n")) {
   1341 			break;
   1342 		}
   1343 		strretchomp(buf);
   1344 		if (!folders) {
   1345 			folders = g_ptr_array_new();
   1346 		}
   1347 		item = folder_find_item_from_identifier(buf);
   1348 		if (item) {
   1349 			g_ptr_array_add(folders, item);
   1350 		} else {
   1351 			g_warning("no such folder: %s", buf);
   1352 		}
   1353 	}
   1354 
   1355 	return folders;
   1356 }
   1357 
   1358 static void lock_socket_input_cb(gpointer data,
   1359 				 gint source,
   1360 				 GIOCondition condition)
   1361 {
   1362 	MainWindow *mainwin = (MainWindow *)data;
   1363 	gint sock;
   1364 	gchar buf[BUFFSIZE];
   1365 
   1366 	sock = fd_accept(source);
   1367 	if (sock < 0)
   1368 		return;
   1369 
   1370 	fd_gets(sock, buf, sizeof(buf) - 1);
   1371 	buf[sizeof(buf) - 1] = '\0';
   1372 
   1373 	if (!STRNCMP(buf, "popup")) {
   1374 		main_window_popup(mainwin);
   1375 #ifdef G_OS_UNIX
   1376 	} else if (!STRNCMP(buf, "get_display")) {
   1377 		GdkDisplay* display = gtk_widget_get_display(mainwin->window);
   1378 		const gchar *display_name = gdk_display_get_name(display);
   1379 		CM_FD_WRITE_ALL(display_name);
   1380 #endif
   1381 	} else if (!STRNCMP(buf, "receive_all")) {
   1382 		inc_all_account_mail(mainwin, prefs_common.newmail_notify_manu);
   1383 	} else if (!STRNCMP(buf, "receive")) {
   1384 		inc_mail(mainwin, prefs_common.newmail_notify_manu);
   1385 	} else if (!STRNCMP(buf, "cancel_receiving")) {
   1386 		inc_cancel_all();
   1387 		imap_cancel_all();
   1388 	} else if (!STRNCMP(buf, "cancel_sending")) {
   1389 		send_cancel();
   1390 	} else if (!STRNCMP(buf, "compose_attach")) {
   1391 		GList *files = NULL, *curr;
   1392 		AttachInfo *ainfo;
   1393 		gchar *mailto;
   1394 
   1395 		mailto = g_strdup(buf + strlen("compose_attach") + 1);
   1396 		while (fd_gets(sock, buf, sizeof(buf) - 1) > 0) {
   1397 			buf[sizeof(buf) - 1] = '\0';
   1398 			strretchomp(buf);
   1399 			if (!g_strcmp0(buf, "."))
   1400 				break;
   1401 
   1402 			ainfo = g_new0(AttachInfo, 1);
   1403 			ainfo->file = g_strdup(strstr(buf, " ") + 1);
   1404 			ainfo->insert = !STRNCMP(buf, "insert ");
   1405 			files = g_list_append(files, ainfo);
   1406 		}
   1407 		open_compose_new(mailto, files);
   1408 
   1409 		curr = g_list_first(files);
   1410 		while (curr != NULL) {
   1411 			ainfo = (AttachInfo *)curr->data;
   1412 			g_free(ainfo->file);
   1413 			g_free(ainfo);
   1414 			curr = curr->next;
   1415 		}
   1416 		g_list_free(files);
   1417 		g_free(mailto);
   1418 	} else if (!STRNCMP(buf, "compose")) {
   1419 		open_compose_new(buf + strlen("compose") + 1, NULL);
   1420 	} else if (!STRNCMP(buf, "subscribe")) {
   1421 		main_window_popup(mainwin);
   1422 		folder_subscribe(buf + strlen("subscribe") + 1);
   1423 	} else if (!STRNCMP(buf, "send")) {
   1424 		send_queue();
   1425 	} else if (!STRNCMP(buf, "online")) {
   1426 		main_window_toggle_work_offline(mainwin, FALSE, FALSE);
   1427 	} else if (!STRNCMP(buf, "offline")) {
   1428 		main_window_toggle_work_offline(mainwin, TRUE, FALSE);
   1429 	} else if (!STRNCMP(buf, "debug")) {
   1430 		debug_set_mode(debug_get_mode() ? FALSE : TRUE);
   1431  	} else if (!STRNCMP(buf, "status-full") ||
   1432  		   !STRNCMP(buf, "status")) {
   1433  		gchar *status;
   1434  		GPtrArray *folders;
   1435 
   1436  		folders = get_folder_item_list(sock);
   1437  		status = folder_get_status
   1438  			(folders, !STRNCMP(buf, "status-full"));
   1439  		CM_FD_WRITE_ALL(status);
   1440  		CM_FD_WRITE_ALL(".\n");
   1441  		g_free(status);
   1442  		if (folders) g_ptr_array_free(folders, TRUE);
   1443 	} else if (!STRNCMP(buf, "select ")) {
   1444 		const gchar *target = buf+7;
   1445 		mainwindow_jump_to(target, TRUE);
   1446 	} else if (!STRNCMP(buf, "exit")) {
   1447 		app_will_exit(NULL, mainwin);
   1448 	}
   1449 	close(sock);
   1450 }
   1451 
   1452 static void open_compose_new(const gchar *address, GList *attach_files)
   1453 {
   1454 	// 254 + mailto:
   1455 	char addr[261];
   1456 	if (address) {
   1457 		strlcpy(addr, address, sizeof(addr));
   1458 		g_strstrip(addr);
   1459 	}
   1460 	compose_new(NULL, addr, attach_files);
   1461 }
   1462 
   1463 static void send_queue(void)
   1464 {
   1465 	GList *list;
   1466 	gchar *errstr = NULL;
   1467 	gboolean error = FALSE;
   1468 	for (list = folder_get_list(); list != NULL; list = list->next) {
   1469 		Folder *folder = list->data;
   1470 
   1471 		if (folder->queue) {
   1472 			gint res = procmsg_send_queue
   1473 				(folder->queue, prefs_common.savemsg,
   1474 				&errstr);
   1475 
   1476 			if (res) {
   1477 				folder_item_scan(folder->queue);
   1478 			}
   1479 
   1480 			if (res < 0)
   1481 				error = TRUE;
   1482 		}
   1483 	}
   1484 	if (errstr) {
   1485 		alertpanel_error_log(_("Some errors occurred "
   1486 				"while sending queued messages:\n%s"), errstr);
   1487 		g_free(errstr);
   1488 	} else if (error) {
   1489 		alertpanel_error_log("Some errors occurred "
   1490 				"while sending queued messages.");
   1491 	}
   1492 }
   1493 
   1494 static void quit_signal_handler(int sig)
   1495 {
   1496 	debug_print("Quitting on signal %d\n", sig);
   1497 
   1498 	g_timeout_add(0, clean_quit, NULL);
   1499 }
   1500 
   1501 static void install_basic_sighandlers()
   1502 {
   1503 	sigset_t    mask;
   1504 	struct sigaction act;
   1505 
   1506 	sigemptyset(&mask);
   1507 
   1508 #ifdef SIGTERM
   1509 	sigaddset(&mask, SIGTERM);
   1510 #endif
   1511 #ifdef SIGINT
   1512 	sigaddset(&mask, SIGINT);
   1513 #endif
   1514 #ifdef SIGHUP
   1515 	sigaddset(&mask, SIGHUP);
   1516 #endif
   1517 
   1518 	act.sa_handler = quit_signal_handler;
   1519 	act.sa_mask    = mask;
   1520 	act.sa_flags   = 0;
   1521 
   1522 #ifdef SIGTERM
   1523 	sigaction(SIGTERM, &act, 0);
   1524 #endif
   1525 #ifdef SIGINT
   1526 	sigaction(SIGINT, &act, 0);
   1527 #endif
   1528 #ifdef SIGHUP
   1529 	sigaction(SIGHUP, &act, 0);
   1530 #endif
   1531 
   1532 	sigprocmask(SIG_UNBLOCK, &mask, 0);
   1533 }