inc.c (36518B)
1 /* 2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client 3 * Copyright (C) 1999-2025 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 "defs.h" 20 21 #include <fcntl.h> 22 #include <glib.h> 23 #include <glib/gi18n.h> 24 #include <gtk/gtk.h> 25 #include <stdio.h> 26 #include <unistd.h> 27 #include <string.h> 28 #include <sys/param.h> 29 30 #include "main.h" 31 #include "inc.h" 32 #include "mainwindow.h" 33 #include "folderview.h" 34 #include "summaryview.h" 35 #include "prefs_common.h" 36 #include "prefs_account.h" 37 #include "account.h" 38 #include "procmsg.h" 39 #include "socket.h" 40 #include "ssl.h" 41 #include "pop.h" 42 #include "mbox.h" 43 #include "file-utils.h" 44 #include "utils.h" 45 #include "gtkutils.h" 46 #include "statusbar.h" 47 #include "msgcache.h" 48 #include "manage_window.h" 49 #include "stock_pixmap.h" 50 #include "progressdialog.h" 51 #include "inputdialog.h" 52 #include "alertpanel.h" 53 #include "folder.h" 54 #include "log.h" 55 #include "hooks.h" 56 #include "logwindow.h" 57 #include "passwordstore.h" 58 59 static GList *inc_dialog_list = NULL; 60 61 static time_t inc_offline_overridden_yes = 0; 62 static time_t inc_offline_overridden_no = 0; 63 64 guint inc_lock_count = 0; 65 66 static GdkPixbuf *currentpix; 67 static GdkPixbuf *errorpix; 68 static GdkPixbuf *okpix; 69 70 #define MESSAGEBUFSIZE 8192 71 72 static gint inc_account_mail_real (MainWindow *mainwin, 73 PrefsAccount *account); 74 75 static IncProgressDialog *inc_progress_dialog_create 76 (gboolean autocheck); 77 static void inc_progress_dialog_set_list(IncProgressDialog *inc_dialog); 78 static void inc_progress_dialog_destroy (IncProgressDialog *inc_dialog); 79 80 static IncSession *inc_session_new (PrefsAccount *account); 81 static void inc_session_destroy (IncSession *session); 82 static gint inc_start (IncProgressDialog *inc_dialog); 83 static IncState inc_pop3_session_do (IncSession *session); 84 85 static void inc_progress_dialog_update (IncProgressDialog *inc_dialog, 86 IncSession *inc_session); 87 88 static void inc_progress_dialog_set_label 89 (IncProgressDialog *inc_dialog, 90 IncSession *inc_session); 91 static void inc_progress_dialog_set_progress 92 (IncProgressDialog *inc_dialog, 93 IncSession *inc_session); 94 95 static void inc_progress_dialog_update_periodic 96 (IncProgressDialog *inc_dialog, 97 IncSession *inc_session); 98 99 static gint inc_recv_data_progressive (Session *session, 100 guint cur_len, 101 guint total_len, 102 gpointer data); 103 static gint inc_recv_data_finished (Session *session, 104 guint len, 105 gpointer data); 106 static gint inc_recv_message (Session *session, 107 const gchar *msg, 108 gpointer data); 109 static gint inc_drop_message (Pop3Session *session, 110 const gchar *file); 111 112 static void inc_put_error (IncState istate, 113 Pop3Session *session); 114 115 static void inc_showlog_cb (GtkWidget *widget, 116 gpointer data); 117 static void inc_cancel_cb (GtkWidget *widget, 118 gpointer data); 119 static gint inc_dialog_delete_cb (GtkWidget *widget, 120 GdkEventAny *event, 121 gpointer data); 122 123 static gint get_spool (FolderItem *dest, 124 const gchar *mbox, 125 PrefsAccount *account); 126 127 static gint inc_spool_account(PrefsAccount *account); 128 129 static void inc_notify_cmd(gint new_msgs, gboolean notify); 130 131 void inc_mail(MainWindow *mainwin, gboolean notify) 132 { 133 gint new_msgs = 0; 134 gint account_new_msgs = 0; 135 136 if (inc_lock_count) return; 137 138 if (prefs_common.work_offline && 139 !inc_offline_should_override(TRUE, 140 _("Claws Mail needs network access in order " 141 "to get mails."))) 142 return; 143 144 inc_lock(); 145 main_window_lock(mainwin); 146 147 if (prefs_common.use_extinc && prefs_common.extinc_cmd) { 148 /* external incorporating program */ 149 if (execute_command_line(prefs_common.extinc_cmd, FALSE, NULL) < 0) { 150 main_window_unlock(mainwin); 151 inc_unlock(); 152 return; 153 } 154 } else { 155 account_new_msgs = inc_account_mail_real(mainwin, cur_account); 156 if (account_new_msgs > 0) 157 new_msgs += account_new_msgs; 158 } 159 160 statusbar_progress_all(0,0,0); 161 main_window_unlock(mainwin); 162 inc_notify_cmd(new_msgs, notify); 163 inc_unlock(); 164 } 165 166 void inc_pop_before_smtp(PrefsAccount *acc) 167 { 168 IncProgressDialog *inc_dialog; 169 IncSession *session; 170 MainWindow *mainwin; 171 172 mainwin = mainwindow_get_mainwindow(); 173 174 session = inc_session_new(acc); 175 if (!session) return; 176 POP3_SESSION(session->session)->pop_before_smtp = TRUE; 177 178 inc_dialog = inc_progress_dialog_create(FALSE); 179 inc_dialog->queue_list = g_list_append(inc_dialog->queue_list, 180 session); 181 /* FIXME: assumes to attach to first main window */ 182 inc_dialog->mainwin = mainwin; 183 inc_progress_dialog_set_list(inc_dialog); 184 185 if (mainwin) { 186 toolbar_main_set_sensitive(mainwin); 187 main_window_set_menu_sensitive(mainwin); 188 } 189 190 inc_start(inc_dialog); 191 } 192 193 static gint inc_account_mail_real(MainWindow *mainwin, PrefsAccount *account) 194 { 195 IncProgressDialog *inc_dialog; 196 IncSession *session; 197 198 switch (account->protocol) { 199 case A_IMAP4: 200 /* Melvin: bug [14] 201 * FIXME: it should return foldeview_check_new() value. 202 * TODO: do it when bug [19] is fixed (IMAP folder sets 203 * an incorrect new message count) 204 */ 205 folderview_check_new(FOLDER(account->folder)); 206 return 0; 207 case A_POP3: 208 session = inc_session_new(account); 209 if (!session) return 0; 210 211 inc_dialog = inc_progress_dialog_create(FALSE); 212 inc_dialog->queue_list = g_list_append(inc_dialog->queue_list, 213 session); 214 inc_dialog->mainwin = mainwin; 215 inc_progress_dialog_set_list(inc_dialog); 216 217 if (mainwin) { 218 toolbar_main_set_sensitive(mainwin); 219 main_window_set_menu_sensitive(mainwin); 220 } 221 222 return inc_start(inc_dialog); 223 224 case A_LOCAL: 225 return inc_spool_account(account); 226 227 default: 228 break; 229 } 230 return 0; 231 } 232 233 gint inc_account_mail(MainWindow *mainwin, PrefsAccount *account) 234 { 235 gint new_msgs; 236 237 if (inc_lock_count) return 0; 238 239 if (account->receive_in_progress) return 0; 240 241 if (prefs_common.work_offline && 242 !inc_offline_should_override(TRUE, 243 _("Claws Mail needs network access in order " 244 "to get mails."))) 245 return 0; 246 247 main_window_lock(mainwin); 248 249 new_msgs = inc_account_mail_real(mainwin, account); 250 251 statusbar_progress_all(0,0,0); 252 main_window_unlock(mainwin); 253 254 return new_msgs; 255 } 256 257 void inc_account_list_mail(MainWindow *mainwin, GList *account_list, gboolean autocheck, 258 gboolean notify) 259 { 260 GList *list, *queue_list = NULL; 261 IncProgressDialog *inc_dialog; 262 gint new_msgs = 0, num; 263 264 if (prefs_common.work_offline && 265 !inc_offline_should_override( (autocheck == FALSE), 266 _("Claws Mail needs network access in order " 267 "to get mails."))) 268 return; 269 270 if (inc_lock_count) return; 271 272 main_window_lock(mainwin); 273 274 if (!account_list) { 275 statusbar_progress_all(0,0,0); 276 main_window_unlock(mainwin); 277 inc_notify_cmd(new_msgs, notify); 278 return; 279 } 280 281 if (prefs_common.use_extinc && prefs_common.extinc_cmd) { 282 /* external incorporating program */ 283 if (execute_command_line(prefs_common.extinc_cmd, FALSE, NULL) < 0) { 284 log_error(LOG_PROTOCOL, _("%s failed\n"), prefs_common.extinc_cmd); 285 286 main_window_unlock(mainwin); 287 return; 288 } 289 } 290 291 /* Check all accounts in the list, one by one. */ 292 for (list = account_list; list != NULL; list = list->next) { 293 PrefsAccount *account = list->data; 294 295 if (account == NULL) { 296 debug_print("INC: Huh? inc_account_list_mail() got a NULL account, this should not happen!\n"); 297 continue; 298 } 299 300 debug_print("INC: checking account %d\n", account->account_id); 301 switch (account->protocol) { 302 case A_POP3: 303 if (!(account->receive_in_progress)) { 304 IncSession *session = inc_session_new(account); 305 306 if (session != NULL) { 307 debug_print("INC: adding POP3 account %d to inc queue\n", 308 account->account_id); 309 queue_list = g_list_append(queue_list, session); 310 } 311 } 312 break; 313 314 case A_IMAP4: 315 new_msgs += folderview_check_new(FOLDER(account->folder)); 316 break; 317 318 case A_LOCAL: 319 num = inc_spool_account(account); 320 if (num > 0) 321 new_msgs += num; 322 break; 323 324 case A_NONE: 325 /* Nothing to do here, it's a SMTP-only account. */ 326 break; 327 328 default: 329 debug_print("INC: encountered account %d with unknown protocol %d, ignoring\n", 330 account->account_id, account->protocol); 331 break; 332 } 333 } 334 335 336 337 if (queue_list) { 338 inc_dialog = inc_progress_dialog_create(autocheck); 339 inc_dialog->queue_list = queue_list; 340 inc_dialog->mainwin = mainwin; 341 inc_progress_dialog_set_list(inc_dialog); 342 343 toolbar_main_set_sensitive(mainwin); 344 main_window_set_menu_sensitive(mainwin); 345 new_msgs += inc_start(inc_dialog); 346 } 347 348 statusbar_progress_all(0,0,0); 349 main_window_unlock(mainwin); 350 inc_notify_cmd(new_msgs, notify); 351 } 352 353 void inc_all_account_mail(MainWindow *mainwin, gboolean notify) 354 { 355 GList *list, *list2 = NULL; 356 gboolean condition = FALSE; 357 358 for (list = account_get_list(); list != NULL; list = list->next) { 359 PrefsAccount *account = list->data; 360 /* Nothing to do for SMTP-only accounts. */ 361 if (account->protocol == A_NONE) 362 continue; 363 list2 = g_list_append(list2, account); 364 } 365 366 /* Do the check on the collected accounts. */ 367 if (list2 != NULL) { 368 gboolean hide_dialog = TRUE; 369 inc_account_list_mail(mainwin, list2, hide_dialog, notify); 370 g_list_free(list2); 371 } 372 } 373 374 static void inc_progress_dialog_size_allocate_cb(GtkWidget *widget, 375 GtkAllocation *allocation) 376 { 377 cm_return_if_fail(allocation != NULL); 378 379 gtk_window_get_size(GTK_WINDOW(widget), 380 &prefs_common.receivewin_width, &prefs_common.receivewin_height); 381 } 382 383 static IncProgressDialog *inc_progress_dialog_create(gboolean autocheck) 384 { 385 IncProgressDialog *dialog; 386 ProgressDialog *progress; 387 static GdkGeometry geometry; 388 389 dialog = g_new0(IncProgressDialog, 1); 390 391 progress = progress_dialog_create(); 392 gtk_window_set_title(GTK_WINDOW(progress->window), 393 _("Retrieving new messages")); 394 g_signal_connect(G_OBJECT(progress->showlog_btn), "clicked", 395 G_CALLBACK(inc_showlog_cb), dialog); 396 g_signal_connect(G_OBJECT(progress->cancel_btn), "clicked", 397 G_CALLBACK(inc_cancel_cb), dialog); 398 g_signal_connect(G_OBJECT(progress->window), "delete_event", 399 G_CALLBACK(inc_dialog_delete_cb), dialog); 400 g_signal_connect(G_OBJECT(progress->window), "size_allocate", 401 G_CALLBACK(inc_progress_dialog_size_allocate_cb), NULL); 402 /* manage_window_set_transient(GTK_WINDOW(progress->window)); */ 403 404 progress_dialog_get_fraction(progress); 405 406 stock_pixbuf_gdk(STOCK_PIXMAP_COMPLETE, &okpix); 407 stock_pixbuf_gdk(STOCK_PIXMAP_CONTINUE, ¤tpix); 408 stock_pixbuf_gdk(STOCK_PIXMAP_ERROR, &errorpix); 409 410 if (!geometry.min_height) { 411 geometry.min_width = 460; 412 geometry.min_height = 250; 413 } 414 415 gtk_window_set_geometry_hints(GTK_WINDOW(progress->window), NULL, &geometry, 416 GDK_HINT_MIN_SIZE); 417 gtk_widget_set_size_request(progress->window, prefs_common.receivewin_width, 418 prefs_common.receivewin_height); 419 420 dialog->dialog = progress; 421 dialog->progress_tv = g_date_time_new_now_local(); 422 dialog->folder_tv = g_date_time_new_now_local(); 423 dialog->queue_list = NULL; 424 dialog->cur_row = 0; 425 426 inc_dialog_list = g_list_append(inc_dialog_list, dialog); 427 428 return dialog; 429 } 430 431 static void inc_progress_dialog_set_list(IncProgressDialog *inc_dialog) 432 { 433 GList *list; 434 435 for (list = inc_dialog->queue_list; list != NULL; list = list->next) { 436 IncSession *session = list->data; 437 Pop3Session *pop3_session = POP3_SESSION(session->session); 438 439 session->data = inc_dialog; 440 441 progress_dialog_list_set(inc_dialog->dialog, 442 -1, NULL, 443 pop3_session->ac_prefs->account_name, 444 _("Standby")); 445 } 446 } 447 448 static void inc_progress_dialog_clear(IncProgressDialog *inc_dialog) 449 { 450 progress_dialog_get_fraction(inc_dialog->dialog); 451 progress_dialog_set_label(inc_dialog->dialog, ""); 452 if (inc_dialog->mainwin) 453 main_window_progress_off(inc_dialog->mainwin); 454 } 455 456 static void inc_progress_dialog_destroy(IncProgressDialog *inc_dialog) 457 { 458 cm_return_if_fail(inc_dialog != NULL); 459 460 inc_dialog_list = g_list_remove(inc_dialog_list, inc_dialog); 461 462 if (inc_dialog->mainwin) 463 main_window_progress_off(inc_dialog->mainwin); 464 progress_dialog_destroy(inc_dialog->dialog); 465 466 g_date_time_unref(inc_dialog->progress_tv); 467 g_date_time_unref(inc_dialog->folder_tv); 468 469 g_free(inc_dialog); 470 } 471 472 static IncSession *inc_session_new(PrefsAccount *account) 473 { 474 IncSession *session; 475 476 cm_return_val_if_fail(account != NULL, NULL); 477 478 if (account->protocol != A_POP3) 479 return NULL; 480 if (!account->recv_server || !account->userid) 481 return NULL; 482 483 session = g_new0(IncSession, 1); 484 485 session->session = pop3_session_new(account); 486 session->session->data = session; 487 POP3_SESSION(session->session)->drop_message = inc_drop_message; 488 session_set_recv_message_notify(session->session, 489 inc_recv_message, session); 490 session_set_recv_data_progressive_notify(session->session, 491 inc_recv_data_progressive, 492 session); 493 session_set_recv_data_notify(session->session, 494 inc_recv_data_finished, session); 495 496 return session; 497 } 498 499 static void inc_session_destroy(IncSession *session) 500 { 501 cm_return_if_fail(session != NULL); 502 503 session_destroy(session->session); 504 g_free(session); 505 } 506 507 static gint pop3_get_port(Pop3Session *pop3_session) 508 { 509 return pop3_session->ac_prefs->set_popport ? 510 pop3_session->ac_prefs->popport : 511 pop3_session->ac_prefs->ssl_pop == SSL_TUNNEL ? 995 : 110; 512 } 513 514 static gint inc_start(IncProgressDialog *inc_dialog) 515 { 516 IncSession *session; 517 GList *qlist; 518 Pop3Session *pop3_session; 519 IncState inc_state; 520 gint new_msgs = 0; 521 gchar *msg; 522 FolderItem *processing, *inbox; 523 GSList *msglist, *msglist_element; 524 gboolean cancelled = FALSE; 525 526 qlist = inc_dialog->queue_list; 527 while (qlist != NULL) { 528 GList *next = qlist->next; 529 530 session = qlist->data; 531 pop3_session = POP3_SESSION(session->session); 532 pop3_session->user = g_strdup(pop3_session->ac_prefs->userid); 533 534 if (inc_dialog->show_dialog) 535 manage_window_focus_in 536 (inc_dialog->dialog->window, 537 NULL, NULL); 538 539 if (password_get(pop3_session->user, 540 pop3_session->ac_prefs->recv_server, 541 "pop3", pop3_get_port(pop3_session), 542 &(pop3_session->pass))) { 543 /* NOP */; 544 } else if ((pop3_session->pass = passwd_store_get_account( 545 pop3_session->ac_prefs->account_id, PWS_ACCOUNT_RECV)) == NULL) { 546 gchar *pass; 547 548 pass = input_dialog_query_password_keep 549 (pop3_session->ac_prefs->recv_server, 550 pop3_session->user, 551 &(pop3_session->ac_prefs->session_passwd)); 552 553 if (pass) { 554 pop3_session->pass = pass; 555 } 556 } 557 558 if (inc_dialog->show_dialog) 559 manage_window_focus_out 560 (inc_dialog->dialog->window, 561 NULL, NULL); 562 563 qlist = next; 564 } 565 566 #define SET_PIXMAP_AND_TEXT(pix, str) \ 567 { \ 568 progress_dialog_list_set(inc_dialog->dialog, \ 569 inc_dialog->cur_row, \ 570 pix, \ 571 NULL, \ 572 str); \ 573 } 574 575 for (; inc_dialog->queue_list != NULL && !cancelled; inc_dialog->cur_row++) { 576 session = inc_dialog->queue_list->data; 577 pop3_session = POP3_SESSION(session->session); 578 579 if (pop3_session->pass == NULL) { 580 SET_PIXMAP_AND_TEXT(okpix, _("Cancelled")); 581 inc_dialog->queue_list = 582 g_list_remove(inc_dialog->queue_list, session); 583 inc_session_destroy(session); 584 continue; 585 } 586 587 inc_progress_dialog_clear(inc_dialog); 588 progress_dialog_scroll_to_row(inc_dialog->dialog, 589 inc_dialog->cur_row); 590 591 SET_PIXMAP_AND_TEXT(currentpix, _("Retrieving")); 592 593 /* begin POP3 session */ 594 inc_state = inc_pop3_session_do(session); 595 596 switch (inc_state) { 597 case INC_SUCCESS: 598 if (pop3_session->cur_total_num > 0) 599 msg = g_strdup_printf( 600 "Done (%d messages (%s) received)", 601 pop3_session->cur_total_num, 602 to_human_readable((goffset)pop3_session->cur_total_recv_bytes)); 603 else 604 msg = g_strdup_printf(_("Done (no new messages)")); 605 SET_PIXMAP_AND_TEXT(okpix, msg); 606 g_free(msg); 607 break; 608 case INC_CONNECT_ERROR: 609 SET_PIXMAP_AND_TEXT(errorpix, _("Connection failed")); 610 break; 611 case INC_AUTH_FAILED: 612 SET_PIXMAP_AND_TEXT(errorpix, _("Auth failed")); 613 if (pop3_session->ac_prefs->session_passwd) { 614 g_free(pop3_session->ac_prefs->session_passwd); 615 pop3_session->ac_prefs->session_passwd = NULL; 616 } 617 break; 618 case INC_LOCKED: 619 SET_PIXMAP_AND_TEXT(errorpix, _("Locked")); 620 break; 621 case INC_ERROR: 622 case INC_NO_SPACE: 623 case INC_IO_ERROR: 624 case INC_SOCKET_ERROR: 625 case INC_EOF: 626 SET_PIXMAP_AND_TEXT(errorpix, _("Error")); 627 break; 628 case INC_TIMEOUT: 629 SET_PIXMAP_AND_TEXT(errorpix, _("Timeout")); 630 break; 631 case INC_CANCEL: 632 SET_PIXMAP_AND_TEXT(okpix, _("Cancelled")); 633 if (!inc_dialog->show_dialog) 634 cancelled = TRUE; 635 break; 636 default: 637 break; 638 } 639 640 /* CLAWS: perform filtering actions on dropped message */ 641 /* CLAWS: get default inbox (perhaps per account) */ 642 if (pop3_session->ac_prefs->inbox) { 643 /* CLAWS: get destination folder / mailbox */ 644 inbox = folder_find_item_from_identifier(pop3_session->ac_prefs->inbox); 645 if (!inbox) 646 inbox = folder_get_default_inbox(); 647 } else 648 inbox = folder_get_default_inbox(); 649 650 /* get list of messages in processing */ 651 processing = folder_get_default_processing(pop3_session->ac_prefs->account_id); 652 folder_item_scan(processing); 653 msglist = folder_item_get_msg_list(processing); 654 655 /* process messages */ 656 folder_item_update_freeze(); 657 658 for(msglist_element = msglist; msglist_element != NULL; 659 msglist_element = msglist_element->next) { 660 procmsg_msginfo_free((MsgInfo**)&(msglist_element->data)); 661 } 662 folder_item_update_thaw(); 663 664 g_slist_free(msglist); 665 666 statusbar_pop_all(); 667 668 new_msgs += pop3_session->cur_total_num; 669 670 pop3_write_uidl_list(pop3_session); 671 672 if (inc_state != INC_SUCCESS && inc_state != INC_CANCEL) { 673 if (inc_dialog->show_dialog) 674 manage_window_focus_in(inc_dialog->dialog->window, NULL, NULL); 675 inc_put_error(inc_state, pop3_session); 676 if (inc_dialog->show_dialog) 677 manage_window_focus_out(inc_dialog->dialog->window, NULL, NULL); 678 if (inc_state == INC_NO_SPACE || inc_state == INC_IO_ERROR) 679 break; 680 } 681 folder_item_free_cache(processing, TRUE); 682 683 inc_session_destroy(session); 684 inc_dialog->queue_list = g_list_remove(inc_dialog->queue_list, session); 685 } 686 687 #undef SET_PIXMAP_AND_TEXT 688 689 char fin_msg[BUFSIZ]; 690 snprintf(fin_msg, sizeof(fin_msg), "Finished (%d new)", new_msgs); 691 progress_dialog_set_label(inc_dialog->dialog, fin_msg); 692 693 while (inc_dialog->queue_list != NULL) { 694 session = inc_dialog->queue_list->data; 695 inc_session_destroy(session); 696 inc_dialog->queue_list = g_list_remove(inc_dialog->queue_list, session); 697 } 698 inc_progress_dialog_destroy(inc_dialog); 699 return new_msgs; 700 } 701 702 static IncState inc_pop3_session_do(IncSession *session) 703 { 704 Pop3Session *pop3_session = POP3_SESSION(session->session); 705 IncProgressDialog *inc_dialog = (IncProgressDialog *)session->data; 706 PrefsAccount *ac = pop3_session->ac_prefs; 707 gchar *server; 708 gchar *account_name; 709 gushort port; 710 gchar *buf; 711 712 debug_print("getting new messages of account %s...\n", 713 ac->account_name); 714 715 ac->last_pop_login_time = time(NULL); 716 717 buf = g_strdup_printf(_("%s: Retrieving new messages"), 718 ac->recv_server); 719 gtk_window_set_title(GTK_WINDOW(inc_dialog->dialog->window), buf); 720 g_free(buf); 721 722 server = ac->recv_server; 723 account_name = ac->account_name; 724 port = pop3_get_port(pop3_session); 725 726 SESSION(pop3_session)->ssl_type = ac->ssl_pop; 727 if (ac->ssl_pop != SSL_NONE) 728 SESSION(pop3_session)->nonblocking = 729 ac->use_nonblocking_ssl; 730 731 buf = g_strdup_printf(_("Account '%s': Connecting to POP3 server: %s:%d..."), 732 account_name, server, port); 733 statusbar_print_all("%s", buf); 734 log_message(LOG_PROTOCOL, "%s\n", buf); 735 736 progress_dialog_set_label(inc_dialog->dialog, buf); 737 738 GTK_EVENTS_FLUSH(); 739 g_free(buf); 740 741 session_set_timeout(SESSION(pop3_session), 742 prefs_common.io_timeout_secs * 1000); 743 744 if (session_connect(SESSION(pop3_session), server, port) < 0) { 745 char msg[BUFSIZ]; 746 snprintf(msg, sizeof(msg), "Cannot connect to POP3 server at %s:%d", server, port); 747 alertpanel_error(msg); 748 manage_window_focus_out(inc_dialog->dialog->window, NULL, NULL); 749 log_error(LOG_PROTOCOL, msg); 750 session->inc_state = INC_CONNECT_ERROR; 751 statusbar_pop_all(); 752 return INC_CONNECT_ERROR; 753 } 754 755 while (session_is_running(SESSION(pop3_session)) && 756 session->inc_state != INC_CANCEL) 757 gtk_main_iteration(); 758 759 if (session->inc_state == INC_SUCCESS) { 760 switch (pop3_session->error_val) { 761 case PS_SUCCESS: 762 switch (SESSION(pop3_session)->state) { 763 case SESSION_ERROR: 764 if (pop3_session->state == POP3_READY) 765 session->inc_state = INC_CONNECT_ERROR; 766 else 767 session->inc_state = INC_ERROR; 768 break; 769 case SESSION_EOF: 770 session->inc_state = INC_EOF; 771 break; 772 case SESSION_TIMEOUT: 773 session->inc_state = INC_TIMEOUT; 774 break; 775 default: 776 session->inc_state = INC_SUCCESS; 777 break; 778 } 779 break; 780 case PS_AUTHFAIL: 781 session->inc_state = INC_AUTH_FAILED; 782 break; 783 case PS_IOERR: 784 session->inc_state = INC_IO_ERROR; 785 break; 786 case PS_SOCKET: 787 session->inc_state = INC_SOCKET_ERROR; 788 break; 789 case PS_LOCKBUSY: 790 session->inc_state = INC_LOCKED; 791 break; 792 default: 793 session->inc_state = INC_ERROR; 794 break; 795 } 796 } 797 798 session_disconnect(SESSION(pop3_session)); 799 statusbar_pop_all(); 800 801 return session->inc_state; 802 } 803 804 static void inc_progress_dialog_update(IncProgressDialog *inc_dialog, 805 IncSession *inc_session) 806 { 807 inc_progress_dialog_set_label(inc_dialog, inc_session); 808 inc_progress_dialog_set_progress(inc_dialog, inc_session); 809 } 810 811 static void inc_progress_dialog_set_label(IncProgressDialog *inc_dialog, 812 IncSession *inc_session) 813 { 814 ProgressDialog *dialog = inc_dialog->dialog; 815 Pop3Session *session; 816 817 cm_return_if_fail(inc_session != NULL); 818 819 session = POP3_SESSION(inc_session->session); 820 821 switch (session->state) { 822 case POP3_GREETING: 823 break; 824 case POP3_GETAUTH_USER: 825 case POP3_GETAUTH_PASS: 826 progress_dialog_set_label(dialog, _("Authenticating...")); 827 statusbar_pop_all(); 828 statusbar_print_all(_("Retrieving messages from %s (%s)..."), 829 SESSION(session)->server, 830 session->ac_prefs->account_name); 831 break; 832 case POP3_GETRANGE_STAT: 833 progress_dialog_set_label 834 (dialog, _("Getting the number of new messages (STAT)...")); 835 break; 836 case POP3_GETRANGE_LAST: 837 progress_dialog_set_label 838 (dialog, _("Getting the number of new messages (LAST)...")); 839 break; 840 case POP3_GETRANGE_UIDL: 841 progress_dialog_set_label 842 (dialog, _("Getting the number of new messages (UIDL)...")); 843 break; 844 case POP3_GETSIZE_LIST: 845 progress_dialog_set_label 846 (dialog, _("Getting the size of messages (LIST)...")); 847 break; 848 case POP3_RETR: 849 case POP3_RETR_RECV: 850 case POP3_DELETE: 851 break; 852 case POP3_LOGOUT: 853 progress_dialog_set_label(dialog, _("Quitting")); 854 break; 855 default: 856 break; 857 } 858 } 859 860 static void inc_progress_dialog_set_progress(IncProgressDialog *inc_dialog, 861 IncSession *inc_session) 862 { 863 gchar buf[MESSAGEBUFSIZE]; 864 Pop3Session *pop3_session = POP3_SESSION(inc_session->session); 865 gchar *total_size_str; 866 gint cur_total; 867 gint total; 868 869 if (!pop3_session->new_msg_exist) return; 870 871 cur_total = inc_session->cur_total_bytes; 872 total = pop3_session->total_bytes; 873 if (pop3_session->state == POP3_RETR || 874 pop3_session->state == POP3_RETR_RECV || 875 pop3_session->state == POP3_DELETE) { 876 Xstrdup_a(total_size_str, to_human_readable((goffset)total), return); 877 g_snprintf(buf, sizeof(buf), 878 _("Retrieving message (%d / %d) (%s / %s)"), 879 pop3_session->cur_msg, pop3_session->count, 880 to_human_readable((goffset)cur_total), total_size_str); 881 progress_dialog_set_label(inc_dialog->dialog, buf); 882 } 883 884 progress_dialog_set_fraction 885 (inc_dialog->dialog, (total == 0) ? 0: (gfloat)cur_total / (gfloat)total); 886 887 statusbar_progress_all(pop3_session->cur_msg, pop3_session->count, 1); 888 889 if (pop3_session->cur_total_num > 0) { 890 int n = pop3_session->cur_total_num; 891 char *recv = to_human_readable((goffset)pop3_session->cur_total_recv_bytes); 892 g_snprintf(buf, sizeof(buf), "Retrieving messages (%d (%s) received)", n, recv), 893 progress_dialog_list_set_status(inc_dialog->dialog, inc_dialog->cur_row, buf); 894 } 895 } 896 897 static void inc_progress_dialog_update_periodic(IncProgressDialog *inc_dialog, 898 IncSession *inc_session) 899 { 900 GDateTime *tv_cur = g_date_time_new_now_local(); 901 GTimeSpan tv_result; 902 903 tv_result = g_date_time_difference(tv_cur, inc_dialog->progress_tv); 904 g_date_time_unref(tv_cur); 905 if (tv_result < 0) { 906 tv_result += G_USEC_PER_SEC; 907 } 908 909 uint progress_update_interval = 200; // milliseconds 910 if (tv_result > progress_update_interval) { 911 inc_progress_dialog_update(inc_dialog, inc_session); 912 tv_cur = g_date_time_add(inc_dialog->progress_tv, tv_result); 913 g_date_time_unref(inc_dialog->progress_tv); 914 inc_dialog->progress_tv = tv_cur; 915 } 916 } 917 918 static gint inc_recv_data_progressive(Session *session, guint cur_len, 919 guint total_len, gpointer data) 920 { 921 IncSession *inc_session = (IncSession *)data; 922 Pop3Session *pop3_session = POP3_SESSION(session); 923 IncProgressDialog *inc_dialog; 924 gint cur_total; 925 926 cm_return_val_if_fail(inc_session != NULL, -1); 927 928 if (pop3_session->state != POP3_RETR && 929 pop3_session->state != POP3_RETR_RECV && 930 pop3_session->state != POP3_DELETE && 931 pop3_session->state != POP3_LOGOUT) return 0; 932 933 if (!pop3_session->new_msg_exist) return 0; 934 935 cur_total = pop3_session->cur_total_bytes + cur_len; 936 if (cur_total > pop3_session->total_bytes) 937 cur_total = pop3_session->total_bytes; 938 inc_session->cur_total_bytes = cur_total; 939 940 inc_dialog = (IncProgressDialog *)inc_session->data; 941 inc_progress_dialog_update_periodic(inc_dialog, inc_session); 942 943 return 0; 944 } 945 946 static gint inc_recv_data_finished(Session *session, guint len, gpointer data) 947 { 948 IncSession *inc_session = (IncSession *)data; 949 IncProgressDialog *inc_dialog; 950 951 cm_return_val_if_fail(inc_session != NULL, -1); 952 953 inc_dialog = (IncProgressDialog *)inc_session->data; 954 955 inc_recv_data_progressive(session, 0, 0, inc_session); 956 957 if (POP3_SESSION(session)->state == POP3_LOGOUT) { 958 inc_progress_dialog_update(inc_dialog, inc_session); 959 } 960 961 return 0; 962 } 963 964 static gint inc_recv_message(Session *session, const gchar *msg, gpointer data) 965 { 966 IncSession *inc_session = (IncSession *)data; 967 IncProgressDialog *inc_dialog; 968 969 cm_return_val_if_fail(inc_session != NULL, -1); 970 971 inc_dialog = (IncProgressDialog *)inc_session->data; 972 973 switch (POP3_SESSION(session)->state) { 974 case POP3_GETAUTH_USER: 975 case POP3_GETAUTH_PASS: 976 case POP3_GETRANGE_STAT: 977 case POP3_GETRANGE_LAST: 978 case POP3_GETRANGE_UIDL: 979 case POP3_GETSIZE_LIST: 980 inc_progress_dialog_update(inc_dialog, inc_session); 981 break; 982 case POP3_RETR: 983 inc_recv_data_progressive(session, 0, 0, inc_session); 984 break; 985 case POP3_LOGOUT: 986 inc_progress_dialog_update(inc_dialog, inc_session); 987 break; 988 default: 989 break; 990 } 991 992 return 0; 993 } 994 995 static gint inc_drop_message(Pop3Session *session, const gchar *file) 996 { 997 FolderItem *inbox; 998 FolderItem *dropfolder; 999 IncSession *inc_session = (IncSession *)(SESSION(session)->data); 1000 gint msgnum; 1001 1002 cm_return_val_if_fail(inc_session != NULL, -1); 1003 1004 if (session->ac_prefs->inbox) { 1005 inbox = folder_find_item_from_identifier 1006 (session->ac_prefs->inbox); 1007 if (!inbox) 1008 inbox = folder_get_default_inbox(); 1009 } else 1010 inbox = folder_get_default_inbox(); 1011 if (!inbox) { 1012 unlink(file); 1013 return -1; 1014 } 1015 1016 /* CLAWS: claws uses a global .processing folder for the filtering. */ 1017 dropfolder = folder_get_default_processing(session->ac_prefs->account_id); 1018 1019 /* add msg file to drop folder */ 1020 if ((msgnum = folder_item_add_msg( 1021 dropfolder, file, NULL, TRUE)) < 0) { 1022 unlink(file); 1023 return -1; 1024 } 1025 1026 return 0; 1027 } 1028 1029 static void inc_put_error(IncState istate, Pop3Session *session) 1030 { 1031 gchar *log_msg = NULL; 1032 gchar *err_msg = NULL; 1033 gboolean fatal_error = FALSE; 1034 1035 switch (istate) { 1036 case INC_CONNECT_ERROR: 1037 fatal_error = TRUE; 1038 err_msg = g_strdup_printf(_("Connection to %s:%d failed."), 1039 SESSION(session)->server, 1040 SESSION(session)->port); 1041 break; 1042 case INC_ERROR: 1043 log_msg = _("Error occurred while processing mail."); 1044 fatal_error = TRUE; 1045 if (session->error_msg) 1046 err_msg = g_strdup_printf 1047 (_("Error occurred while processing mail:\n%s"), 1048 session->error_msg); 1049 else 1050 err_msg = g_strdup(log_msg); 1051 break; 1052 case INC_NO_SPACE: 1053 log_msg = _("No disk space left."); 1054 err_msg = g_strdup(log_msg); 1055 fatal_error = TRUE; 1056 break; 1057 case INC_IO_ERROR: 1058 log_msg = _("Can't write file."); 1059 err_msg = g_strdup(log_msg); 1060 fatal_error = TRUE; 1061 break; 1062 case INC_SOCKET_ERROR: 1063 log_msg = _("Socket error."); 1064 err_msg = g_strdup_printf(_("Socket error on connection to %s:%d."), 1065 SESSION(session)->server, 1066 SESSION(session)->port); 1067 break; 1068 case INC_EOF: 1069 log_msg = _("Connection closed by the remote host."); 1070 err_msg = g_strdup_printf(_("Connection to %s:%d closed by the remote host."), 1071 SESSION(session)->server, 1072 SESSION(session)->port); 1073 break; 1074 case INC_LOCKED: 1075 log_msg = _("Mailbox is locked."); 1076 if (session->error_msg) 1077 err_msg = g_strdup_printf(_("Mailbox is locked:\n%s"), 1078 session->error_msg); 1079 else 1080 err_msg = g_strdup(log_msg); 1081 break; 1082 case INC_AUTH_FAILED: 1083 log_msg = _("Authentication failed."); 1084 fatal_error = TRUE; 1085 if (session->error_msg) 1086 err_msg = g_strdup_printf 1087 (_("Authentication failed:\n%s"), session->error_msg); 1088 else 1089 err_msg = g_strdup(log_msg); 1090 break; 1091 case INC_TIMEOUT: 1092 log_msg = _("Session timed out. You may be able to " 1093 "recover by increasing the timeout value in " 1094 "Preferences/Other/Miscellaneous."); 1095 err_msg = g_strdup_printf(_("Connection to %s:%d timed out."), 1096 SESSION(session)->server, 1097 SESSION(session)->port); 1098 break; 1099 default: 1100 break; 1101 } 1102 1103 if (log_msg) { 1104 if (fatal_error) 1105 log_error(LOG_PROTOCOL, "%s\n", log_msg); 1106 else 1107 log_warning(LOG_PROTOCOL, "%s\n", log_msg); 1108 } 1109 1110 if (err_msg) { 1111 alertpanel_error_log("%s", err_msg); 1112 g_free(err_msg); 1113 } 1114 } 1115 1116 static void inc_cancel(IncProgressDialog *dialog) 1117 { 1118 IncSession *session; 1119 1120 cm_return_if_fail(dialog != NULL); 1121 1122 if (dialog->queue_list == NULL) { 1123 inc_progress_dialog_destroy(dialog); 1124 return; 1125 } 1126 1127 session = dialog->queue_list->data; 1128 1129 session->inc_state = INC_CANCEL; 1130 1131 log_message(LOG_PROTOCOL, _("Incorporation cancelled\n")); 1132 } 1133 1134 gboolean inc_is_active(void) 1135 { 1136 return (inc_dialog_list != NULL); 1137 } 1138 1139 void inc_cancel_all(void) 1140 { 1141 GList *cur; 1142 1143 for (cur = inc_dialog_list; cur != NULL; cur = cur->next) 1144 inc_cancel((IncProgressDialog *)cur->data); 1145 } 1146 1147 static void inc_showlog_cb(GtkWidget *widget, gpointer data) 1148 { 1149 MainWindow *mainwin = mainwindow_get_mainwindow(); 1150 1151 log_window_show(mainwin->logwin); 1152 } 1153 1154 static void inc_cancel_cb(GtkWidget *widget, gpointer data) 1155 { 1156 inc_cancel((IncProgressDialog *)data); 1157 } 1158 1159 static gint inc_dialog_delete_cb(GtkWidget *widget, GdkEventAny *event, 1160 gpointer data) 1161 { 1162 IncProgressDialog *dialog = (IncProgressDialog *)data; 1163 1164 if (dialog->queue_list == NULL) 1165 inc_progress_dialog_destroy(dialog); 1166 1167 return TRUE; 1168 } 1169 1170 static gint inc_spool_account(PrefsAccount *account) 1171 { 1172 FolderItem *inbox; 1173 gchar *mbox; 1174 gint result; 1175 1176 if (account->local_inbox) { 1177 inbox = folder_find_item_from_identifier(account->local_inbox); 1178 if (!inbox) 1179 inbox = folder_get_default_inbox(); 1180 } else 1181 inbox = folder_get_default_inbox(); 1182 1183 if (account->local_mbox) { 1184 if (is_file_exist(account->local_mbox)) 1185 mbox = g_strdup(account->local_mbox); 1186 else if (is_dir_exist(account->local_mbox)) 1187 mbox = g_strconcat(account->local_mbox, G_DIR_SEPARATOR_S, 1188 g_get_user_name(), NULL); 1189 else { 1190 debug_print("%s: local mailbox not found.\n", 1191 account->local_mbox); 1192 return -1; 1193 } 1194 } else { 1195 debug_print("local mailbox not set in account info.\n"); 1196 return -1; 1197 } 1198 1199 result = get_spool(inbox, mbox, account); 1200 g_free(mbox); 1201 1202 statusbar_pop_all(); 1203 1204 return result; 1205 } 1206 1207 static gint get_spool(FolderItem *dest, const gchar *mbox, PrefsAccount *account) 1208 { 1209 gint msgs, size; 1210 gchar tmp_mbox[MAXPATHLEN + 1]; 1211 1212 cm_return_val_if_fail(dest != NULL, -1); 1213 cm_return_val_if_fail(mbox != NULL, -1); 1214 cm_return_val_if_fail(account != NULL, -1); 1215 1216 if (!is_file_exist(mbox) || (size = get_file_size(mbox)) == 0) { 1217 debug_print("%s: no messages\n", mbox); 1218 return 0; 1219 } else if (size < 0) 1220 return -1; 1221 1222 g_snprintf(tmp_mbox, sizeof(tmp_mbox), "%s/tmpmbox.%p", get_tmp_dir(), mbox); 1223 1224 int spool = open(mbox, O_RDONLY); 1225 if (flock(spool, LOCK_EX|LOCK_NB) < 0) { 1226 perror("lock mbox"); 1227 return -1; 1228 } 1229 debug_print("Getting new messages from %s into %s...\n", mbox, dest->path); 1230 if (copy_mbox(spool, tmp_mbox) < 0) { 1231 perror("copy mbox"); 1232 flock(spool, LOCK_UN); 1233 close(spool); 1234 return -1; 1235 } 1236 close(spool); 1237 1238 msgs = proc_mbox(dest, tmp_mbox, account); 1239 unlink(tmp_mbox); 1240 if (msgs >= 0) { 1241 if (truncate(mbox, 0) < 0) 1242 perror("truncate mbox"); 1243 } 1244 1245 flock(spool, LOCK_UN); 1246 1247 return msgs; 1248 } 1249 1250 void inc_lock_real(void) 1251 { 1252 inc_lock_count++; 1253 } 1254 1255 void inc_unlock_real(void) 1256 { 1257 if (inc_lock_count > 0) 1258 inc_lock_count--; 1259 } 1260 1261 static guint autocheck_timer = 0; 1262 static gpointer autocheck_data = NULL; 1263 1264 static void inc_notify_cmd(gint new_msgs, gboolean notify) 1265 { 1266 gchar *buf, *numpos, *ret_str; 1267 gssize by_read = 0, by_written = 0; 1268 1269 if (!(new_msgs && notify && prefs_common.newmail_notify_cmd && 1270 *prefs_common.newmail_notify_cmd)) 1271 return; 1272 1273 buf = g_strdup(prefs_common.newmail_notify_cmd); 1274 if ((numpos = strstr(buf, "%d")) != NULL) { 1275 gchar *buf2; 1276 1277 *numpos = '\0'; 1278 buf2 = g_strdup_printf("%s%d%s", buf, new_msgs, numpos + 2); 1279 g_free(buf); 1280 buf = buf2; 1281 } 1282 1283 ret_str = g_locale_from_utf8(buf, strlen(buf), &by_read, &by_written, 1284 NULL); 1285 if (ret_str) { 1286 if (by_written) { 1287 g_free(buf); 1288 buf = ret_str; 1289 } else 1290 g_free(ret_str); 1291 } 1292 debug_print("executing new mail notification command: %s\n", buf); 1293 execute_command_line(buf, TRUE, NULL); 1294 1295 g_free(buf); 1296 } 1297 1298 gboolean inc_offline_should_override(gboolean force_ask, const gchar *msg) 1299 { 1300 gint length = 10; /* seconds */ 1301 gint answer = G_ALERTDEFAULT; 1302 1303 if (force_ask) { 1304 inc_offline_overridden_no = (time_t)0; 1305 } 1306 1307 if (prefs_common.work_offline) { 1308 gchar *tmp = NULL; 1309 1310 if (time(NULL) - inc_offline_overridden_yes < length * 60) /* seconds */ 1311 return TRUE; 1312 else if (time(NULL) - inc_offline_overridden_no < length * 60) /* seconds */ 1313 return FALSE; 1314 1315 if (!force_ask) { 1316 char buf[8]; 1317 char *unit = "seconds"; 1318 1319 /* show the offline override time (length) using the must appropriate unit: 1320 the biggest unit possible (hours, minutes, seconds), provided that there 1321 is not inferior unit involved: 1 hour, 150 minutes, 25 minutes, 90 minutes, 1322 30 seconds, 90 seconds. */ 1323 if ((length / 3600) > 0) { /* hours? */ 1324 if (((length % 3600) % 60) == 0) { /* no seconds left? */ 1325 if ((length % 3600) > 0) { /* minutes left? */ 1326 length = length / 60; 1327 unit = "minute"; 1328 } else { 1329 length = length / 3600; 1330 unit = "hour"; 1331 } 1332 } /* else: seconds */ 1333 } else { 1334 if ((length / 60) > 0) { /* minutes left? */ 1335 if ((length % 60) == 0) { 1336 length = length / 60; 1337 unit = "minute"; 1338 } 1339 } /* else: seconds */ 1340 } 1341 strlcpy(buf, unit, sizeof(buf)); 1342 if (length > 1) 1343 strlcat(buf, "s", sizeof(buf)); 1344 tmp = g_strdup_printf("%s\nWorking offline. Override for %d %s?", msg, length, buf); 1345 } else 1346 tmp = g_strdup_printf("%sWorking offline. Override?", msg), 1347 1348 answer = alertpanel(_("Offline warning"), 1349 tmp, 1350 NULL, "Cancel", NULL, "Override", 1351 NULL, !force_ask? _("On_ly once"):NULL, ALERTFOCUS_SECOND); 1352 g_free(tmp); 1353 if (answer == G_ALERTALTERNATE) { 1354 inc_offline_overridden_yes = time(NULL); 1355 return TRUE; 1356 } else if (answer == G_ALERTDEFAULT) { 1357 if (!force_ask) 1358 inc_offline_overridden_no = time(NULL); 1359 return FALSE; 1360 } else { 1361 inc_reset_offline_override_timers(); 1362 return TRUE; 1363 } 1364 } 1365 return TRUE; 1366 } 1367 1368 void inc_reset_offline_override_timers() 1369 { 1370 debug_print("resetting offline override timers\n"); 1371 inc_offline_overridden_yes = (time_t)0; 1372 inc_offline_overridden_no = (time_t)0; 1373 }