procmsg.c (52455B)
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 "defs.h" 20 21 #include <glib.h> 22 #include <glib/gi18n.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <sys/param.h> 26 #include <ctype.h> 27 #include <math.h> 28 29 30 #include "main.h" 31 #include "utils.h" 32 #include "procmsg.h" 33 #include "procheader.h" 34 #include "send_message.h" 35 #include "procmime.h" 36 #include "statusbar.h" 37 #include "fence.h" 38 #include "folder.h" 39 #include "foldersel.h" 40 #include "prefs_common.h" 41 #include "account.h" 42 #include "alertpanel.h" 43 #include "hooks.h" 44 #include "msgcache.h" 45 #include "mainwindow.h" 46 #include "summaryview.h" 47 #include "log.h" 48 #include "inc.h" 49 #include "file-utils.h" 50 51 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr, 52 FolderItem *queue, gint msgnum, gboolean *queued_removed); 53 static void procmsg_update_unread_children (MsgInfo *info, 54 gboolean newly_marked); 55 enum 56 { 57 Q_SENDER = 0, 58 Q_SMTPSERVER = 1, 59 Q_RECIPIENTS = 2, 60 Q_NEWSGROUPS = 3, 61 Q_MAIL_ACCOUNT_ID = 4, 62 Q_NEWS_ACCOUNT_ID = 5, 63 Q_SAVE_COPY_FOLDER = 6, 64 Q_REPLY_MESSAGE_ID = 7, 65 Q_FWD_MESSAGE_ID = 8, 66 Q_PRIVACY_SYSTEM = 9, 67 Q_ENCRYPT = 10, 68 Q_ENCRYPT_DATA = 11, 69 Q_CLAWS_HDRS = 12, 70 Q_PRIVACY_SYSTEM_OLD = 13, 71 Q_ENCRYPT_OLD = 14, 72 Q_ENCRYPT_DATA_OLD = 15, 73 Q_CLAWS_HDRS_OLD = 16, 74 }; 75 76 void procmsg_msg_list_free(GSList *mlist) 77 { 78 GSList *cur; 79 MsgInfo *msginfo; 80 81 for (cur = mlist; cur != NULL; cur = cur->next) { 82 msginfo = (MsgInfo *)cur->data; 83 procmsg_msginfo_free(&msginfo); 84 } 85 g_slist_free(mlist); 86 } 87 88 MsgNumberList *procmsg_get_number_list_for_msgs(MsgInfoList *msglist) 89 { 90 GSList *cur = NULL; 91 GSList *nums = NULL; 92 93 for (cur = msglist; cur; cur = cur->next) { 94 MsgInfo *msg = (MsgInfo *)cur->data; 95 nums = g_slist_prepend(nums, GUINT_TO_POINTER(msg->msgnum)); 96 } 97 98 return g_slist_reverse(nums); 99 } 100 101 struct MarkSum { 102 gint *new_msgs; 103 gint *unread_msgs; 104 gint *total_msgs; 105 gint *min; 106 gint *max; 107 gint first; 108 }; 109 110 /* CLAWS subject threading: 111 112 in the first round it inserts subject lines in a 113 hashtable (subject <-> node) 114 115 the second round finishes the threads by attaching 116 matching subject lines to the one found in the 117 hashtable. will use the oldest node with the same 118 subject that is not more then thread_by_subject_max_age 119 days old (see subject_hashtable_lookup) 120 */ 121 122 static void subject_hashtable_insert(GHashTable *hashtable, GNode *node) 123 { 124 gchar *subject; 125 MsgInfo *msginfo; 126 GSList *list = NULL; 127 128 cm_return_if_fail(hashtable != NULL); 129 cm_return_if_fail(node != NULL); 130 msginfo = (MsgInfo *) node->data; 131 cm_return_if_fail(msginfo != NULL); 132 133 subject = msginfo->subject; 134 if (subject == NULL) 135 return; 136 137 subject += subject_get_prefix_length(subject); 138 139 list = g_hash_table_lookup(hashtable, subject); 140 list = g_slist_prepend(list, node); 141 g_hash_table_insert(hashtable, subject, list); 142 } 143 144 static GNode *subject_hashtable_lookup(GHashTable *hashtable, MsgInfo *msginfo) 145 { 146 gchar *subject; 147 GSList *list, *cur; 148 GNode *node = NULL, *hashtable_node = NULL; 149 gint prefix_length; 150 MsgInfo *hashtable_msginfo = NULL, *best_msginfo = NULL; 151 gboolean match; 152 153 cm_return_val_if_fail(hashtable != NULL, NULL); 154 155 subject = msginfo->subject; 156 if (subject == NULL) 157 return NULL; 158 prefix_length = subject_get_prefix_length(subject); 159 if (prefix_length <= 0) 160 return NULL; 161 subject += prefix_length; 162 163 list = g_hash_table_lookup(hashtable, subject); 164 if (list == NULL) 165 return NULL; 166 167 /* check all nodes with the same subject to find the best parent */ 168 for (cur = list; cur; cur = cur->next) { 169 hashtable_node = (GNode *)cur->data; 170 hashtable_msginfo = (MsgInfo *) hashtable_node->data; 171 match = FALSE; 172 173 /* best node should be the oldest in the found nodes */ 174 /* parent node must not be older then msginfo */ 175 if ((hashtable_msginfo->date_t < msginfo->date_t) && 176 ((best_msginfo == NULL) || 177 (best_msginfo->date_t > hashtable_msginfo->date_t))) 178 match = TRUE; 179 180 /* parent node must not be more then thread_by_subject_max_age 181 days older then msginfo */ 182 if (fabs(difftime(msginfo->date_t, hashtable_msginfo->date_t)) > 183 prefs_common.thread_by_subject_max_age * 3600 * 24) 184 match = FALSE; 185 186 /* can add new tests for all matching 187 nodes found by subject */ 188 189 if (match) { 190 node = hashtable_node; 191 best_msginfo = hashtable_msginfo; 192 } 193 } 194 195 return node; 196 } 197 198 static void subject_hashtable_free(gpointer key, gpointer value, gpointer data) 199 { 200 g_slist_free(value); 201 } 202 203 /* return the reversed thread tree */ 204 GNode *procmsg_get_thread_tree(GSList *mlist) 205 { 206 GNode *root, *parent, *node, *next; 207 GHashTable *msgid_table; 208 GHashTable *subject_hashtable = NULL; 209 MsgInfo *msginfo; 210 const gchar *msgid; 211 GSList *reflist; 212 root = g_node_new(NULL); 213 msgid_table = g_hash_table_new(g_str_hash, g_str_equal); 214 215 if (prefs_common.thread_by_subject) { 216 subject_hashtable = g_hash_table_new(g_str_hash, g_str_equal); 217 } 218 219 for (; mlist != NULL; mlist = mlist->next) { 220 msginfo = (MsgInfo *)mlist->data; 221 parent = root; 222 223 if (msginfo->inreplyto) { 224 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto); 225 if (parent == NULL) { 226 parent = root; 227 } 228 } 229 node = g_node_insert_data_before 230 (parent, parent == root ? parent->children : NULL, 231 msginfo); 232 if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL) 233 g_hash_table_insert(msgid_table, (gchar *)msgid, node); 234 235 /* CLAWS: add subject to hashtable (without prefix) */ 236 if (prefs_common.thread_by_subject) { 237 subject_hashtable_insert(subject_hashtable, node); 238 } 239 } 240 241 /* complete the unfinished threads */ 242 for (node = root->children; node != NULL; ) { 243 next = node->next; 244 msginfo = (MsgInfo *)node->data; 245 parent = NULL; 246 247 if (msginfo->inreplyto) 248 parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto); 249 250 /* try looking for the indirect parent */ 251 if (!parent && msginfo->references) { 252 for (reflist = msginfo->references; 253 reflist != NULL; reflist = reflist->next) 254 if ((parent = g_hash_table_lookup 255 (msgid_table, reflist->data)) != NULL) 256 break; 257 } 258 259 /* node should not be the parent, and node should not 260 be an ancestor of parent (circular reference) */ 261 if (parent && parent != node && 262 !g_node_is_ancestor(node, parent)) { 263 g_node_unlink(node); 264 g_node_insert_before 265 (parent, parent->children, node); 266 } 267 268 node = next; 269 } 270 271 if (prefs_common.thread_by_subject) { 272 for (node = root->children; node && node != NULL;) { 273 next = node->next; 274 msginfo = (MsgInfo *) node->data; 275 276 parent = subject_hashtable_lookup(subject_hashtable, msginfo); 277 278 /* the node may already be threaded by IN-REPLY-TO, so go up 279 * in the tree to 280 find the parent node */ 281 if (parent != NULL) { 282 if (g_node_is_ancestor(node, parent)) 283 parent = NULL; 284 if (parent == node) 285 parent = NULL; 286 } 287 288 if (parent) { 289 g_node_unlink(node); 290 g_node_append(parent, node); 291 } 292 293 node = next; 294 } 295 } 296 297 if (prefs_common.thread_by_subject) 298 { 299 g_hash_table_foreach(subject_hashtable, subject_hashtable_free, NULL); 300 g_hash_table_destroy(subject_hashtable); 301 } 302 303 g_hash_table_destroy(msgid_table); 304 return root; 305 } 306 307 gint procmsg_move_messages(GSList *mlist) 308 { 309 GSList *cur, *movelist = NULL; 310 MsgInfo *msginfo; 311 FolderItem *dest = NULL; 312 gint retval = 0; 313 gboolean finished = TRUE; 314 if (!mlist) return 0; 315 316 folder_item_update_freeze(); 317 318 next_folder: 319 for (cur = mlist; cur != NULL; cur = cur->next) { 320 msginfo = (MsgInfo *)cur->data; 321 if (!msginfo->to_folder) { 322 continue; 323 } else { 324 finished = FALSE; 325 } 326 if (!dest) { 327 dest = msginfo->to_folder; 328 movelist = g_slist_prepend(movelist, msginfo); 329 } else if (dest == msginfo->to_folder) { 330 movelist = g_slist_prepend(movelist, msginfo); 331 } else { 332 continue; 333 } 334 procmsg_msginfo_set_to_folder(msginfo, NULL); 335 } 336 if (movelist) { 337 movelist = g_slist_reverse(movelist); 338 retval |= folder_item_move_msgs(dest, movelist); 339 g_slist_free(movelist); 340 movelist = NULL; 341 } 342 if (finished == FALSE) { 343 finished = TRUE; 344 dest = NULL; 345 goto next_folder; 346 } 347 348 folder_item_update_thaw(); 349 return retval; 350 } 351 352 void procmsg_copy_messages(GSList *mlist) 353 { 354 GSList *cur, *copylist = NULL; 355 MsgInfo *msginfo; 356 FolderItem *dest = NULL; 357 gboolean finished = TRUE; 358 if (!mlist) return; 359 360 folder_item_update_freeze(); 361 362 next_folder: 363 for (cur = mlist; cur != NULL; cur = cur->next) { 364 msginfo = (MsgInfo *)cur->data; 365 if (!msginfo->to_folder) { 366 continue; 367 } else { 368 finished = FALSE; 369 } 370 if (!dest) { 371 dest = msginfo->to_folder; 372 copylist = g_slist_prepend(copylist, msginfo); 373 } else if (dest == msginfo->to_folder) { 374 copylist = g_slist_prepend(copylist, msginfo); 375 } else { 376 continue; 377 } 378 procmsg_msginfo_set_to_folder(msginfo, NULL); 379 } 380 if (copylist) { 381 copylist = g_slist_reverse(copylist); 382 folder_item_copy_msgs(dest, copylist); 383 g_slist_free(copylist); 384 copylist = NULL; 385 } 386 if (finished == FALSE) { 387 finished = TRUE; 388 dest = NULL; 389 goto next_folder; 390 } 391 392 folder_item_update_thaw(); 393 } 394 395 gchar *procmsg_get_message_file_path(MsgInfo *msginfo) 396 { 397 cm_return_val_if_fail(msginfo != NULL, NULL); 398 399 return folder_item_fetch_msg(msginfo->folder, msginfo->msgnum); 400 } 401 402 gchar *procmsg_get_message_file(MsgInfo *msginfo) 403 { 404 gchar *filename = NULL; 405 406 cm_return_val_if_fail(msginfo != NULL, NULL); 407 408 filename = folder_item_fetch_msg(msginfo->folder, msginfo->msgnum); 409 if (!filename) 410 debug_print("can't fetch message %d\n", msginfo->msgnum); 411 412 return filename; 413 } 414 415 gchar *procmsg_get_message_file_full(MsgInfo *msginfo, gboolean headers, gboolean body) 416 { 417 gchar *filename = NULL; 418 419 cm_return_val_if_fail(msginfo != NULL, NULL); 420 421 filename = folder_item_fetch_msg_full(msginfo->folder, msginfo->msgnum, 422 headers, body); 423 if (!filename) 424 debug_print("can't fetch message %d\n", msginfo->msgnum); 425 426 return filename; 427 } 428 429 GSList *procmsg_get_message_file_list(GSList *mlist) 430 { 431 GSList *file_list = NULL; 432 MsgInfo *msginfo; 433 MsgFileInfo *fileinfo; 434 gchar *file; 435 436 while (mlist != NULL) { 437 msginfo = (MsgInfo *)mlist->data; 438 file = procmsg_get_message_file(msginfo); 439 if (!file) { 440 procmsg_message_file_list_free(file_list); 441 return NULL; 442 } 443 fileinfo = g_new(MsgFileInfo, 1); 444 fileinfo->msginfo = procmsg_msginfo_new_ref(msginfo); 445 fileinfo->file = file; 446 fileinfo->flags = g_new(MsgFlags, 1); 447 *fileinfo->flags = msginfo->flags; 448 file_list = g_slist_prepend(file_list, fileinfo); 449 mlist = mlist->next; 450 } 451 452 file_list = g_slist_reverse(file_list); 453 454 return file_list; 455 } 456 457 void procmsg_message_file_list_free(MsgInfoList *file_list) 458 { 459 GSList *cur; 460 MsgFileInfo *fileinfo; 461 462 for (cur = file_list; cur != NULL; cur = cur->next) { 463 fileinfo = (MsgFileInfo *)cur->data; 464 procmsg_msginfo_free(&(fileinfo->msginfo)); 465 g_free(fileinfo->file); 466 g_free(fileinfo->flags); 467 g_free(fileinfo); 468 } 469 470 g_slist_free(file_list); 471 } 472 473 FILE *procmsg_open_message(MsgInfo *msginfo, gboolean skip_special_headers) 474 { 475 FILE *fp; 476 gchar *file; 477 478 cm_return_val_if_fail(msginfo != NULL, NULL); 479 480 file = procmsg_get_message_file_path(msginfo); 481 cm_return_val_if_fail(file != NULL, NULL); 482 483 if (!is_file_exist(file)) { 484 g_free(file); 485 file = procmsg_get_message_file(msginfo); 486 if (!file) 487 return NULL; 488 } 489 490 if ((fp = g_fopen(file, "rb")) == NULL) { 491 FILE_OP_ERROR(file, "g_fopen"); 492 g_free(file); 493 return NULL; 494 } 495 496 g_free(file); 497 498 if (MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags) || 499 skip_special_headers == TRUE) { 500 gchar buf[BUFFSIZE]; 501 502 while (fgets(buf, sizeof(buf), fp) != NULL) { 503 /* new way */ 504 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1", 505 strlen("X-Claws-End-Special-Headers:"))) || 506 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1", 507 strlen("X-Sylpheed-End-Special-Headers:")))) 508 break; 509 /* old way */ 510 if (buf[0] == '\r' || buf[0] == '\n') break; 511 /* from other mailers */ 512 if (!strncmp(buf, "Date: ", 6) 513 || !strncmp(buf, "To: ", 4) 514 || !strncmp(buf, "From: ", 6) 515 || !strncmp(buf, "Subject: ", 9)) { 516 rewind(fp); 517 break; 518 } 519 } 520 } 521 522 return fp; 523 } 524 525 gboolean procmsg_msg_exist(MsgInfo *msginfo) 526 { 527 gboolean ret; 528 529 if (!msginfo) return FALSE; 530 531 ret = !folder_item_is_msg_changed(msginfo->folder, msginfo); 532 533 return ret; 534 } 535 536 static void procmsg_empty_trash(FolderItem *trash) 537 { 538 GNode *node, *next; 539 540 if (!trash || 541 (trash->stype != F_TRASH && 542 !folder_has_parent_of_type(trash, F_TRASH))) 543 return; 544 545 if (trash && trash->total_msgs > 0) { 546 GSList *mlist = folder_item_get_msg_list(trash); 547 GSList *cur; 548 for (cur = mlist ; cur != NULL ; cur = cur->next) { 549 MsgInfo * msginfo = (MsgInfo *) cur->data; 550 if (MSG_IS_LOCKED(msginfo->flags)) { 551 procmsg_msginfo_free(&msginfo); 552 continue; 553 } 554 procmsg_msginfo_free(&msginfo); 555 } 556 g_slist_free(mlist); 557 folder_item_remove_all_msg(trash); 558 } 559 560 if (!trash->node || !trash->node->children) 561 return; 562 563 node = trash->node->children; 564 while (node != NULL) { 565 next = node->next; 566 procmsg_empty_trash(FOLDER_ITEM(node->data)); 567 node = next; 568 } 569 } 570 571 void procmsg_empty_all_trash(void) 572 { 573 FolderItem *trash; 574 GList *cur; 575 576 for (cur = folder_get_list(); cur != NULL; cur = cur->next) { 577 Folder *folder = FOLDER(cur->data); 578 trash = folder->trash; 579 procmsg_empty_trash(trash); 580 if (folder->account && folder->account->set_trash_folder && 581 folder_find_item_from_identifier(folder->account->trash_folder)) 582 procmsg_empty_trash( 583 folder_find_item_from_identifier(folder->account->trash_folder)); 584 } 585 } 586 587 static PrefsAccount *procmsg_get_account_from_file(const gchar *file) 588 { 589 PrefsAccount *mailac = NULL; 590 FILE *fp; 591 int hnum; 592 gchar *buf = NULL; 593 static HeaderEntry qentry[] = {{"S:", NULL, FALSE}, 594 {"SSV:", NULL, FALSE}, 595 {"R:", NULL, FALSE}, 596 {"NG:", NULL, FALSE}, 597 {"MAID:", NULL, FALSE}, 598 {"NAID:", NULL, FALSE}, 599 {"SCF:", NULL, FALSE}, 600 {"RMID:", NULL, FALSE}, 601 {"FMID:", NULL, FALSE}, 602 {"X-Claws-End-Special-Headers", NULL, FALSE}, 603 {NULL, NULL, FALSE}}; 604 605 cm_return_val_if_fail(file != NULL, NULL); 606 607 if ((fp = g_fopen(file, "rb")) == NULL) { 608 FILE_OP_ERROR(file, "g_fopen"); 609 return NULL; 610 } 611 612 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) { 613 gchar *p = buf + strlen(qentry[hnum].name); 614 615 if (hnum == Q_MAIL_ACCOUNT_ID) { 616 mailac = account_find_from_id(atoi(p)); 617 break; 618 } 619 g_free(buf); 620 buf = NULL; 621 } 622 if (buf) 623 g_free(buf); 624 fclose(fp); 625 return mailac; 626 } 627 628 gchar *procmsg_msginfo_get_identifier(MsgInfo *msginfo) 629 { 630 gchar *folder_id; 631 const gchar *msgid; 632 gchar *id; 633 634 cm_return_val_if_fail(msginfo != NULL, NULL); 635 folder_id = folder_item_get_identifier(msginfo->folder); 636 msgid = msginfo->msgid; 637 638 id = g_strconcat(folder_id, G_DIR_SEPARATOR_S, msgid, NULL); 639 640 g_free(folder_id); 641 642 return id; 643 } 644 645 MsgInfo *procmsg_get_msginfo_from_identifier(const gchar *id) 646 { 647 gchar *folder_id = g_strdup(id); 648 gchar *separator = strrchr(folder_id, G_DIR_SEPARATOR); 649 const gchar *msgid; 650 FolderItem *item; 651 MsgInfo *msginfo; 652 653 if (separator == NULL) { 654 g_free(folder_id); 655 return NULL; 656 } 657 658 *separator = '\0'; 659 msgid = separator + 1; 660 661 item = folder_find_item_from_identifier(folder_id); 662 663 if (item == NULL) { 664 g_free(folder_id); 665 return NULL; 666 } 667 668 msginfo = folder_item_get_msginfo_by_msgid(item, msgid); 669 g_free(folder_id); 670 671 return msginfo; 672 } 673 674 static GSList *procmsg_list_sort_by_account(FolderItem *queue, GSList *list) 675 { 676 GSList *result = NULL; 677 GSList *orig = NULL; 678 PrefsAccount *last_account = NULL; 679 MsgInfo *msg = NULL; 680 GSList *cur = NULL; 681 gboolean nothing_to_sort = TRUE; 682 683 if (!list) 684 return NULL; 685 686 orig = g_slist_copy(list); 687 688 msg = (MsgInfo *)orig->data; 689 690 for (cur = orig; cur; cur = cur->next) 691 debug_print("sort before %s\n", ((MsgInfo *)cur->data)->from); 692 693 debug_print("\n"); 694 695 parse_again: 696 nothing_to_sort = TRUE; 697 cur = orig; 698 while (cur) { 699 gchar *file = NULL; 700 PrefsAccount *ac = NULL; 701 msg = (MsgInfo *)cur->data; 702 file = folder_item_fetch_msg(queue, msg->msgnum); 703 ac = procmsg_get_account_from_file(file); 704 g_free(file); 705 706 if (last_account == NULL || (ac != NULL && ac == last_account)) { 707 result = g_slist_append(result, msg); 708 orig = g_slist_remove(orig, msg); 709 last_account = ac; 710 nothing_to_sort = FALSE; 711 goto parse_again; 712 } 713 cur = cur->next; 714 } 715 716 if (orig && g_slist_length(orig)) { 717 if (!last_account && nothing_to_sort) { 718 /* can't find an account for the rest of the list */ 719 cur = orig; 720 while (cur) { 721 result = g_slist_append(result, cur->data); 722 cur = cur->next; 723 } 724 } else { 725 last_account = NULL; 726 goto parse_again; 727 } 728 } 729 730 g_slist_free(orig); 731 732 for (cur = result; cur; cur = cur->next) 733 debug_print("sort after %s\n", ((MsgInfo *)cur->data)->from); 734 735 debug_print("\n"); 736 737 return result; 738 } 739 740 static gboolean procmsg_is_last_for_account(FolderItem *queue, MsgInfo *msginfo, GSList *elem) 741 { 742 gchar *file = folder_item_fetch_msg(queue, msginfo->msgnum); 743 PrefsAccount *ac = procmsg_get_account_from_file(file); 744 GSList *cur; 745 g_free(file); 746 for (cur = elem; cur; cur = cur->next) { 747 MsgInfo *cur_msginfo = (MsgInfo *)cur->data; 748 file = folder_item_fetch_msg(queue, cur_msginfo->msgnum); 749 750 if (cur_msginfo != msginfo && !MSG_IS_LOCKED(cur_msginfo->flags)) { 751 if (procmsg_get_account_from_file(file) == ac) { 752 g_free(file); 753 return FALSE; 754 } 755 } 756 757 g_free(file); 758 } 759 return TRUE; 760 } 761 762 static gboolean send_queue_lock = FALSE; 763 764 gboolean procmsg_queue_lock(char **errstr) 765 { 766 if (send_queue_lock) { 767 /* Avoid having to translate two similar strings */ 768 log_warning(LOG_PROTOCOL, "%s\n", _("Already trying to send.")); 769 if (errstr) { 770 if (*errstr) g_free(*errstr); 771 *errstr = g_strdup_printf(_("Already trying to send.")); 772 } 773 return FALSE; 774 } 775 send_queue_lock = TRUE; 776 return TRUE; 777 } 778 void procmsg_queue_unlock(void) 779 { 780 send_queue_lock = FALSE; 781 } 782 /*! 783 *\brief Send messages in queue 784 * 785 *\param queue Queue folder to process 786 *\param save_msgs Unused 787 * 788 *\return Number of messages sent, negative if an error occurred 789 * positive if no error occurred 790 */ 791 gint procmsg_send_queue(FolderItem *queue, gboolean save_msgs, gchar **errstr) 792 { 793 gint sent = 0, err = 0; 794 GSList *list, *elem; 795 GSList *sorted_list = NULL; 796 GNode *node, *next; 797 798 if (!procmsg_queue_lock(errstr)) { 799 main_window_set_menu_sensitive(mainwindow_get_mainwindow()); 800 toolbar_main_set_sensitive(mainwindow_get_mainwindow()); 801 return -1; 802 } 803 inc_lock(); 804 if (!queue) 805 queue = folder_get_default_queue(); 806 807 if (queue == NULL) { 808 procmsg_queue_unlock(); 809 inc_unlock(); 810 return -1; 811 } 812 813 main_window_set_menu_sensitive(mainwindow_get_mainwindow()); 814 toolbar_main_set_sensitive(mainwindow_get_mainwindow()); 815 816 folder_item_scan(queue); 817 list = folder_item_get_msg_list(queue); 818 819 /* sort the list per sender account; this helps reusing the same SMTP server */ 820 sorted_list = procmsg_list_sort_by_account(queue, list); 821 822 for (elem = sorted_list; elem != NULL; elem = elem->next) { 823 gchar *file; 824 MsgInfo *msginfo; 825 826 msginfo = (MsgInfo *)(elem->data); 827 if (!MSG_IS_LOCKED(msginfo->flags) && !MSG_IS_DELETED(msginfo->flags)) { 828 file = folder_item_fetch_msg(queue, msginfo->msgnum); 829 if (file) { 830 gboolean queued_removed = FALSE; 831 if (procmsg_send_message_queue_full(file, 832 !procmsg_is_last_for_account(queue, msginfo, elem), 833 errstr, queue, msginfo->msgnum, &queued_removed) < 0) { 834 g_warning("sending queued message %d failed", 835 msginfo->msgnum); 836 err++; 837 } else { 838 sent++; 839 if (!queued_removed) 840 folder_item_remove_msg(queue, msginfo->msgnum); 841 } 842 g_free(file); 843 } 844 } 845 /* FIXME: supposedly if only one message is locked, and queue 846 * is being flushed, the following free says something like 847 * "freeing msg ## in folder (nil)". */ 848 procmsg_msginfo_free(&msginfo); 849 } 850 851 g_slist_free(sorted_list); 852 folder_item_scan(queue); 853 854 if (queue->node && queue->node->children) { 855 node = queue->node->children; 856 while (node != NULL) { 857 int res = 0; 858 next = node->next; 859 send_queue_lock = FALSE; 860 res = procmsg_send_queue(FOLDER_ITEM(node->data), save_msgs, errstr); 861 send_queue_lock = TRUE; 862 if (res < 0) 863 err = -res; 864 else 865 sent += res; 866 node = next; 867 } 868 } 869 procmsg_queue_unlock(); 870 inc_unlock(); 871 main_window_set_menu_sensitive(mainwindow_get_mainwindow()); 872 toolbar_main_set_sensitive(mainwindow_get_mainwindow()); 873 874 return (err != 0 ? -err : sent); 875 } 876 877 gboolean procmsg_is_sending(void) 878 { 879 return send_queue_lock; 880 } 881 882 /*! 883 *\brief Determine if a queue folder is empty 884 * 885 *\param queue Queue folder to process 886 * 887 *\return TRUE if the queue folder is empty, otherwise return FALSE 888 */ 889 gboolean procmsg_queue_is_empty(FolderItem *queue) 890 { 891 GSList *list; 892 gboolean res = FALSE; 893 if (!queue) 894 queue = folder_get_default_queue(); 895 cm_return_val_if_fail(queue != NULL, TRUE); 896 897 folder_item_scan(queue); 898 list = folder_item_get_msg_list(queue); 899 res = (list == NULL); 900 procmsg_msg_list_free(list); 901 902 if (res == TRUE) { 903 GNode *node, *next; 904 if (queue->node && queue->node->children) { 905 node = queue->node->children; 906 while (node != NULL) { 907 next = node->next; 908 if (!procmsg_queue_is_empty(FOLDER_ITEM(node->data))) 909 return FALSE; 910 node = next; 911 } 912 } 913 } 914 return res; 915 } 916 917 gint procmsg_save_to_outbox(FolderItem *outbox, const gchar *file) 918 { 919 gint num; 920 MsgInfo *msginfo, *tmp_msginfo; 921 MsgFlags flag = {0, 0}; 922 gchar *outbox_path = NULL; 923 924 if (!outbox) { 925 debug_print("using default outbox\n"); 926 outbox = folder_get_default_outbox(); 927 } 928 929 cm_return_val_if_fail(outbox != NULL, -1); 930 931 outbox_path = folder_item_get_path(outbox); 932 debug_print("attempting to save sent message to %s...\n", outbox_path); 933 g_free(outbox_path); 934 935 gchar tmp[MAXPATHLEN + 1]; 936 937 g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.out.%08x", 938 get_rc_dir(), G_DIR_SEPARATOR, (guint) rand()); 939 940 char *end = "X-Claws-End-Special-Headers:"; 941 if (fence_copy_after_line(tmp, file, end) == 0) 942 return -1; 943 944 while (folder_item_scan(outbox) < 0) { 945 outbox = foldersel_folder_sel(NULL, FOLDER_SEL_SAVE, NULL, FALSE, 946 _("Select the folder where you want to save the sent message")); 947 if (outbox == NULL) { 948 g_warning("not saving sent message"); 949 unlink(tmp); 950 return -1; 951 } 952 } 953 if ((num = folder_item_add_msg(outbox, tmp, &flag, TRUE)) < 0) { 954 g_warning("not saving sent message"); 955 outbox_path = folder_item_get_path(outbox); 956 alertpanel_warning(_("Could not save sent message to %s."), outbox_path); 957 unlink(tmp); 958 g_free(outbox_path); 959 return -1; 960 } 961 962 msginfo = folder_item_get_msginfo(outbox, num); /* refcnt++ */ 963 tmp_msginfo = procmsg_msginfo_get_full_info(msginfo); /* refcnt++ */ 964 if (msginfo != NULL) { 965 procmsg_msginfo_unset_flags(msginfo, ~0, 0); 966 procmsg_msginfo_free(&msginfo); /* refcnt-- */ 967 /* tmp_msginfo == msginfo */ 968 if (tmp_msginfo && msginfo->extradata && 969 (msginfo->extradata->dispositionnotificationto || 970 msginfo->extradata->returnreceiptto)) { 971 procmsg_msginfo_set_flags(msginfo, MSG_RETRCPT_SENT, 0); 972 } 973 procmsg_msginfo_free(&tmp_msginfo); /* refcnt-- */ 974 } 975 976 return 0; 977 } 978 979 980 MsgInfo *procmsg_msginfo_new_ref(MsgInfo *msginfo) 981 { 982 msginfo->refcnt++; 983 984 return msginfo; 985 } 986 987 MsgInfo *procmsg_msginfo_new(void) 988 { 989 MsgInfo *newmsginfo; 990 991 newmsginfo = g_new0(MsgInfo, 1); 992 newmsginfo->refcnt = 1; 993 994 return newmsginfo; 995 } 996 997 MsgInfo *procmsg_msginfo_copy(MsgInfo *msginfo) 998 { 999 MsgInfo *newmsginfo; 1000 GSList *refs; 1001 1002 if (msginfo == NULL) return NULL; 1003 1004 newmsginfo = g_new0(MsgInfo, 1); 1005 1006 newmsginfo->refcnt = 1; 1007 1008 #define MEMBCOPY(mmb) newmsginfo->mmb = msginfo->mmb 1009 #define MEMBDUP(mmb) newmsginfo->mmb = msginfo->mmb ? \ 1010 g_strdup(msginfo->mmb) : NULL 1011 1012 MEMBCOPY(msgnum); 1013 MEMBCOPY(size); 1014 MEMBCOPY(mtime); 1015 MEMBCOPY(date_t); 1016 1017 MEMBCOPY(flags); 1018 1019 MEMBDUP(fromname); 1020 1021 MEMBDUP(date); 1022 MEMBDUP(from); 1023 MEMBDUP(to); 1024 MEMBDUP(cc); 1025 MEMBDUP(subject); 1026 MEMBDUP(msgid); 1027 MEMBDUP(inreplyto); 1028 MEMBDUP(xref); 1029 1030 MEMBCOPY(folder); 1031 MEMBCOPY(to_folder); 1032 1033 if (msginfo->extradata) { 1034 newmsginfo->extradata = g_new0(MsgInfoExtraData, 1); 1035 MEMBDUP(extradata->dispositionnotificationto); 1036 MEMBDUP(extradata->returnreceiptto); 1037 MEMBDUP(extradata->partial_recv); 1038 MEMBDUP(extradata->account_server); 1039 MEMBDUP(extradata->account_login); 1040 MEMBDUP(extradata->list_post); 1041 MEMBDUP(extradata->list_subscribe); 1042 MEMBDUP(extradata->list_unsubscribe); 1043 MEMBDUP(extradata->list_help); 1044 MEMBDUP(extradata->list_archive); 1045 MEMBDUP(extradata->list_owner); 1046 MEMBDUP(extradata->resent_from); 1047 } 1048 1049 refs = msginfo->references; 1050 for (refs = msginfo->references; refs != NULL; refs = refs->next) { 1051 newmsginfo->references = g_slist_prepend 1052 (newmsginfo->references, g_strdup(refs->data)); 1053 } 1054 newmsginfo->references = g_slist_reverse(newmsginfo->references); 1055 1056 MEMBCOPY(score); 1057 MEMBDUP(plaintext_file); 1058 1059 return newmsginfo; 1060 } 1061 1062 MsgInfo *procmsg_msginfo_get_full_info_from_file(MsgInfo *msginfo, const gchar *file) 1063 { 1064 MsgInfo *full_msginfo; 1065 1066 if (msginfo == NULL) return NULL; 1067 1068 if (!file || !is_file_exist(file)) { 1069 g_warning("procmsg_msginfo_get_full_info_from_file(): can't get message file"); 1070 return NULL; 1071 } 1072 1073 full_msginfo = procheader_parse_file(file, msginfo->flags, TRUE, FALSE); 1074 if (!full_msginfo) return NULL; 1075 1076 msginfo->total_size = full_msginfo->total_size; 1077 msginfo->planned_download = full_msginfo->planned_download; 1078 1079 if (full_msginfo->extradata) { 1080 if (!msginfo->extradata) 1081 msginfo->extradata = g_new0(MsgInfoExtraData, 1); 1082 if (!msginfo->extradata->list_post) 1083 msginfo->extradata->list_post = g_strdup(full_msginfo->extradata->list_post); 1084 if (!msginfo->extradata->list_subscribe) 1085 msginfo->extradata->list_subscribe = g_strdup(full_msginfo->extradata->list_subscribe); 1086 if (!msginfo->extradata->list_unsubscribe) 1087 msginfo->extradata->list_unsubscribe = g_strdup(full_msginfo->extradata->list_unsubscribe); 1088 if (!msginfo->extradata->list_help) 1089 msginfo->extradata->list_help = g_strdup(full_msginfo->extradata->list_help); 1090 if (!msginfo->extradata->list_archive) 1091 msginfo->extradata->list_archive= g_strdup(full_msginfo->extradata->list_archive); 1092 if (!msginfo->extradata->list_owner) 1093 msginfo->extradata->list_owner = g_strdup(full_msginfo->extradata->list_owner); 1094 if (!msginfo->extradata->dispositionnotificationto) 1095 msginfo->extradata->dispositionnotificationto = 1096 g_strdup(full_msginfo->extradata->dispositionnotificationto); 1097 if (!msginfo->extradata->returnreceiptto) 1098 msginfo->extradata->returnreceiptto = g_strdup 1099 (full_msginfo->extradata->returnreceiptto); 1100 if (!msginfo->extradata->partial_recv && full_msginfo->extradata->partial_recv) 1101 msginfo->extradata->partial_recv = g_strdup(full_msginfo->extradata->partial_recv); 1102 if (!msginfo->extradata->account_server && full_msginfo->extradata->account_server) 1103 msginfo->extradata->account_server = g_strdup 1104 (full_msginfo->extradata->account_server); 1105 if (!msginfo->extradata->account_login && full_msginfo->extradata->account_login) 1106 msginfo->extradata->account_login = g_strdup 1107 (full_msginfo->extradata->account_login); 1108 if (!msginfo->extradata->resent_from && full_msginfo->extradata->resent_from) 1109 msginfo->extradata->resent_from = g_strdup 1110 (full_msginfo->extradata->resent_from); 1111 } 1112 procmsg_msginfo_free(&full_msginfo); 1113 1114 return procmsg_msginfo_new_ref(msginfo); 1115 } 1116 1117 MsgInfo *procmsg_msginfo_get_full_info(MsgInfo *msginfo) 1118 { 1119 MsgInfo *full_msginfo; 1120 gchar *file; 1121 1122 if (msginfo == NULL) return NULL; 1123 1124 file = procmsg_get_message_file_path(msginfo); 1125 if (!file || !is_file_exist(file)) { 1126 g_free(file); 1127 file = procmsg_get_message_file(msginfo); 1128 } 1129 if (!file || !is_file_exist(file)) { 1130 g_warning("procmsg_msginfo_get_full_info(): can't get message file"); 1131 return NULL; 1132 } 1133 1134 full_msginfo = procmsg_msginfo_get_full_info_from_file(msginfo, file); 1135 g_free(file); 1136 return full_msginfo; 1137 } 1138 1139 #define FREENULL(n) { g_free(n); n = NULL; } 1140 void procmsg_msginfo_free(MsgInfo **msginfo_ptr) 1141 { 1142 MsgInfo *msginfo = *msginfo_ptr; 1143 1144 if (msginfo == NULL) return; 1145 1146 msginfo->refcnt--; 1147 if (msginfo->refcnt > 0) 1148 return; 1149 1150 if (msginfo->to_folder) { 1151 msginfo->to_folder->op_count--; 1152 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT); 1153 } 1154 1155 FREENULL(msginfo->fromspace); 1156 1157 FREENULL(msginfo->fromname); 1158 1159 FREENULL(msginfo->date); 1160 FREENULL(msginfo->from); 1161 FREENULL(msginfo->to); 1162 FREENULL(msginfo->cc); 1163 FREENULL(msginfo->subject); 1164 FREENULL(msginfo->msgid); 1165 FREENULL(msginfo->inreplyto); 1166 FREENULL(msginfo->xref); 1167 1168 if (msginfo->extradata) { 1169 FREENULL(msginfo->extradata->returnreceiptto); 1170 FREENULL(msginfo->extradata->dispositionnotificationto); 1171 FREENULL(msginfo->extradata->list_post); 1172 FREENULL(msginfo->extradata->list_subscribe); 1173 FREENULL(msginfo->extradata->list_unsubscribe); 1174 FREENULL(msginfo->extradata->list_help); 1175 FREENULL(msginfo->extradata->list_archive); 1176 FREENULL(msginfo->extradata->list_owner); 1177 FREENULL(msginfo->extradata->partial_recv); 1178 FREENULL(msginfo->extradata->account_server); 1179 FREENULL(msginfo->extradata->account_login); 1180 FREENULL(msginfo->extradata->resent_from); 1181 FREENULL(msginfo->extradata); 1182 } 1183 slist_free_strings_full(msginfo->references); 1184 msginfo->references = NULL; 1185 g_slist_free(msginfo->tags); 1186 msginfo->tags = NULL; 1187 1188 FREENULL(msginfo->plaintext_file); 1189 1190 g_free(msginfo); 1191 *msginfo_ptr = NULL; 1192 } 1193 #undef FREENULL 1194 1195 guint procmsg_msginfo_memusage(MsgInfo *msginfo) 1196 { 1197 guint memusage = 0; 1198 GSList *tmp; 1199 1200 memusage += sizeof(MsgInfo); 1201 if (msginfo->fromname) 1202 memusage += strlen(msginfo->fromname); 1203 if (msginfo->date) 1204 memusage += strlen(msginfo->date); 1205 if (msginfo->from) 1206 memusage += strlen(msginfo->from); 1207 if (msginfo->to) 1208 memusage += strlen(msginfo->to); 1209 if (msginfo->cc) 1210 memusage += strlen(msginfo->cc); 1211 if (msginfo->subject) 1212 memusage += strlen(msginfo->subject); 1213 if (msginfo->msgid) 1214 memusage += strlen(msginfo->msgid); 1215 if (msginfo->inreplyto) 1216 memusage += strlen(msginfo->inreplyto); 1217 1218 for (tmp = msginfo->references; tmp; tmp=tmp->next) { 1219 gchar *r = (gchar *)tmp->data; 1220 memusage += r?strlen(r):0 + sizeof(GSList); 1221 } 1222 if (msginfo->fromspace) 1223 memusage += strlen(msginfo->fromspace); 1224 1225 for (tmp = msginfo->tags; tmp; tmp=tmp->next) { 1226 memusage += sizeof(GSList); 1227 } 1228 if (msginfo->extradata) { 1229 memusage += sizeof(MsgInfoExtraData); 1230 if (msginfo->extradata->dispositionnotificationto) 1231 memusage += strlen(msginfo->extradata->dispositionnotificationto); 1232 if (msginfo->extradata->returnreceiptto) 1233 memusage += strlen(msginfo->extradata->returnreceiptto); 1234 1235 if (msginfo->extradata->partial_recv) 1236 memusage += strlen(msginfo->extradata->partial_recv); 1237 if (msginfo->extradata->account_server) 1238 memusage += strlen(msginfo->extradata->account_server); 1239 if (msginfo->extradata->account_login) 1240 memusage += strlen(msginfo->extradata->account_login); 1241 if (msginfo->extradata->resent_from) 1242 memusage += strlen(msginfo->extradata->resent_from); 1243 1244 if (msginfo->extradata->list_post) 1245 memusage += strlen(msginfo->extradata->list_post); 1246 if (msginfo->extradata->list_subscribe) 1247 memusage += strlen(msginfo->extradata->list_subscribe); 1248 if (msginfo->extradata->list_unsubscribe) 1249 memusage += strlen(msginfo->extradata->list_unsubscribe); 1250 if (msginfo->extradata->list_help) 1251 memusage += strlen(msginfo->extradata->list_help); 1252 if (msginfo->extradata->list_archive) 1253 memusage += strlen(msginfo->extradata->list_archive); 1254 if (msginfo->extradata->list_owner) 1255 memusage += strlen(msginfo->extradata->list_owner); 1256 } 1257 return memusage; 1258 } 1259 1260 static gint procmsg_send_message_queue_full(const gchar *file, gboolean keep_session, gchar **errstr, 1261 FolderItem *queue, gint msgnum, gboolean *queued_removed) 1262 { 1263 static HeaderEntry qentry[] = { 1264 {"S:", NULL, FALSE}, /* 0 */ 1265 {"SSV:", NULL, FALSE}, 1266 {"R:", NULL, FALSE}, 1267 {"NG:", NULL, FALSE}, 1268 {"MAID:", NULL, FALSE}, 1269 {"NAID:", NULL, FALSE}, /* 5 */ 1270 {"SCF:", NULL, FALSE}, 1271 {"RMID:", NULL, FALSE}, 1272 {"FMID:", NULL, FALSE}, 1273 {"X-Claws-End-Special-Headers:", NULL, FALSE}, 1274 {"X-Sylpheed-End-Special-Headers:", NULL, FALSE}, 1275 {NULL, NULL, FALSE}}; 1276 FILE *fp; 1277 gint filepos; 1278 gint mailval = 0; 1279 gchar *from = NULL; 1280 gchar *smtpserver = NULL; 1281 GSList *to_list = NULL; 1282 gchar *savecopyfolder = NULL; 1283 gchar *replymessageid = NULL; 1284 gchar *fwdmessageid = NULL; 1285 gchar *buf; 1286 gint hnum; 1287 PrefsAccount *mailac = NULL; 1288 FolderItem *outbox; 1289 1290 cm_return_val_if_fail(file != NULL, -1); 1291 1292 if ((fp = g_fopen(file, "rb")) == NULL) { 1293 FILE_OP_ERROR(file, "g_fopen"); 1294 if (errstr) { 1295 if (*errstr) g_free(*errstr); 1296 *errstr = g_strdup_printf(_("Couldn't open file %s."), file); 1297 } 1298 return -1; 1299 } 1300 1301 while ((hnum = procheader_get_one_field(&buf, fp, qentry)) != -1 && buf != NULL) { 1302 gchar *p = buf + strlen(qentry[hnum].name); 1303 1304 switch (hnum) { 1305 case Q_SENDER: 1306 if (from == NULL) 1307 from = g_strdup(p); 1308 break; 1309 case Q_SMTPSERVER: 1310 if (smtpserver == NULL) 1311 smtpserver = g_strdup(p); 1312 break; 1313 case Q_RECIPIENTS: 1314 to_list = address_list_append(to_list, p); 1315 break; 1316 case Q_MAIL_ACCOUNT_ID: 1317 mailac = account_find_from_id(atoi(p)); 1318 break; 1319 case Q_SAVE_COPY_FOLDER: 1320 if (savecopyfolder == NULL) 1321 savecopyfolder = g_strdup(p); 1322 break; 1323 case Q_REPLY_MESSAGE_ID: 1324 if (replymessageid == NULL) 1325 replymessageid = g_strdup(p); 1326 break; 1327 case Q_FWD_MESSAGE_ID: 1328 if (fwdmessageid == NULL) 1329 fwdmessageid = g_strdup(p); 1330 break; 1331 case Q_CLAWS_HDRS: 1332 case Q_CLAWS_HDRS_OLD: 1333 /* end of special headers reached */ 1334 g_free(buf); 1335 goto send_mail; /* can't "break;break;" */ 1336 } 1337 g_free(buf); 1338 } 1339 1340 send_mail: 1341 filepos = ftell(fp); 1342 if (filepos < 0) { 1343 FILE_OP_ERROR(file, "ftell"); 1344 if (errstr) { 1345 if (*errstr) g_free(*errstr); 1346 *errstr = g_strdup_printf(_("Couldn't open file %s."), file); 1347 } 1348 return -1; 1349 } 1350 1351 if (to_list) { 1352 debug_print("Sending message by mail\n"); 1353 if (!from) { 1354 if (errstr) { 1355 if (*errstr) g_free(*errstr); 1356 *errstr = g_strdup_printf(_("Queued message header is broken.")); 1357 } 1358 mailval = -1; 1359 } else if (mailac && mailac->use_mail_command && 1360 mailac->mail_command && (* mailac->mail_command)) { 1361 mailval = send_message_local(mailac->mail_command, fp); 1362 } else { 1363 if (!mailac) { 1364 mailac = account_find_from_smtp_server(from, smtpserver); 1365 if (!mailac) { 1366 g_warning("account not found, " 1367 "using current account..."); 1368 mailac = cur_account; 1369 } 1370 } 1371 1372 if (mailac) { 1373 mailval = send_message_smtp_full(mailac, to_list, fp, keep_session); 1374 if (mailval == -1 && errstr) { 1375 if (*errstr) g_free(*errstr); 1376 *errstr = g_strdup_printf(_("An error happened during SMTP session.")); 1377 } 1378 } else { 1379 PrefsAccount tmp_ac; 1380 1381 g_warning("account not found"); 1382 1383 memset(&tmp_ac, 0, sizeof(PrefsAccount)); 1384 tmp_ac.address = from; 1385 tmp_ac.smtp_server = smtpserver; 1386 tmp_ac.smtpport = SMTP_PORT; 1387 mailval = send_message_smtp(&tmp_ac, to_list, fp); 1388 if (mailval == -1 && errstr) { 1389 if (*errstr) g_free(*errstr); 1390 *errstr = g_strdup_printf(_("No specific account has been found to " 1391 "send, and an error happened during SMTP session.")); 1392 } 1393 } 1394 } 1395 } else if (!to_list) { 1396 if (errstr) { 1397 if (*errstr) g_free(*errstr); 1398 *errstr = g_strdup(_("Couldn't determine sending information. " 1399 "Maybe the email hasn't been generated by Claws Mail.")); 1400 } 1401 mailval = -1; 1402 } 1403 1404 if (fseek(fp, filepos, SEEK_SET) < 0) { 1405 FILE_OP_ERROR(file, "fseek"); 1406 mailval = -1; 1407 } 1408 1409 fclose(fp); 1410 1411 /* save message to outbox */ 1412 if (mailval == 0 && savecopyfolder) { 1413 debug_print("saving sent message to %s...\n", savecopyfolder); 1414 outbox = folder_find_item_from_identifier(savecopyfolder); 1415 if (!outbox) { 1416 gchar *id; 1417 outbox = folder_get_default_outbox(); 1418 if (outbox != NULL) { 1419 id = folder_item_get_identifier(outbox); 1420 debug_print("%s not found, using %s\n", savecopyfolder, id); 1421 g_free(id); 1422 } else { 1423 debug_print("could not find outbox\n"); 1424 } 1425 } 1426 gboolean saved = FALSE; 1427 *queued_removed = FALSE; 1428 if (queue && msgnum > 0) { 1429 MsgInfo *queued_mail = folder_item_get_msginfo(queue, msgnum); 1430 if (folder_item_move_msg(outbox, queued_mail) >= 0) { 1431 debug_print("moved queued mail %d to sent folder\n", msgnum); 1432 saved = TRUE; 1433 *queued_removed = TRUE; 1434 } else if (folder_item_copy_msg(outbox, queued_mail) >= 0) { 1435 debug_print("copied queued mail %d to sent folder\n", msgnum); 1436 saved = TRUE; 1437 } 1438 procmsg_msginfo_free(&queued_mail); 1439 } 1440 if (!saved) { 1441 debug_print("resaving queued mail to sent folder\n"); 1442 procmsg_save_to_outbox(outbox, file); 1443 } 1444 } 1445 1446 if (replymessageid != NULL || fwdmessageid != NULL) { 1447 gchar **tokens; 1448 FolderItem *item; 1449 1450 if (replymessageid != NULL) 1451 tokens = g_strsplit(replymessageid, "\t", 0); 1452 else 1453 tokens = g_strsplit(fwdmessageid, "\t", 0); 1454 item = folder_find_item_from_identifier(tokens[0]); 1455 1456 /* check if queued message has valid folder and message id */ 1457 if (item != NULL && tokens[2] != NULL) { 1458 MsgInfo *msginfo; 1459 1460 msginfo = folder_item_get_msginfo(item, atoi(tokens[1])); 1461 1462 /* check if referring message exists and has a message id */ 1463 if ((msginfo != NULL) && 1464 (msginfo->msgid != NULL) && 1465 (strcmp(msginfo->msgid, tokens[2]) != 0)) { 1466 procmsg_msginfo_free(&msginfo); 1467 msginfo = NULL; 1468 } 1469 1470 if (msginfo == NULL) { 1471 msginfo = folder_item_get_msginfo_by_msgid(item, tokens[2]); 1472 } 1473 1474 if (msginfo != NULL) { 1475 if (replymessageid != NULL) { 1476 MsgPermFlags to_unset = 0; 1477 1478 if (prefs_common.mark_as_read_on_new_window) 1479 to_unset = (MSG_NEW|MSG_UNREAD); 1480 1481 procmsg_msginfo_unset_flags(msginfo, to_unset, 0); 1482 procmsg_msginfo_set_flags(msginfo, MSG_REPLIED, 0); 1483 } else { 1484 procmsg_msginfo_set_flags(msginfo, MSG_FORWARDED, 0); 1485 } 1486 procmsg_msginfo_free(&msginfo); 1487 } 1488 } 1489 g_strfreev(tokens); 1490 } 1491 1492 g_free(from); 1493 g_free(smtpserver); 1494 slist_free_strings_full(to_list); 1495 g_free(savecopyfolder); 1496 g_free(replymessageid); 1497 g_free(fwdmessageid); 1498 1499 return (mailval); 1500 } 1501 1502 gint procmsg_send_message_queue(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed) 1503 { 1504 gint result = procmsg_send_message_queue_full(file, FALSE, errstr, queue, msgnum, queued_removed); 1505 main_window_set_menu_sensitive(mainwindow_get_mainwindow()); 1506 toolbar_main_set_sensitive(mainwindow_get_mainwindow()); 1507 return result; 1508 } 1509 1510 gint procmsg_send_message_queue_with_lock(const gchar *file, gchar **errstr, FolderItem *queue, gint msgnum, gboolean *queued_removed) 1511 { 1512 gint val; 1513 if (procmsg_queue_lock(errstr)) { 1514 val = procmsg_send_message_queue(file, errstr, queue, msgnum, queued_removed); 1515 procmsg_queue_unlock(); 1516 return val; 1517 } 1518 return -1; 1519 } 1520 1521 static void update_folder_msg_counts(FolderItem *item, MsgInfo *msginfo, MsgPermFlags old_flags) 1522 { 1523 MsgPermFlags new_flags = msginfo->flags.perm_flags; 1524 1525 /* NEW flag */ 1526 if (!(old_flags & MSG_NEW) && (new_flags & MSG_NEW)) { 1527 item->new_msgs++; 1528 } 1529 1530 if ((old_flags & MSG_NEW) && !(new_flags & MSG_NEW)) { 1531 item->new_msgs--; 1532 } 1533 1534 /* UNREAD flag */ 1535 if (!(old_flags & MSG_UNREAD) && (new_flags & MSG_UNREAD)) { 1536 item->unread_msgs++; 1537 if (procmsg_msg_has_marked_parent(msginfo)) 1538 item->unreadmarked_msgs++; 1539 } 1540 1541 if ((old_flags & MSG_UNREAD) && !(new_flags & MSG_UNREAD)) { 1542 item->unread_msgs--; 1543 if (procmsg_msg_has_marked_parent(msginfo)) 1544 item->unreadmarked_msgs--; 1545 } 1546 1547 /* MARK flag */ 1548 if (!(old_flags & MSG_MARKED) && (new_flags & MSG_MARKED)) { 1549 procmsg_update_unread_children(msginfo, TRUE); 1550 item->marked_msgs++; 1551 } 1552 1553 if ((old_flags & MSG_MARKED) && !(new_flags & MSG_MARKED)) { 1554 procmsg_update_unread_children(msginfo, FALSE); 1555 item->marked_msgs--; 1556 } 1557 1558 if (!(old_flags & MSG_REPLIED) && (new_flags & MSG_REPLIED)) { 1559 item->replied_msgs++; 1560 } 1561 1562 if ((old_flags & MSG_REPLIED) && !(new_flags & MSG_REPLIED)) { 1563 item->replied_msgs--; 1564 } 1565 1566 if (!(old_flags & MSG_FORWARDED) && (new_flags & MSG_FORWARDED)) { 1567 item->forwarded_msgs++; 1568 } 1569 1570 if ((old_flags & MSG_FORWARDED) && !(new_flags & MSG_FORWARDED)) { 1571 item->forwarded_msgs--; 1572 } 1573 1574 if (!(old_flags & MSG_LOCKED) && (new_flags & MSG_LOCKED)) { 1575 item->locked_msgs++; 1576 } 1577 1578 if ((old_flags & MSG_LOCKED) && !(new_flags & MSG_LOCKED)) { 1579 item->locked_msgs--; 1580 } 1581 1582 if ((old_flags & MSG_IGNORE_THREAD) && !(new_flags & MSG_IGNORE_THREAD)) { 1583 item->ignored_msgs--; 1584 } 1585 1586 if (!(old_flags & MSG_IGNORE_THREAD) && (new_flags & MSG_IGNORE_THREAD)) { 1587 item->ignored_msgs++; 1588 } 1589 1590 if ((old_flags & MSG_WATCH_THREAD) && !(new_flags & MSG_WATCH_THREAD)) { 1591 item->watched_msgs--; 1592 } 1593 1594 if (!(old_flags & MSG_WATCH_THREAD) && (new_flags & MSG_WATCH_THREAD)) { 1595 item->watched_msgs++; 1596 } 1597 } 1598 1599 void procmsg_msginfo_set_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags) 1600 { 1601 FolderItem *item; 1602 MsgInfoUpdate msginfo_update; 1603 MsgPermFlags perm_flags_new, perm_flags_old; 1604 MsgTmpFlags tmp_flags_old; 1605 1606 cm_return_if_fail(msginfo != NULL); 1607 item = msginfo->folder; 1608 cm_return_if_fail(item != NULL); 1609 1610 debug_print("Setting flags for message %d in folder %s\n", msginfo->msgnum, item->path); 1611 1612 /* Perm Flags handling */ 1613 perm_flags_old = msginfo->flags.perm_flags; 1614 perm_flags_new = msginfo->flags.perm_flags | perm_flags; 1615 if ((perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) { 1616 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD); 1617 } 1618 if ((perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) { 1619 perm_flags_new &= ~(MSG_IGNORE_THREAD); 1620 } 1621 1622 if (perm_flags_old != perm_flags_new) { 1623 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new); 1624 1625 update_folder_msg_counts(item, msginfo, perm_flags_old); 1626 summary_update_unread(mainwindow_get_mainwindow()->summaryview, NULL); 1627 } 1628 1629 /* Tmp flags handling */ 1630 tmp_flags_old = msginfo->flags.tmp_flags; 1631 msginfo->flags.tmp_flags |= tmp_flags; 1632 1633 /* update notification */ 1634 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) { 1635 msginfo_update.msginfo = msginfo; 1636 msginfo_update.flags = MSGINFO_UPDATE_FLAGS; 1637 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update); 1638 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT); 1639 } 1640 } 1641 1642 void procmsg_msginfo_unset_flags(MsgInfo *msginfo, MsgPermFlags perm_flags, MsgTmpFlags tmp_flags) 1643 { 1644 FolderItem *item; 1645 MsgInfoUpdate msginfo_update; 1646 MsgPermFlags perm_flags_new, perm_flags_old; 1647 MsgTmpFlags tmp_flags_old; 1648 1649 cm_return_if_fail(msginfo != NULL); 1650 item = msginfo->folder; 1651 cm_return_if_fail(item != NULL); 1652 1653 debug_print("Unsetting flags for message %d in folder %s\n", msginfo->msgnum, item->path); 1654 1655 /* Perm Flags handling */ 1656 perm_flags_old = msginfo->flags.perm_flags; 1657 perm_flags_new = msginfo->flags.perm_flags & ~perm_flags; 1658 1659 if (perm_flags_old != perm_flags_new) { 1660 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new); 1661 1662 update_folder_msg_counts(item, msginfo, perm_flags_old); 1663 } 1664 1665 /* Tmp flags hanlding */ 1666 tmp_flags_old = msginfo->flags.tmp_flags; 1667 msginfo->flags.tmp_flags &= ~tmp_flags; 1668 1669 /* update notification */ 1670 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) { 1671 msginfo_update.msginfo = msginfo; 1672 msginfo_update.flags = MSGINFO_UPDATE_FLAGS; 1673 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update); 1674 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT); 1675 } 1676 } 1677 1678 void procmsg_msginfo_change_flags(MsgInfo *msginfo, 1679 MsgPermFlags add_perm_flags, MsgTmpFlags add_tmp_flags, 1680 MsgPermFlags rem_perm_flags, MsgTmpFlags rem_tmp_flags) 1681 { 1682 FolderItem *item; 1683 MsgInfoUpdate msginfo_update; 1684 MsgPermFlags perm_flags_new, perm_flags_old; 1685 MsgTmpFlags tmp_flags_old; 1686 1687 cm_return_if_fail(msginfo != NULL); 1688 item = msginfo->folder; 1689 cm_return_if_fail(item != NULL); 1690 1691 debug_print("Changing flags for message %d in folder %s\n", msginfo->msgnum, item->path); 1692 1693 /* Perm Flags handling */ 1694 perm_flags_old = msginfo->flags.perm_flags; 1695 perm_flags_new = (msginfo->flags.perm_flags & ~rem_perm_flags) | add_perm_flags; 1696 if ((add_perm_flags & MSG_IGNORE_THREAD) || (perm_flags_old & MSG_IGNORE_THREAD)) { 1697 perm_flags_new &= ~(MSG_NEW | MSG_UNREAD); 1698 } 1699 if ((add_perm_flags & MSG_WATCH_THREAD) || (perm_flags_old & MSG_WATCH_THREAD)) { 1700 perm_flags_new &= ~(MSG_IGNORE_THREAD); 1701 } 1702 1703 if (perm_flags_old != perm_flags_new) { 1704 folder_item_change_msg_flags(msginfo->folder, msginfo, perm_flags_new); 1705 1706 update_folder_msg_counts(item, msginfo, perm_flags_old); 1707 1708 } 1709 1710 /* Tmp flags handling */ 1711 tmp_flags_old = msginfo->flags.tmp_flags; 1712 msginfo->flags.tmp_flags &= ~rem_tmp_flags; 1713 msginfo->flags.tmp_flags |= add_tmp_flags; 1714 1715 /* update notification */ 1716 if ((perm_flags_old != perm_flags_new) || (tmp_flags_old != msginfo->flags.tmp_flags)) { 1717 msginfo_update.msginfo = msginfo; 1718 msginfo_update.flags = MSGINFO_UPDATE_FLAGS; 1719 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update); 1720 folder_item_update(msginfo->folder, F_ITEM_UPDATE_MSGCNT); 1721 } 1722 } 1723 1724 /*! 1725 *\brief check for flags (e.g. mark) in prior msgs of current thread 1726 * 1727 *\param info Current message 1728 *\param perm_flags Flags to be checked 1729 *\param parentmsgs Hash of prior msgs to avoid loops 1730 * 1731 *\return gboolean TRUE if perm_flags are found 1732 */ 1733 static gboolean procmsg_msg_has_flagged_parent_real(MsgInfo *info, 1734 MsgPermFlags perm_flags, GHashTable *parentmsgs) 1735 { 1736 MsgInfo *tmp; 1737 1738 cm_return_val_if_fail(info != NULL, FALSE); 1739 1740 if (info != NULL && info->folder != NULL && info->inreplyto != NULL) { 1741 tmp = folder_item_get_msginfo_by_msgid(info->folder, 1742 info->inreplyto); 1743 if (tmp && (tmp->flags.perm_flags & perm_flags)) { 1744 procmsg_msginfo_free(&tmp); 1745 return TRUE; 1746 } else if (tmp != NULL) { 1747 gboolean result; 1748 1749 if (g_hash_table_lookup(parentmsgs, info)) { 1750 debug_print("loop detected: %d\n", 1751 info->msgnum); 1752 result = FALSE; 1753 } else { 1754 g_hash_table_insert(parentmsgs, info, "1"); 1755 result = procmsg_msg_has_flagged_parent_real( 1756 tmp, perm_flags, parentmsgs); 1757 } 1758 procmsg_msginfo_free(&tmp); 1759 return result; 1760 } else { 1761 return FALSE; 1762 } 1763 } else 1764 return FALSE; 1765 } 1766 1767 /*! 1768 *\brief Callback for cleaning up hash of parentmsgs 1769 */ 1770 static gboolean parentmsgs_hash_remove(gpointer key, 1771 gpointer value, 1772 gpointer user_data) 1773 { 1774 return TRUE; 1775 } 1776 1777 /*! 1778 *\brief Set up list of parentmsgs 1779 * See procmsg_msg_has_flagged_parent_real() 1780 */ 1781 gboolean procmsg_msg_has_flagged_parent(MsgInfo *info, MsgPermFlags perm_flags) 1782 { 1783 gboolean result; 1784 static GHashTable *parentmsgs = NULL; 1785 1786 if (parentmsgs == NULL) 1787 parentmsgs = g_hash_table_new(NULL, NULL); 1788 1789 result = procmsg_msg_has_flagged_parent_real(info, perm_flags, parentmsgs); 1790 g_hash_table_foreach_remove(parentmsgs, parentmsgs_hash_remove, NULL); 1791 1792 return result; 1793 } 1794 1795 /*! 1796 *\brief Check if msgs prior in thread are marked 1797 * See procmsg_msg_has_flagged_parent_real() 1798 */ 1799 gboolean procmsg_msg_has_marked_parent(MsgInfo *info) 1800 { 1801 return procmsg_msg_has_flagged_parent(info, MSG_MARKED); 1802 } 1803 1804 1805 static GSList *procmsg_find_children_func(MsgInfo *info, 1806 GSList *children, GSList *all) 1807 { 1808 GSList *cur; 1809 1810 cm_return_val_if_fail(info!=NULL, children); 1811 if (info->msgid == NULL) 1812 return children; 1813 1814 for (cur = all; cur != NULL; cur = g_slist_next(cur)) { 1815 MsgInfo *tmp = (MsgInfo *)cur->data; 1816 if (tmp->inreplyto && !strcmp(tmp->inreplyto, info->msgid)) { 1817 /* Check if message is already in the list */ 1818 if ((children == NULL) || 1819 (g_slist_index(children, tmp) == -1)) { 1820 children = g_slist_prepend(children, 1821 procmsg_msginfo_new_ref(tmp)); 1822 children = procmsg_find_children_func(tmp, 1823 children, 1824 all); 1825 } 1826 } 1827 } 1828 return children; 1829 } 1830 1831 static GSList *procmsg_find_children (MsgInfo *info) 1832 { 1833 GSList *children; 1834 GSList *all, *cur; 1835 1836 cm_return_val_if_fail(info!=NULL, NULL); 1837 all = folder_item_get_msg_list(info->folder); 1838 children = procmsg_find_children_func(info, NULL, all); 1839 if (children != NULL) { 1840 for (cur = all; cur != NULL; cur = g_slist_next(cur)) { 1841 /* this will not free the used pointers 1842 created with procmsg_msginfo_new_ref */ 1843 procmsg_msginfo_free((MsgInfo **)&(cur->data)); 1844 } 1845 } 1846 g_slist_free(all); 1847 1848 return children; 1849 } 1850 1851 static void procmsg_update_unread_children(MsgInfo *info, gboolean newly_marked) 1852 { 1853 GSList *children = procmsg_find_children(info); 1854 GSList *cur; 1855 for (cur = children; cur != NULL; cur = g_slist_next(cur)) { 1856 MsgInfo *tmp = (MsgInfo *)cur->data; 1857 if(MSG_IS_UNREAD(tmp->flags) && !MSG_IS_IGNORE_THREAD(tmp->flags)) { 1858 if(newly_marked) 1859 info->folder->unreadmarked_msgs++; 1860 else 1861 info->folder->unreadmarked_msgs--; 1862 folder_item_update(info->folder, F_ITEM_UPDATE_MSGCNT); 1863 } 1864 procmsg_msginfo_free(&tmp); 1865 } 1866 g_slist_free(children); 1867 } 1868 1869 /** 1870 * Set the destination folder for a copy or move operation 1871 * 1872 * \param msginfo The message which's destination folder is changed 1873 * \param to_folder The destination folder for the operation 1874 */ 1875 void procmsg_msginfo_set_to_folder(MsgInfo *msginfo, FolderItem *to_folder) 1876 { 1877 if(msginfo->to_folder != NULL) { 1878 msginfo->to_folder->op_count--; 1879 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT); 1880 } 1881 msginfo->to_folder = to_folder; 1882 if(to_folder != NULL) { 1883 to_folder->op_count++; 1884 folder_item_update(msginfo->to_folder, F_ITEM_UPDATE_MSGCNT); 1885 } 1886 } 1887 1888 MsgInfo *procmsg_msginfo_new_from_mimeinfo(MsgInfo *src_msginfo, MimeInfo *mimeinfo) 1889 { 1890 MsgInfo *tmp_msginfo = NULL; 1891 MsgFlags flags = {0, 0}; 1892 gchar *tmpfile = get_tmp_file(); 1893 FILE *fp = g_fopen(tmpfile, "wb"); 1894 1895 if (!mimeinfo || mimeinfo->type != MIMETYPE_MESSAGE || 1896 g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) { 1897 g_warning("procmsg_msginfo_new_from_mimeinfo(): unsuitable mimeinfo"); 1898 if (fp) 1899 fclose(fp); 1900 g_free(tmpfile); 1901 return NULL; 1902 } 1903 1904 if (fp && procmime_write_mimeinfo(mimeinfo, fp) >= 0) { 1905 fclose(fp); 1906 fp = NULL; 1907 tmp_msginfo = procheader_parse_file( 1908 tmpfile, flags, 1909 TRUE, FALSE); 1910 } 1911 if (fp) 1912 fclose(fp); 1913 1914 if (tmp_msginfo != NULL) { 1915 if (src_msginfo) 1916 tmp_msginfo->folder = src_msginfo->folder; 1917 tmp_msginfo->plaintext_file = g_strdup(tmpfile); 1918 } else { 1919 g_warning("procmsg_msginfo_new_from_mimeinfo(): can't generate new msginfo"); 1920 } 1921 1922 g_free(tmpfile); 1923 1924 return tmp_msginfo; 1925 } 1926 1927 static void item_has_queued_mails(FolderItem *item, gpointer data) 1928 { 1929 gboolean *result = (gboolean *)data; 1930 if (*result == TRUE) 1931 return; 1932 if (folder_has_parent_of_type(item, F_QUEUE)) { 1933 if (item->total_msgs == 0) 1934 return; 1935 else { 1936 GSList *msglist = folder_item_get_msg_list(item); 1937 GSList *cur; 1938 for (cur = msglist; cur; cur = cur->next) { 1939 MsgInfo *msginfo = (MsgInfo *)cur->data; 1940 if (!MSG_IS_DELETED(msginfo->flags) && 1941 !MSG_IS_LOCKED(msginfo->flags)) { 1942 *result = TRUE; 1943 break; 1944 } 1945 } 1946 procmsg_msg_list_free(msglist); 1947 } 1948 } 1949 } 1950 1951 gboolean procmsg_have_queued_mails_fast (void) 1952 { 1953 gboolean result = FALSE; 1954 folder_func_to_all_folders(item_has_queued_mails, &result); 1955 return result; 1956 } 1957 1958 static void item_has_trashed_mails(FolderItem *item, gpointer data) 1959 { 1960 gboolean *result = (gboolean *)data; 1961 if (*result == TRUE) 1962 return; 1963 if (folder_has_parent_of_type(item, F_TRASH) && item->total_msgs > 0) 1964 *result = TRUE; 1965 } 1966 1967 gboolean procmsg_have_trashed_mails_fast (void) 1968 { 1969 gboolean result = FALSE; 1970 folder_func_to_all_folders(item_has_trashed_mails, &result); 1971 return result; 1972 } 1973