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 }