folder.c (111552B)
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 <glib.h> 22 #include <glib/gi18n.h> 23 #include <stdio.h> 24 #include <string.h> 25 #include <sys/types.h> 26 #include <sys/stat.h> 27 #include <unistd.h> 28 #include <stdlib.h> 29 30 #include "alertpanel.h" 31 #include "folder.h" 32 #include "session.h" 33 #include "inc.h" 34 #include "imap.h" 35 #include "mh.h" 36 #include "utils.h" 37 #include "xml.h" 38 #include "codeconv.h" 39 #include "prefs_gtk.h" 40 #include "account.h" 41 #include "procheader.h" 42 #include "hooks.h" 43 #include "log.h" 44 #include "folder_item_prefs.h" 45 #include "remotefolder.h" 46 #include "statusbar.h" 47 #include "gtkutils.h" 48 #include "compose.h" 49 #include "main.h" 50 #include "msgcache.h" 51 #include "prefs_common.h" 52 #include "file-utils.h" 53 54 /* Dependecies to be removed ?! */ 55 #include "prefs_account.h" 56 57 # ifndef S_IROTH 58 # define S_IROTH 0 59 # define S_IWOTH 0 60 # endif 61 62 static GList *folder_list = NULL; 63 static GSList *class_list = NULL; 64 static GSList *folder_unloaded_list = NULL; 65 66 void folder_init (Folder *folder, 67 const gchar *name); 68 69 static gchar *folder_item_get_cache_file (FolderItem *item); 70 static gchar *folder_item_get_mark_file (FolderItem *item); 71 static gchar *folder_item_get_tags_file (FolderItem *item); 72 static GNode *folder_get_xml_node (Folder *folder); 73 static Folder *folder_get_from_xml (GNode *node); 74 static void folder_update_op_count_rec (GNode *node); 75 76 static void folder_item_read_cache (FolderItem *item); 77 gint folder_item_scan_full (FolderItem *item, gboolean filtering); 78 static void folder_item_update_with_msg (FolderItem *item, FolderItemUpdateFlags update_flags, 79 MsgInfo *msg); 80 81 void folder_system_init(void) 82 { 83 folder_register_class(mh_get_class()); 84 folder_register_class(imap_get_class()); 85 } 86 87 static GSList *folder_get_class_list(void) 88 { 89 return class_list; 90 } 91 92 void folder_register_class(FolderClass *klass) 93 { 94 GSList *xmllist, *cur; 95 96 debug_print("registering folder class %s\n", klass->idstr); 97 98 class_list = g_slist_append(class_list, klass); 99 100 xmllist = g_slist_copy(folder_unloaded_list); 101 for (cur = xmllist; cur != NULL; cur = g_slist_next(cur)) { 102 GNode *node = (GNode *) cur->data; 103 XMLNode *xmlnode = (XMLNode *) node->data; 104 GList *cur = xmlnode->tag->attr; 105 106 for (; cur != NULL; cur = g_list_next(cur)) { 107 XMLAttr *attr = (XMLAttr *) cur->data; 108 109 if (!attr || !attr->name || !attr->value) continue; 110 if (!strcmp(attr->name, "type") && !strcmp(attr->value, klass->idstr)) { 111 Folder *folder; 112 113 folder = folder_get_from_xml(node); 114 if (folder) { 115 folder_add(folder); 116 folder_unloaded_list = g_slist_remove(folder_unloaded_list, node); 117 xml_free_tree(node); 118 } 119 cur = NULL; 120 continue; 121 } 122 } 123 } 124 g_slist_free(xmllist); 125 } 126 127 void folder_unregister_class(FolderClass *klass) 128 { 129 GList *folderlist, *cur; 130 131 debug_print("unregistering folder class %s\n", klass->idstr); 132 133 class_list = g_slist_remove(class_list, klass); 134 135 folderlist = g_list_copy(folder_get_list()); 136 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) { 137 Folder *folder = (Folder *) cur->data; 138 139 if (folder->klass == klass) { 140 GNode *xmlnode = folder_get_xml_node(folder); 141 folder_unloaded_list = g_slist_append(folder_unloaded_list, xmlnode); 142 folder_destroy(folder); 143 } 144 } 145 g_list_free(folderlist); 146 147 if (klass->prefs_pages) 148 g_slist_free(klass->prefs_pages); 149 } 150 151 Folder *folder_new(FolderClass *klass, const gchar *name, const gchar *path) 152 { 153 Folder *folder = NULL; 154 FolderItem *item; 155 156 cm_return_val_if_fail(klass != NULL, NULL); 157 158 name = name ? name : path; 159 folder = klass->new_folder(name, path); 160 161 /* Create root folder item */ 162 item = folder_item_new(folder, name, NULL); 163 if (item == NULL) { 164 return NULL; 165 } 166 item->folder = folder; 167 folder->node = item->node; 168 folder->data = NULL; 169 170 return folder; 171 } 172 173 void folder_init(Folder *folder, const gchar *name) 174 { 175 cm_return_if_fail(folder != NULL); 176 177 folder_set_name(folder, name); 178 179 /* Init folder data */ 180 folder->account = NULL; 181 folder->sort = 0; 182 folder->inbox = NULL; 183 folder->outbox = NULL; 184 folder->draft = NULL; 185 folder->queue = NULL; 186 folder->trash = NULL; 187 } 188 189 static void reset_parent_type(FolderItem *item, gpointer data) { 190 item->parent_stype = -1; 191 } 192 193 void folder_item_change_type(FolderItem *item, SpecialFolderItemType newtype) 194 { 195 Folder *folder = NULL; 196 FolderUpdateData hookdata; 197 198 if (item == NULL) 199 return; 200 201 folder = item->folder; 202 /* unset previous root of newtype */ 203 switch(newtype) { 204 case F_INBOX: 205 folder_item_change_type(folder->inbox, F_NORMAL); 206 folder->inbox = item; 207 break; 208 case F_OUTBOX: 209 folder_item_change_type(folder->outbox, F_NORMAL); 210 folder->outbox = item; 211 break; 212 case F_QUEUE: 213 folder_item_change_type(folder->queue, F_NORMAL); 214 folder->queue = item; 215 break; 216 case F_DRAFT: 217 folder_item_change_type(folder->draft, F_NORMAL); 218 folder->draft = item; 219 break; 220 case F_TRASH: 221 folder_item_change_type(folder->trash, F_NORMAL); 222 folder->trash = item; 223 break; 224 case F_NORMAL: 225 default: 226 break; 227 } 228 /* set new type for current folder and sons */ 229 item->stype = newtype; 230 folder_func_to_all_folders(reset_parent_type, NULL); 231 232 hookdata.folder = folder; 233 hookdata.update_flags = FOLDER_TREE_CHANGED; 234 hookdata.item = NULL; 235 hookdata.item2 = NULL; 236 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata); 237 } 238 239 void folder_destroy(Folder *folder) 240 { 241 cm_return_if_fail(folder != NULL); 242 cm_return_if_fail(folder->klass->destroy_folder != NULL); 243 244 folder_remove(folder); 245 246 folder_tree_destroy(folder); 247 248 folder->klass->destroy_folder(folder); 249 250 g_free(folder->name); 251 g_free(folder); 252 } 253 254 void folder_set_xml(Folder *folder, XMLTag *tag) 255 { 256 GList *cur; 257 FolderItem *rootitem = NULL; 258 259 if ((folder->node != NULL) && (folder->node->data != NULL)) 260 rootitem = (FolderItem *) folder->node->data; 261 262 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) { 263 XMLAttr *attr = (XMLAttr *) cur->data; 264 265 if (!attr || !attr->name || !attr->value) continue; 266 if (!strcmp(attr->name, "name")) { 267 g_free(folder->name); 268 folder->name = g_strdup(attr->value); 269 if (rootitem != NULL) { 270 g_free(rootitem->name); 271 rootitem->name = g_strdup(attr->value); 272 } 273 } else if (!strcmp(attr->name, "account_id")) { 274 PrefsAccount *account; 275 276 account = account_find_from_id(atoi(attr->value)); 277 if (!account) 278 g_warning("account_id: %s not found", attr->value); 279 else { 280 folder->account = account; 281 account->folder = folder; 282 } 283 } else if (!strcmp(attr->name, "collapsed")) { 284 if (rootitem != NULL) 285 rootitem->collapsed = *attr->value == '1' ? TRUE : FALSE; 286 } else if (!strcmp(attr->name, "sort")) { 287 folder->sort = atoi(attr->value); 288 } 289 } 290 } 291 292 XMLTag *folder_get_xml(Folder *folder) 293 { 294 XMLTag *tag; 295 296 tag = xml_tag_new("folder"); 297 298 if (folder->name) 299 xml_tag_add_attr(tag, xml_attr_new("name", folder->name)); 300 if (folder->account) 301 xml_tag_add_attr(tag, xml_attr_new_int("account_id", folder->account->account_id)); 302 if (folder->node && folder->node->data) { 303 FolderItem *rootitem = (FolderItem *) folder->node->data; 304 305 xml_tag_add_attr(tag, xml_attr_new("collapsed", rootitem->collapsed ? "1" : "0")); 306 } 307 xml_tag_add_attr(tag, xml_attr_new_int("sort", folder->sort)); 308 309 return tag; 310 } 311 312 FolderItem *folder_item_new(Folder *folder, const gchar *name, const gchar *path) 313 { 314 FolderItem *item = NULL; 315 316 cm_return_val_if_fail(folder != NULL, NULL); 317 318 if (folder->klass->item_new) { 319 item = folder->klass->item_new(folder); 320 } else { 321 item = g_new0(FolderItem, 1); 322 } 323 324 cm_return_val_if_fail(item != NULL, NULL); 325 326 item->stype = F_NORMAL; 327 328 if(!g_utf8_validate(name, -1, NULL)) { 329 item->name = g_malloc(strlen(name)*2+1); 330 conv_localetodisp(item->name, strlen(name)*2+1, name); 331 } else { 332 item->name = g_strdup(name); 333 } 334 335 item->path = g_strdup(path); 336 item->mtime = 0; 337 item->new_msgs = 0; 338 item->unread_msgs = 0; 339 item->unreadmarked_msgs = 0; 340 item->marked_msgs = 0; 341 item->total_msgs = 0; 342 item->replied_msgs = 0; 343 item->forwarded_msgs = 0; 344 item->locked_msgs = 0; 345 item->ignored_msgs = 0; 346 item->watched_msgs = 0; 347 item->order = 0; 348 item->last_num = -1; 349 item->cache = NULL; 350 item->no_sub = FALSE; 351 item->no_select = FALSE; 352 item->collapsed = FALSE; 353 item->thread_collapsed = prefs_common.folder_default_thread_collapsed; 354 item->threaded = prefs_common.folder_default_thread; 355 item->hide_read_threads = prefs_common.folder_default_hide_read_threads; 356 item->hide_read_msgs = prefs_common.folder_default_hide_read_msgs; 357 item->hide_del_msgs = prefs_common.folder_default_hide_del_msgs; 358 item->opened = FALSE; 359 item->node = g_node_new(item); 360 item->folder = NULL; 361 item->account = NULL; 362 item->apply_sub = FALSE; 363 item->mark_queue = NULL; 364 item->data = NULL; 365 item->parent_stype = -1; 366 367 item->sort_key = prefs_common.default_sort_key; 368 item->sort_type = prefs_common.default_sort_type; 369 370 item->prefs = folder_item_prefs_new(); 371 372 return item; 373 } 374 375 void folder_item_append(FolderItem *parent, FolderItem *item) 376 { 377 cm_return_if_fail(parent != NULL); 378 cm_return_if_fail(parent->folder != NULL); 379 cm_return_if_fail(parent->node != NULL); 380 cm_return_if_fail(item != NULL); 381 382 item->folder = parent->folder; 383 g_node_append(parent->node, item->node); 384 } 385 386 void folder_item_remove(FolderItem *item) 387 { 388 GNode *node, *start_node; 389 FolderUpdateData hookdata; 390 gchar *tags_file = NULL; 391 392 cm_return_if_fail(item != NULL); 393 cm_return_if_fail(item->folder != NULL); 394 cm_return_if_fail(item->folder->node != NULL); 395 396 start_node = item->node; 397 398 node = item->folder->node; 399 400 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item); 401 node = node->children; 402 403 /* remove my children */ 404 while (node != NULL) { 405 if (node && node->data) { 406 FolderItem *sub_item = (FolderItem*) node->data; 407 node = node->next; 408 folder_item_remove(sub_item); 409 } 410 } 411 412 /* remove myself */ 413 if (item->cache != NULL) { 414 msgcache_destroy(item->cache); 415 item->cache = NULL; 416 } 417 418 tags_file = folder_item_get_tags_file(item); 419 if (tags_file) { 420 gchar *tags_dir; 421 422 unlink(tags_file); 423 424 tags_dir = g_path_get_dirname(tags_file); 425 if (tags_dir) 426 rmdir(tags_dir); 427 g_free(tags_dir); 428 } 429 g_free(tags_file); 430 431 hookdata.folder = item->folder; 432 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_REMOVE_FOLDERITEM; 433 hookdata.item = item; 434 hookdata.item2 = NULL; 435 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata); 436 437 node = start_node; 438 439 if (item->folder->node == node) 440 item->folder->node = NULL; 441 442 folder_item_destroy(item); 443 444 g_node_destroy(node); 445 } 446 447 void folder_item_remove_children(FolderItem *item) 448 { 449 GNode *node, *next; 450 451 cm_return_if_fail(item != NULL); 452 cm_return_if_fail(item->folder != NULL); 453 cm_return_if_fail(item->node != NULL); 454 455 node = item->node->children; 456 while (node != NULL) { 457 next = node->next; 458 folder_item_remove(FOLDER_ITEM(node->data)); 459 node = next; 460 } 461 } 462 463 void folder_item_destroy(FolderItem *item) 464 { 465 Folder *folder; 466 467 cm_return_if_fail(item != NULL); 468 469 folder = item->folder; 470 if (folder) { 471 if (folder->inbox == item) 472 folder->inbox = NULL; 473 else if (folder->outbox == item) 474 folder->outbox = NULL; 475 else if (folder->draft == item) 476 folder->draft = NULL; 477 else if (folder->queue == item) 478 folder->queue = NULL; 479 else if (folder->trash == item) 480 folder->trash = NULL; 481 } 482 483 if (item->cache) 484 folder_item_free_cache(item, TRUE); 485 if (item->prefs) 486 folder_item_prefs_free(item->prefs); 487 g_free(item->name); 488 g_free(item->path); 489 490 if (item->folder != NULL) { 491 if(item->folder->klass->item_destroy) { 492 item->folder->klass->item_destroy(item->folder, item); 493 } else { 494 g_free(item); 495 } 496 } 497 } 498 499 FolderItem *folder_item_parent(FolderItem *item) 500 { 501 cm_return_val_if_fail(item != NULL, NULL); 502 cm_return_val_if_fail(item->node != NULL, NULL); 503 504 if (item->node->parent == NULL) 505 return NULL; 506 return (FolderItem *) item->node->parent->data; 507 } 508 509 void folder_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag) 510 { 511 GList *cur; 512 513 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) { 514 XMLAttr *attr = (XMLAttr *) cur->data; 515 516 if (!attr || !attr->name || !attr->value) continue; 517 if (!strcmp(attr->name, "type")) { 518 if (!g_ascii_strcasecmp(attr->value, "normal")) 519 item->stype = F_NORMAL; 520 else if (!g_ascii_strcasecmp(attr->value, "inbox")) 521 item->stype = F_INBOX; 522 else if (!g_ascii_strcasecmp(attr->value, "outbox")) 523 item->stype = F_OUTBOX; 524 else if (!g_ascii_strcasecmp(attr->value, "draft")) 525 item->stype = F_DRAFT; 526 else if (!g_ascii_strcasecmp(attr->value, "queue")) 527 item->stype = F_QUEUE; 528 else if (!g_ascii_strcasecmp(attr->value, "trash")) 529 item->stype = F_TRASH; 530 } else if (!strcmp(attr->name, "name")) { 531 g_free(item->name); 532 item->name = g_strdup(attr->value); 533 } else if (!strcmp(attr->name, "path")) { 534 g_free(item->path); 535 item->path = g_strdup(attr->value); 536 } else if (!strcmp(attr->name, "mtime")) 537 item->mtime = strtoul(attr->value, NULL, 10); 538 else if (!strcmp(attr->name, "new")) 539 item->new_msgs = atoi(attr->value); 540 else if (!strcmp(attr->name, "unread")) 541 item->unread_msgs = atoi(attr->value); 542 else if (!strcmp(attr->name, "unreadmarked")) 543 item->unreadmarked_msgs = atoi(attr->value); 544 else if (!strcmp(attr->name, "marked")) 545 item->marked_msgs = atoi(attr->value); 546 else if (!strcmp(attr->name, "replied")) 547 item->replied_msgs = atoi(attr->value); 548 else if (!strcmp(attr->name, "forwarded")) 549 item->forwarded_msgs = atoi(attr->value); 550 else if (!strcmp(attr->name, "locked")) 551 item->locked_msgs = atoi(attr->value); 552 else if (!strcmp(attr->name, "ignored")) 553 item->ignored_msgs = atoi(attr->value); 554 else if (!strcmp(attr->name, "watched")) 555 item->watched_msgs = atoi(attr->value); 556 else if (!strcmp(attr->name, "order")) 557 item->order = atoi(attr->value); 558 else if (!strcmp(attr->name, "total")) 559 item->total_msgs = atoi(attr->value); 560 else if (!strcmp(attr->name, "no_sub")) 561 item->no_sub = *attr->value == '1' ? TRUE : FALSE; 562 else if (!strcmp(attr->name, "no_select")) 563 item->no_select = *attr->value == '1' ? TRUE : FALSE; 564 else if (!strcmp(attr->name, "collapsed")) 565 item->collapsed = *attr->value == '1' ? TRUE : FALSE; 566 else if (!strcmp(attr->name, "thread_collapsed")) 567 item->thread_collapsed = *attr->value == '1' ? TRUE : FALSE; 568 else if (!strcmp(attr->name, "threaded")) 569 item->threaded = *attr->value == '1' ? TRUE : FALSE; 570 else if (!strcmp(attr->name, "hidereadmsgs")) 571 item->hide_read_msgs = *attr->value == '1' ? TRUE : FALSE; 572 else if (!strcmp(attr->name, "hidedelmsgs")) 573 item->hide_del_msgs = *attr->value == '1' ? TRUE : FALSE; 574 else if (!strcmp(attr->name, "hidereadthreads")) 575 item->hide_read_threads = *attr->value == '1' ? TRUE : FALSE; 576 else if (!strcmp(attr->name, "sort_key")) { 577 if (!strcmp(attr->value, "none")) 578 item->sort_key = SORT_BY_NONE; 579 else if (!strcmp(attr->value, "number")) 580 item->sort_key = SORT_BY_NUMBER; 581 else if (!strcmp(attr->value, "size")) 582 item->sort_key = SORT_BY_SIZE; 583 else if (!strcmp(attr->value, "date")) 584 item->sort_key = SORT_BY_DATE; 585 else if (!strcmp(attr->value, "from")) 586 item->sort_key = SORT_BY_FROM; 587 else if (!strcmp(attr->value, "subject")) 588 item->sort_key = SORT_BY_SUBJECT; 589 else if (!strcmp(attr->value, "score")) 590 item->sort_key = SORT_BY_SCORE; 591 else if (!strcmp(attr->value, "label")) 592 item->sort_key = SORT_BY_LABEL; 593 else if (!strcmp(attr->value, "mark")) 594 item->sort_key = SORT_BY_MARK; 595 else if (!strcmp(attr->value, "unread")) 596 item->sort_key = SORT_BY_STATUS; 597 else if (!strcmp(attr->value, "mime")) 598 item->sort_key = SORT_BY_MIME; 599 else if (!strcmp(attr->value, "to")) 600 item->sort_key = SORT_BY_TO; 601 else if (!strcmp(attr->value, "locked")) 602 item->sort_key = SORT_BY_LOCKED; 603 else if (!strcmp(attr->value, "tags")) 604 item->sort_key = SORT_BY_TAGS; 605 else if (!strcmp(attr->value, "thread_date")) 606 item->sort_key = SORT_BY_THREAD_DATE; 607 } else if (!strcmp(attr->name, "sort_type")) { 608 if (!strcmp(attr->value, "ascending")) 609 item->sort_type = SORT_ASCENDING; 610 else 611 item->sort_type = SORT_DESCENDING; 612 } else if (!strcmp(attr->name, "account_id")) { 613 PrefsAccount *account; 614 615 account = account_find_from_id(atoi(attr->value)); 616 if (!account) 617 g_warning("account_id: %s not found", attr->value); 618 else 619 item->account = account; 620 } else if (!strcmp(attr->name, "apply_sub")) { 621 item->apply_sub = *attr->value == '1' ? TRUE : FALSE; 622 } else if (!strcmp(attr->name, "last_seen")) { 623 item->last_seen = atoi(attr->value); 624 } 625 } 626 /* options without meaning in drafts */ 627 if (item->stype == F_DRAFT) 628 item->hide_read_msgs = 629 item->hide_del_msgs = 630 item->hide_read_threads = FALSE; 631 } 632 633 XMLTag *folder_item_get_xml(Folder *folder, FolderItem *item) 634 { 635 static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox", 636 "draft", "queue", "trash"}; 637 static gchar *sort_key_str[] = {"none", "number", "size", "date", 638 "from", "subject", "score", "label", 639 "mark", "unread", "mime", "to", 640 "locked", "tags", "thread_date" }; 641 XMLTag *tag; 642 gchar *value; 643 644 tag = xml_tag_new("folderitem"); 645 646 xml_tag_add_attr(tag, xml_attr_new("type", folder_item_stype_str[item->stype])); 647 if (item->name) 648 xml_tag_add_attr(tag, xml_attr_new("name", item->name)); 649 if (item->path) 650 xml_tag_add_attr(tag, xml_attr_new("path", item->path)); 651 if (item->no_sub) 652 xml_tag_add_attr(tag, xml_attr_new("no_sub", "1")); 653 if (item->no_select) 654 xml_tag_add_attr(tag, xml_attr_new("no_select", "1")); 655 xml_tag_add_attr(tag, xml_attr_new("collapsed", item->collapsed && item->node->children ? "1" : "0")); 656 xml_tag_add_attr(tag, xml_attr_new("thread_collapsed", item->thread_collapsed ? "1" : "0")); 657 xml_tag_add_attr(tag, xml_attr_new("threaded", item->threaded ? "1" : "0")); 658 xml_tag_add_attr(tag, xml_attr_new("hidereadmsgs", item->hide_read_msgs ? "1" : "0")); 659 xml_tag_add_attr(tag, xml_attr_new("hidedelmsgs", item->hide_del_msgs ? "1" : "0")); 660 xml_tag_add_attr(tag, xml_attr_new("hidereadthreads", item->hide_read_threads ? "1" : "0")); 661 662 if (item->sort_key != SORT_BY_NONE) { 663 xml_tag_add_attr(tag, xml_attr_new("sort_key", sort_key_str[item->sort_key])); 664 xml_tag_add_attr(tag, xml_attr_new("sort_type", item->sort_type == SORT_ASCENDING ? "ascending" : "descending")); 665 } 666 667 value = g_strdup_printf("%ld", (unsigned long int) item->mtime); 668 xml_tag_add_attr(tag, xml_attr_new("mtime", value)); 669 g_free(value); 670 xml_tag_add_attr(tag, xml_attr_new_int("new", item->new_msgs)); 671 xml_tag_add_attr(tag, xml_attr_new_int("unread", item->unread_msgs)); 672 xml_tag_add_attr(tag, xml_attr_new_int("unreadmarked", item->unreadmarked_msgs)); 673 xml_tag_add_attr(tag, xml_attr_new_int("marked", item->marked_msgs)); 674 xml_tag_add_attr(tag, xml_attr_new_int("total", item->total_msgs)); 675 xml_tag_add_attr(tag, xml_attr_new_int("replied", item->replied_msgs)); 676 xml_tag_add_attr(tag, xml_attr_new_int("forwarded", item->forwarded_msgs)); 677 xml_tag_add_attr(tag, xml_attr_new_int("locked", item->locked_msgs)); 678 xml_tag_add_attr(tag, xml_attr_new_int("ignore", item->ignored_msgs)); 679 xml_tag_add_attr(tag, xml_attr_new_int("watched", item->watched_msgs)); 680 xml_tag_add_attr(tag, xml_attr_new_int("order", item->order)); 681 682 if (item->account) 683 xml_tag_add_attr(tag, xml_attr_new_int("account_id", item->account->account_id)); 684 if (item->apply_sub) 685 xml_tag_add_attr(tag, xml_attr_new("apply_sub", "1")); 686 687 xml_tag_add_attr(tag, xml_attr_new_int("last_seen", item->last_seen)); 688 689 return tag; 690 } 691 692 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data) 693 { 694 cm_return_if_fail(folder != NULL); 695 696 folder->ui_func = func; 697 folder->ui_func_data = data; 698 } 699 700 void folder_set_name(Folder *folder, const gchar *name) 701 { 702 cm_return_if_fail(folder != NULL); 703 704 g_free(folder->name); 705 folder->name = name ? g_strdup(name) : NULL; 706 if (folder->node && folder->node->data) { 707 FolderItem *item = (FolderItem *)folder->node->data; 708 709 g_free(item->name); 710 item->name = name ? g_strdup(name) : NULL; 711 } 712 } 713 714 void folder_set_sort(Folder *folder, guint sort) 715 { 716 cm_return_if_fail(folder != NULL); 717 718 if (folder->sort != sort) { 719 folder_remove(folder); 720 folder->sort = sort; 721 folder_add(folder); 722 } 723 } 724 725 static gboolean folder_tree_destroy_func(GNode *node, gpointer data) { 726 FolderItem *item = (FolderItem *) node->data; 727 728 folder_item_destroy(item); 729 return FALSE; 730 } 731 732 void folder_tree_destroy(Folder *folder) 733 { 734 GNode *node; 735 736 cm_return_if_fail(folder != NULL); 737 738 node = folder->node; 739 if (node != NULL) { 740 g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1, 741 folder_tree_destroy_func, NULL); 742 g_node_destroy(node); 743 folder->node = NULL; 744 } 745 } 746 747 void folder_add(Folder *folder) 748 { 749 Folder *cur_folder; 750 GList *cur; 751 gint i; 752 FolderUpdateData hookdata; 753 754 cm_return_if_fail(folder != NULL); 755 756 if ((FOLDER_TYPE(folder) == F_IMAP || 757 FOLDER_TYPE(folder) == F_NEWS) && 758 folder->account == NULL) { 759 return; 760 } 761 762 for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) { 763 cur_folder = FOLDER(cur->data); 764 if (cur_folder->sort < folder->sort) 765 break; 766 } 767 768 folder_list = g_list_insert(folder_list, folder, i); 769 770 hookdata.folder = folder; 771 hookdata.update_flags = FOLDER_ADD_FOLDER; 772 hookdata.item = NULL; 773 hookdata.item2 = NULL; 774 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata); 775 } 776 777 void folder_remove(Folder *folder) 778 { 779 FolderUpdateData hookdata; 780 781 cm_return_if_fail(folder != NULL); 782 783 folder_list = g_list_remove(folder_list, folder); 784 785 hookdata.folder = folder; 786 hookdata.update_flags = FOLDER_REMOVE_FOLDER; 787 hookdata.item = NULL; 788 hookdata.item2 = NULL; 789 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata); 790 } 791 792 GList *folder_get_list(void) 793 { 794 return folder_list; 795 } 796 797 gint folder_read_list(void) 798 { 799 GNode *node, *cur; 800 XMLNode *xmlnode; 801 gchar *path; 802 GList *list; 803 gint config_version = -1; 804 805 path = folder_get_list_path(); 806 node = xml_parse_file(path); 807 if (!node) return -1; 808 809 xmlnode = node->data; 810 if (g_strcmp0(xmlnode->tag->tag, "folderlist") != 0) { 811 g_warning("wrong folder list"); 812 xml_free_tree(node); 813 return -1; 814 } 815 816 cur = node->children; 817 while (cur != NULL) { 818 Folder *folder; 819 820 folder = folder_get_from_xml(cur); 821 if (folder != NULL) 822 folder_add(folder); 823 else 824 folder_unloaded_list = g_slist_append(folder_unloaded_list, 825 (gpointer) xml_copy_tree(cur)); 826 cur = cur->next; 827 } 828 829 for (list = xmlnode->tag->attr; list != NULL; list = list->next) { 830 XMLAttr *attr = list->data; 831 832 if (!attr || !attr->name || !attr->value) continue; 833 if (!strcmp(attr->name, "config_version")) { 834 config_version = atoi(attr->value); 835 debug_print("Found folderlist config_version %d\n", config_version); 836 } 837 } 838 839 xml_free_tree(node); 840 841 if (folder_list || folder_unloaded_list) 842 return 0; 843 return -1; 844 } 845 846 void folder_write_list(void) 847 { 848 GList *list; 849 GSList *slist; 850 Folder *folder; 851 gchar *path; 852 PrefFile *pfile; 853 GNode *rootnode; 854 XMLNode *xmlnode; 855 XMLTag *tag; 856 857 path = folder_get_list_path(); 858 if ((pfile = prefs_write_open(path)) == NULL) return; 859 860 if (xml_file_put_xml_decl(pfile->fp) < 0) { 861 prefs_file_close_revert(pfile); 862 g_warning("failed to start write folder list"); 863 return; 864 } 865 tag = xml_tag_new("folderlist"); 866 xml_tag_add_attr(tag, xml_attr_new_int("config_version", 867 CLAWS_CONFIG_VERSION)); 868 869 xmlnode = xml_node_new(tag, NULL); 870 871 rootnode = g_node_new(xmlnode); 872 873 for (list = folder_list; list != NULL; list = list->next) { 874 GNode *node; 875 876 folder = list->data; 877 node = folder_get_xml_node(folder); 878 if (node != NULL) 879 g_node_append(rootnode, node); 880 } 881 882 for (slist = folder_unloaded_list; slist != NULL; slist = g_slist_next(slist)) { 883 GNode *node = (GNode *) slist->data; 884 885 g_node_append(rootnode, (gpointer) xml_copy_tree(node)); 886 } 887 888 if (xml_write_tree(rootnode, pfile->fp) < 0) { 889 prefs_file_close_revert(pfile); 890 g_warning("failed to write folder list"); 891 } else if (prefs_file_close(pfile) < 0) { 892 g_warning("failed to write folder list"); 893 } 894 xml_free_tree(rootnode); 895 } 896 897 void folder_scan_tree(Folder *folder, gboolean rebuild) 898 { 899 FolderUpdateData hookdata; 900 Folder *old_folder = folder; 901 902 if (!folder->klass->scan_tree) 903 return; 904 905 if (rebuild) 906 folder_remove(folder); 907 908 if (folder->klass->scan_tree(folder) < 0) { 909 if (rebuild) 910 folder_add(old_folder); 911 return; 912 } else if (rebuild) 913 folder_add(folder); 914 915 hookdata.folder = folder; 916 hookdata.update_flags = FOLDER_TREE_CHANGED; 917 hookdata.item = NULL; 918 hookdata.item2 = NULL; 919 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata); 920 921 folder_write_list(); 922 } 923 924 FolderItem *folder_create_folder(FolderItem *parent, const gchar *name) 925 { 926 FolderItem *new_item; 927 928 cm_return_val_if_fail(parent != NULL, NULL); 929 930 new_item = parent->folder->klass->create_folder(parent->folder, parent, name); 931 if (new_item) { 932 FolderUpdateData hookdata; 933 934 new_item->cache = msgcache_new(); 935 new_item->cache_dirty = TRUE; 936 new_item->mark_dirty = TRUE; 937 new_item->tags_dirty = TRUE; 938 939 hookdata.folder = new_item->folder; 940 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDERITEM; 941 hookdata.item = new_item; 942 hookdata.item2 = NULL; 943 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata); 944 } 945 946 return new_item; 947 } 948 949 gint folder_item_rename(FolderItem *item, gchar *newname) 950 { 951 gint retval; 952 953 cm_return_val_if_fail(item != NULL, -1); 954 cm_return_val_if_fail(newname != NULL, -1); 955 956 retval = item->folder->klass->rename_folder(item->folder, item, newname); 957 958 if (retval >= 0) { 959 FolderItemUpdateData hookdata; 960 FolderUpdateData hookdata2; 961 962 hookdata.item = item; 963 hookdata.update_flags = F_ITEM_UPDATE_NAME; 964 hookdata.msg = NULL; 965 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata); 966 967 hookdata2.folder = item->folder; 968 hookdata2.item = item; 969 hookdata2.item2 = NULL; 970 hookdata2.update_flags = FOLDER_RENAME_FOLDERITEM; 971 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata2); 972 } 973 974 return retval; 975 } 976 977 struct TotalMsgCount 978 { 979 guint new_msgs; 980 guint unread_msgs; 981 guint unreadmarked_msgs; 982 guint marked_msgs; 983 guint total_msgs; 984 guint replied_msgs; 985 guint forwarded_msgs; 986 guint locked_msgs; 987 guint ignored_msgs; 988 guint watched_msgs; 989 }; 990 991 struct FuncToAllFoldersData 992 { 993 FolderItemFunc function; 994 gpointer data; 995 }; 996 997 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data) 998 { 999 FolderItem *item; 1000 struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data; 1001 1002 cm_return_val_if_fail(node->data != NULL, FALSE); 1003 1004 item = FOLDER_ITEM(node->data); 1005 cm_return_val_if_fail(item != NULL, FALSE); 1006 1007 function_data->function(item, function_data->data); 1008 1009 return FALSE; 1010 } 1011 1012 void folder_func_to_all_folders(FolderItemFunc function, gpointer data) 1013 { 1014 GList *list; 1015 Folder *folder; 1016 struct FuncToAllFoldersData function_data; 1017 1018 function_data.function = function; 1019 function_data.data = data; 1020 1021 for (list = folder_list; list != NULL; list = list->next) { 1022 folder = FOLDER(list->data); 1023 if (folder->node) 1024 g_node_traverse(folder->node, G_PRE_ORDER, 1025 G_TRAVERSE_ALL, -1, 1026 folder_func_to_all_folders_func, 1027 &function_data); 1028 } 1029 } 1030 1031 static void folder_count_total_msgs_func(FolderItem *item, gpointer data) 1032 { 1033 struct TotalMsgCount *count = (struct TotalMsgCount *)data; 1034 1035 count->new_msgs += item->new_msgs; 1036 count->unread_msgs += item->unread_msgs; 1037 count->unreadmarked_msgs += item->unreadmarked_msgs; 1038 count->marked_msgs += item->marked_msgs; 1039 count->total_msgs += item->total_msgs; 1040 count->replied_msgs += item->replied_msgs; 1041 count->forwarded_msgs += item->forwarded_msgs; 1042 count->locked_msgs += item->locked_msgs; 1043 count->ignored_msgs += item->ignored_msgs; 1044 count->watched_msgs += item->watched_msgs; 1045 } 1046 1047 struct TotalMsgStatus 1048 { 1049 guint new; 1050 guint unread; 1051 guint total; 1052 GString *str; 1053 }; 1054 1055 static gboolean folder_get_status_full_all_func(GNode *node, gpointer data) 1056 { 1057 FolderItem *item; 1058 struct TotalMsgStatus *status = (struct TotalMsgStatus *)data; 1059 gchar *id; 1060 1061 cm_return_val_if_fail(node->data != NULL, FALSE); 1062 1063 item = FOLDER_ITEM(node->data); 1064 1065 if (!item->path) return FALSE; 1066 1067 status->new += item->new_msgs; 1068 status->unread += item->unread_msgs; 1069 status->total += item->total_msgs; 1070 1071 if (status->str) { 1072 id = folder_item_get_identifier(item); 1073 g_string_append_printf(status->str, "%5d %5d %5d %s\n", 1074 item->new_msgs, item->unread_msgs, 1075 item->total_msgs, id); 1076 g_free(id); 1077 } 1078 1079 return FALSE; 1080 } 1081 1082 static void folder_get_status_full_all(GString *str, guint *new, guint *unread, 1083 guint *total) 1084 { 1085 GList *list; 1086 Folder *folder; 1087 struct TotalMsgStatus status; 1088 1089 status.new = status.unread = status.total = 0; 1090 status.str = str; 1091 1092 debug_print("Counting total number of messages...\n"); 1093 1094 for (list = folder_list; list != NULL; list = list->next) { 1095 folder = FOLDER(list->data); 1096 if (folder->node) 1097 g_node_traverse(folder->node, G_PRE_ORDER, 1098 G_TRAVERSE_ALL, -1, 1099 folder_get_status_full_all_func, 1100 &status); 1101 } 1102 1103 *new = status.new; 1104 *unread = status.unread; 1105 *total = status.total; 1106 } 1107 1108 gchar *folder_get_status(GPtrArray *folders, gboolean full) 1109 { 1110 guint new, unread, total; 1111 GString *str; 1112 gint i; 1113 1114 new = unread = total = 0; 1115 1116 str = g_string_new(NULL); 1117 1118 if (folders) { 1119 for (i = 0; i < folders->len; i++) { 1120 FolderItem *item; 1121 1122 item = g_ptr_array_index(folders, i); 1123 new += item->new_msgs; 1124 unread += item->unread_msgs; 1125 total += item->total_msgs; 1126 1127 if (full) { 1128 gchar *id; 1129 1130 id = folder_item_get_identifier(item); 1131 g_string_append_printf(str, "%5d %5d %5d %s\n", 1132 item->new_msgs, item->unread_msgs, 1133 item->total_msgs, id); 1134 g_free(id); 1135 } 1136 } 1137 } else { 1138 folder_get_status_full_all(full ? str : NULL, 1139 &new, &unread, &total); 1140 } 1141 1142 if (full) 1143 g_string_append_printf(str, "%5d %5d %5d\n", new, unread, total); 1144 else 1145 g_string_append_printf(str, "%d %d %d\n", new, unread, total); 1146 1147 return g_string_free(str, FALSE); 1148 } 1149 1150 void folder_count_total_msgs(guint *new_msgs, guint *unread_msgs, 1151 guint *unreadmarked_msgs, guint *marked_msgs, 1152 guint *total_msgs, guint *replied_msgs, 1153 guint *forwarded_msgs, guint *locked_msgs, 1154 guint *ignored_msgs, guint *watched_msgs) 1155 { 1156 struct TotalMsgCount count; 1157 1158 count.new_msgs = count.unread_msgs = count.unreadmarked_msgs = 0; 1159 count.total_msgs = count.replied_msgs = count.forwarded_msgs = 0; 1160 count.locked_msgs = count.ignored_msgs = count.watched_msgs = 0; 1161 count.marked_msgs = 0; 1162 1163 debug_print("Counting total number of messages...\n"); 1164 1165 folder_func_to_all_folders(folder_count_total_msgs_func, &count); 1166 1167 *new_msgs = count.new_msgs; 1168 *unread_msgs = count.unread_msgs; 1169 *unreadmarked_msgs = count.unreadmarked_msgs; 1170 *marked_msgs = count.marked_msgs; 1171 *total_msgs = count.total_msgs; 1172 *replied_msgs = count.replied_msgs; 1173 *forwarded_msgs = count.forwarded_msgs; 1174 *locked_msgs = count.locked_msgs; 1175 *ignored_msgs = count.ignored_msgs; 1176 *watched_msgs = count.watched_msgs; 1177 } 1178 1179 Folder *folder_find_from_path(const gchar *path) 1180 { 1181 GList *list; 1182 Folder *folder; 1183 1184 for (list = folder_list; list != NULL; list = list->next) { 1185 folder = list->data; 1186 if ((FOLDER_TYPE(folder) == F_MH || 1187 FOLDER_TYPE(folder) == F_MBOX) && 1188 !path_cmp(LOCAL_FOLDER(folder)->rootpath, path)) 1189 return folder; 1190 } 1191 1192 return NULL; 1193 } 1194 1195 Folder *folder_find_from_name(const gchar *name, FolderClass *klass) 1196 { 1197 GList *list; 1198 Folder *folder; 1199 1200 for (list = folder_list; list != NULL; list = list->next) { 1201 folder = list->data; 1202 if (folder->klass == klass && 1203 g_strcmp0(name, folder->name) == 0) 1204 return folder; 1205 } 1206 1207 return NULL; 1208 } 1209 1210 static gboolean folder_item_find_func(GNode *node, gpointer data) 1211 { 1212 FolderItem *item = node->data; 1213 gpointer *d = data; 1214 const gchar *path = d[0]; 1215 1216 if (path_cmp(path, item->path) != 0) 1217 return FALSE; 1218 1219 d[1] = item; 1220 1221 return TRUE; 1222 } 1223 1224 FolderItem *folder_find_item_from_path(const gchar *path) 1225 { 1226 Folder *folder; 1227 gpointer d[2]; 1228 GList *list = folder_get_list(); 1229 1230 folder = list ? list->data:NULL; 1231 1232 cm_return_val_if_fail(folder != NULL, NULL); 1233 1234 d[0] = (gpointer)path; 1235 d[1] = NULL; 1236 while (d[1] == NULL && list) { 1237 folder = FOLDER(list->data); 1238 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, 1239 folder_item_find_func, d); 1240 list = list->next; 1241 } 1242 return d[1]; 1243 } 1244 1245 static gboolean folder_item_find_func_real_path(GNode *node, gpointer data) 1246 { 1247 FolderItem *item = node->data; 1248 gpointer *d = data; 1249 const gchar *path = d[0]; 1250 gchar *tmp = folder_item_get_path(item); 1251 if (path_cmp(path, tmp) != 0) { 1252 g_free(tmp); 1253 return FALSE; 1254 } 1255 g_free(tmp); 1256 d[1] = item; 1257 1258 return TRUE; 1259 } 1260 1261 FolderItem *folder_find_item_from_real_path(const gchar *path) 1262 { 1263 Folder *folder; 1264 gpointer d[2]; 1265 GList *list = folder_get_list(); 1266 1267 folder = list ? list->data:NULL; 1268 1269 cm_return_val_if_fail(folder != NULL, NULL); 1270 1271 d[0] = (gpointer)path; 1272 d[1] = NULL; 1273 while (d[1] == NULL && list) { 1274 folder = FOLDER(list->data); 1275 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, 1276 folder_item_find_func_real_path, d); 1277 list = list->next; 1278 } 1279 return d[1]; 1280 } 1281 1282 FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name) 1283 { 1284 GNode *node; 1285 FolderItem *child; 1286 1287 for (node = item->node->children; node != NULL; node = node->next) { 1288 child = FOLDER_ITEM(node->data); 1289 if (g_strcmp0(child->name, name) == 0) { 1290 return child; 1291 } 1292 } 1293 1294 return NULL; 1295 } 1296 1297 FolderClass *folder_get_class_from_string(const gchar *str) 1298 { 1299 GSList *classlist; 1300 1301 classlist = folder_get_class_list(); 1302 for (; classlist != NULL; classlist = g_slist_next(classlist)) { 1303 FolderClass *class = (FolderClass *) classlist->data; 1304 if (g_ascii_strcasecmp(class->idstr, str) == 0) 1305 return class; 1306 } 1307 1308 return NULL; 1309 } 1310 1311 gchar *folder_get_identifier(Folder *folder) 1312 { 1313 gchar *type_str; 1314 1315 cm_return_val_if_fail(folder != NULL, NULL); 1316 1317 type_str = folder->klass->idstr; 1318 return g_strconcat("#", type_str, "/", folder->name, NULL); 1319 } 1320 1321 gchar *folder_item_get_identifier(FolderItem *item) 1322 { 1323 gchar *id = NULL; 1324 gchar *folder_id = NULL; 1325 1326 cm_return_val_if_fail(item != NULL, NULL); 1327 1328 if (item->path == NULL) 1329 return NULL; 1330 1331 folder_id = folder_get_identifier(item->folder); 1332 id = g_strconcat(folder_id, "/", item->path, NULL); 1333 g_free(folder_id); 1334 1335 return id; 1336 } 1337 1338 Folder *folder_find_from_identifier(const gchar *identifier) 1339 { 1340 gchar *p; 1341 gchar *name; 1342 FolderClass *class; 1343 1344 cm_return_val_if_fail(identifier != NULL, NULL); 1345 1346 if (*identifier != '#') 1347 return NULL; 1348 1349 char str[BUFSIZ]; 1350 strlcpy(str, identifier, sizeof(str)); 1351 1352 p = strchr(str, '/'); 1353 if (!p) 1354 return NULL; 1355 *p = '\0'; 1356 p++; 1357 class = folder_get_class_from_string(&str[1]); 1358 if (class == NULL) 1359 return NULL; 1360 1361 name = p; 1362 p = strchr(p, '/'); 1363 if (p) 1364 return NULL; 1365 1366 return folder_find_from_name(name, class); 1367 } 1368 1369 FolderItem *folder_find_item_from_identifier(const gchar *identifier) 1370 { 1371 Folder *folder; 1372 gpointer d[2]; 1373 gchar *str; 1374 gchar *p; 1375 gchar *name; 1376 gchar *path; 1377 FolderClass *class; 1378 1379 cm_return_val_if_fail(identifier != NULL, NULL); 1380 1381 if (*identifier != '#') 1382 return folder_find_item_from_path(identifier); 1383 1384 Xstrdup_a(str, identifier, return NULL); 1385 1386 p = strchr(str, '/'); 1387 if (!p) 1388 return folder_find_item_from_path(identifier); 1389 *p = '\0'; 1390 p++; 1391 class = folder_get_class_from_string(&str[1]); 1392 if (class == NULL) 1393 return folder_find_item_from_path(identifier); 1394 1395 name = p; 1396 p = strchr(p, '/'); 1397 if (!p) 1398 return folder_find_item_from_path(identifier); 1399 *p = '\0'; 1400 p++; 1401 1402 folder = folder_find_from_name(name, class); 1403 if (!folder) 1404 return folder_find_item_from_path(identifier); 1405 1406 path = p; 1407 1408 d[0] = (gpointer)path; 1409 d[1] = NULL; 1410 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, 1411 folder_item_find_func, d); 1412 return d[1]; 1413 } 1414 1415 /** Returns the FolderItem from a given identifier 1416 * 1417 * The FolderItem is created if it doesn't already exist. 1418 * If creation failed, the function returns NULL. 1419 * 1420 * Identifiers are of the form #type/Mailbox/FolderA/FolderB/FolderC 1421 */ 1422 FolderItem *folder_get_item_from_identifier(const gchar *identifier) 1423 { 1424 FolderItem *item, *last_parent; 1425 Folder *folder; 1426 gchar *p1, *p2, *str; 1427 size_t len; 1428 FolderClass *class; 1429 gboolean created_something = FALSE; 1430 1431 item = folder_find_item_from_identifier(identifier); 1432 if(item) 1433 return item; 1434 1435 /* trivial sanity check: need at least # and two slashes */ 1436 len = strlen(identifier); 1437 if(len < 3) 1438 return NULL; 1439 1440 /* make sure identifier ends with a slash */ 1441 if(identifier[len-1] == G_DIR_SEPARATOR) { 1442 Xstrdup_a(str, identifier, return NULL); 1443 } 1444 else { 1445 Xstrndup_a(str, identifier, len+1, return NULL); 1446 str[len] = G_DIR_SEPARATOR; 1447 } 1448 1449 /* find folder class */ 1450 p1 = strchr(str, G_DIR_SEPARATOR); 1451 if(!p1) 1452 return NULL; 1453 *p1 = '\0'; 1454 class = folder_get_class_from_string(&str[1]); 1455 if(!class) 1456 return NULL; 1457 *p1 = G_DIR_SEPARATOR; 1458 ++p1; 1459 1460 /* find folder from class and name */ 1461 p2 = strchr(p1, G_DIR_SEPARATOR); 1462 if(!p2) 1463 return NULL; 1464 *p2 = '\0'; 1465 folder = folder_find_from_name(p1, class); 1466 if(!folder) 1467 return NULL; 1468 *p2 = G_DIR_SEPARATOR; 1469 ++p2; 1470 p1 = p2; 1471 1472 /* Now, move forward and make sure all sections in the path exist */ 1473 last_parent = folder->node->data; 1474 while((p1 = strchr(p1, G_DIR_SEPARATOR)) != NULL) { 1475 *p1 = '\0'; 1476 item = folder_find_item_from_identifier(str); 1477 if(!item) { 1478 item = folder_create_folder(last_parent, p2); 1479 if(!item) 1480 return NULL; 1481 debug_print("Created folder '%s'\n", str); 1482 created_something = TRUE; 1483 if(prefs_common.inherit_folder_props && (last_parent != item->folder->node->data)) { 1484 folder_item_prefs_copy_prefs(last_parent, item); 1485 } 1486 } 1487 last_parent = item; 1488 *p1 = G_DIR_SEPARATOR; 1489 ++p1; 1490 p2 = p1; 1491 } 1492 1493 if(created_something) 1494 folder_write_list(); 1495 1496 return item; 1497 } 1498 1499 1500 /** 1501 * Get a displayable name for a FolderItem 1502 * 1503 * \param item FolderItem for that a name should be created 1504 * \return Displayable name for item, returned string has to 1505 * be freed 1506 */ 1507 gchar *folder_item_get_name(FolderItem *item) 1508 { 1509 gchar *name = NULL; 1510 1511 cm_return_val_if_fail(item != NULL, g_strdup("")); 1512 1513 switch (item->stype) { 1514 case F_INBOX: 1515 name = g_strdup(!g_strcmp0(item->name, INBOX_DIR) ? _("Inbox") : 1516 item->name); 1517 break; 1518 case F_OUTBOX: 1519 name = g_strdup(!g_strcmp0(item->name, OUTBOX_DIR) ? _("Sent") : 1520 item->name); 1521 break; 1522 case F_QUEUE: 1523 name = g_strdup(!g_strcmp0(item->name, QUEUE_DIR) ? _("Queue") : 1524 item->name); 1525 break; 1526 case F_TRASH: 1527 name = g_strdup(!g_strcmp0(item->name, TRASH_DIR) ? _("Trash") : 1528 item->name); 1529 break; 1530 case F_DRAFT: 1531 name = g_strdup(!g_strcmp0(item->name, DRAFT_DIR) ? _("Drafts") : 1532 item->name); 1533 break; 1534 default: 1535 break; 1536 } 1537 1538 if (name == NULL) { 1539 /* 1540 * should probably be done by a virtual function, 1541 * the folder knows the ui string and how to abbrev 1542 */ 1543 if (folder_item_parent(item) == NULL) { 1544 name = g_strconcat(item->name, " (", item->folder->klass->uistr, ")", NULL); 1545 } else { 1546 name = g_strdup(item->name); 1547 } 1548 } 1549 1550 if (name == NULL) 1551 name = g_strdup(""); 1552 1553 return name; 1554 } 1555 1556 gboolean folder_have_mailbox (void) 1557 { 1558 GList *cur; 1559 for (cur = folder_list; cur != NULL; cur = g_list_next(cur)) { 1560 Folder *folder = FOLDER(cur->data); 1561 if (folder->inbox && folder->outbox) 1562 return TRUE; 1563 } 1564 return FALSE; 1565 } 1566 1567 FolderItem *folder_get_default_inbox(void) 1568 { 1569 GList *flist; 1570 1571 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) { 1572 Folder * folder = FOLDER(flist->data); 1573 1574 if (folder == NULL) 1575 continue; 1576 if (folder->inbox == NULL) 1577 continue; 1578 if (folder->klass->type == F_UNKNOWN) 1579 continue; 1580 1581 return folder->inbox; 1582 } 1583 1584 return NULL; 1585 } 1586 1587 FolderItem *folder_get_default_inbox_for_class(FolderType type) 1588 { 1589 GList *flist; 1590 1591 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) { 1592 Folder * folder = FOLDER(flist->data); 1593 1594 if (folder == NULL) 1595 continue; 1596 if (folder->inbox == NULL) 1597 continue; 1598 if (folder->klass->type != type) 1599 continue; 1600 1601 return folder->inbox; 1602 } 1603 1604 return NULL; 1605 } 1606 1607 FolderItem *folder_get_default_outbox(void) 1608 { 1609 GList *flist; 1610 1611 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) { 1612 Folder * folder = FOLDER(flist->data); 1613 1614 if (folder == NULL) 1615 continue; 1616 if (folder->outbox == NULL) 1617 continue; 1618 if (folder->klass->type == F_UNKNOWN) 1619 continue; 1620 1621 return folder->outbox; 1622 } 1623 1624 return NULL; 1625 } 1626 1627 FolderItem *folder_get_default_outbox_for_class(FolderType type) 1628 { 1629 GList *flist; 1630 1631 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) { 1632 Folder * folder = FOLDER(flist->data); 1633 1634 if (folder == NULL) 1635 continue; 1636 if (folder->outbox == NULL) 1637 continue; 1638 if (folder->klass->type != type) 1639 continue; 1640 1641 return folder->outbox; 1642 } 1643 1644 return NULL; 1645 } 1646 1647 FolderItem *folder_get_default_draft(void) 1648 { 1649 GList *flist; 1650 1651 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) { 1652 Folder * folder = FOLDER(flist->data); 1653 1654 if (folder == NULL) 1655 continue; 1656 if (folder->draft == NULL) 1657 continue; 1658 if (folder->klass->type == F_UNKNOWN) 1659 continue; 1660 1661 return folder->draft; 1662 } 1663 1664 return NULL; 1665 } 1666 1667 FolderItem *folder_get_default_draft_for_class(FolderType type) 1668 { 1669 GList *flist; 1670 1671 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) { 1672 Folder * folder = FOLDER(flist->data); 1673 1674 if (folder == NULL) 1675 continue; 1676 if (folder->draft == NULL) 1677 continue; 1678 if (folder->klass->type != type) 1679 continue; 1680 1681 return folder->draft; 1682 } 1683 1684 return NULL; 1685 } 1686 1687 FolderItem *folder_get_default_queue(void) 1688 { 1689 GList *flist; 1690 1691 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) { 1692 Folder * folder = FOLDER(flist->data); 1693 1694 if (folder == NULL) 1695 continue; 1696 if (folder->queue == NULL) 1697 continue; 1698 if (folder->klass->type == F_UNKNOWN) 1699 continue; 1700 1701 return folder->queue; 1702 } 1703 1704 return NULL; 1705 } 1706 1707 FolderItem *folder_get_default_queue_for_class(FolderType type) 1708 { 1709 GList *flist; 1710 1711 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) { 1712 Folder * folder = FOLDER(flist->data); 1713 1714 if (folder == NULL) 1715 continue; 1716 if (folder->queue == NULL) 1717 continue; 1718 if (folder->klass->type != type) 1719 continue; 1720 1721 return folder->queue; 1722 } 1723 1724 return NULL; 1725 } 1726 1727 FolderItem *folder_get_default_trash(void) 1728 { 1729 GList *flist; 1730 1731 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) { 1732 Folder * folder = FOLDER(flist->data); 1733 1734 if (folder == NULL) 1735 continue; 1736 if (folder->trash == NULL) 1737 continue; 1738 if (folder->klass->type == F_UNKNOWN) 1739 continue; 1740 1741 return folder->trash; 1742 } 1743 1744 return NULL; 1745 } 1746 1747 FolderItem *folder_get_default_trash_for_class(FolderType type) 1748 { 1749 GList *flist; 1750 1751 for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) { 1752 Folder * folder = FOLDER(flist->data); 1753 1754 if (folder == NULL) 1755 continue; 1756 if (folder->trash == NULL) 1757 continue; 1758 if (folder->klass->type != type) 1759 continue; 1760 1761 return folder->trash; 1762 } 1763 1764 return NULL; 1765 } 1766 1767 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type) \ 1768 { \ 1769 if (!folder->member) { \ 1770 item = folder_item_new(folder, dir, dir); \ 1771 item->stype = type; \ 1772 folder_item_append(rootitem, item); \ 1773 folder->member = item; \ 1774 } \ 1775 } 1776 1777 void folder_set_missing_folders(void) 1778 { 1779 Folder *folder; 1780 FolderItem *rootitem; 1781 FolderItem *item; 1782 GList *list; 1783 1784 for (list = folder_list; list != NULL; list = list->next) { 1785 folder = list->data; 1786 if (FOLDER_TYPE(folder) != F_MH) continue; 1787 rootitem = FOLDER_ITEM(folder->node->data); 1788 cm_return_if_fail(rootitem != NULL); 1789 1790 if (folder->inbox && folder->outbox && folder->draft && 1791 folder->queue && folder->trash) 1792 continue; 1793 1794 if (folder->klass->create_tree(folder) < 0) { 1795 g_warning("%s: can't create the folder tree", 1796 LOCAL_FOLDER(folder)->rootpath); 1797 continue; 1798 } 1799 1800 CREATE_FOLDER_IF_NOT_EXIST(inbox, INBOX_DIR, F_INBOX); 1801 CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX); 1802 CREATE_FOLDER_IF_NOT_EXIST(draft, DRAFT_DIR, F_DRAFT); 1803 CREATE_FOLDER_IF_NOT_EXIST(queue, QUEUE_DIR, F_QUEUE); 1804 CREATE_FOLDER_IF_NOT_EXIST(trash, TRASH_DIR, F_TRASH); 1805 } 1806 } 1807 1808 static gboolean folder_unref_account_func(GNode *node, gpointer data) 1809 { 1810 FolderItem *item = node->data; 1811 PrefsAccount *account = data; 1812 1813 if (item->account == account) 1814 item->account = NULL; 1815 1816 return FALSE; 1817 } 1818 1819 void folder_unref_account_all(PrefsAccount *account) 1820 { 1821 Folder *folder; 1822 GList *list; 1823 1824 if (!account) return; 1825 1826 for (list = folder_list; list != NULL; list = list->next) { 1827 folder = list->data; 1828 if (folder->account == account) 1829 folder->account = NULL; 1830 g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, 1831 folder_unref_account_func, account); 1832 } 1833 } 1834 1835 #undef CREATE_FOLDER_IF_NOT_EXIST 1836 1837 gchar *folder_item_get_path(FolderItem *item) 1838 { 1839 Folder *folder; 1840 1841 cm_return_val_if_fail(item != NULL, NULL); 1842 folder = item->folder; 1843 cm_return_val_if_fail(folder != NULL, NULL); 1844 1845 return folder->klass->item_get_path(folder, item); 1846 } 1847 1848 static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b) 1849 { 1850 MsgInfo *msginfo_a = (MsgInfo *) a; 1851 MsgInfo *msginfo_b = (MsgInfo *) b; 1852 1853 return (msginfo_a->msgnum - msginfo_b->msgnum); 1854 } 1855 1856 static gint folder_sort_folder_list(gconstpointer a, gconstpointer b) 1857 { 1858 guint gint_a = GPOINTER_TO_INT(a); 1859 guint gint_b = GPOINTER_TO_INT(b); 1860 1861 return (gint_a - gint_b); 1862 } 1863 1864 static gint syncronize_flags(FolderItem *item, MsgInfoList *msglist) 1865 { 1866 GHashTable *relation; 1867 gint ret = 0; 1868 GSList *cur; 1869 1870 if(msglist == NULL) 1871 return 0; 1872 if(item->folder->klass->get_flags == NULL) 1873 return 0; 1874 if (item->no_select) 1875 return 0; 1876 1877 relation = g_hash_table_new(g_direct_hash, g_direct_equal); 1878 if ((ret = item->folder->klass->get_flags( 1879 item->folder, item, msglist, relation)) == 0) { 1880 gpointer data, old_key; 1881 MsgInfo *msginfo; 1882 MsgPermFlags permflags = 0; 1883 1884 folder_item_update_freeze(); 1885 folder_item_set_batch(item, TRUE); 1886 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) { 1887 msginfo = (MsgInfo *) cur->data; 1888 1889 if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data)) { 1890 permflags = GPOINTER_TO_INT(data); 1891 1892 if (msginfo->flags.perm_flags != permflags) { 1893 procmsg_msginfo_change_flags(msginfo, 1894 permflags & ~msginfo->flags.perm_flags, 0, 1895 ~permflags & msginfo->flags.perm_flags, 0); 1896 } 1897 } 1898 } 1899 folder_item_set_batch(item, FALSE); 1900 folder_item_update_thaw(); 1901 } 1902 g_hash_table_destroy(relation); 1903 1904 return ret; 1905 } 1906 1907 gint folder_item_open(FolderItem *item) 1908 { 1909 if (item->no_select) 1910 return -1; 1911 1912 if (item->scanning != ITEM_NOT_SCANNING) { 1913 debug_print("%s is scanning... \n", item->path ? item->path : item->name); 1914 return -2; 1915 } 1916 1917 item->processing_pending = FALSE; 1918 item->opened = TRUE; 1919 return 0; 1920 } 1921 1922 gint folder_item_close(FolderItem *item) 1923 { 1924 GSList *mlist, *cur; 1925 Folder *folder; 1926 1927 cm_return_val_if_fail(item != NULL, -1); 1928 1929 if (item->no_select) 1930 return -1; 1931 1932 if (item->new_msgs) { 1933 folder_item_update_freeze(); 1934 mlist = folder_item_get_msg_list(item); 1935 for (cur = mlist ; cur != NULL ; cur = cur->next) { 1936 MsgInfo * msginfo; 1937 1938 msginfo = (MsgInfo *) cur->data; 1939 if (MSG_IS_NEW(msginfo->flags)) 1940 procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0); 1941 procmsg_msginfo_free(&msginfo); 1942 } 1943 g_slist_free(mlist); 1944 folder_item_update_thaw(); 1945 } 1946 1947 folder_item_write_cache(item); 1948 1949 folder_item_update(item, F_ITEM_UPDATE_MSGCNT); 1950 1951 item->opened = FALSE; 1952 folder = item->folder; 1953 1954 if (folder->klass->close == NULL) 1955 return 0; 1956 1957 return folder->klass->close(folder, item); 1958 } 1959 1960 static MsgInfoList *get_msginfos(FolderItem *item, MsgNumberList *numlist) 1961 { 1962 MsgInfoList *msglist = NULL; 1963 Folder *folder = item->folder; 1964 if (item->no_select) 1965 return NULL; 1966 1967 if (folder->klass->get_msginfos != NULL) 1968 msglist = folder->klass->get_msginfos(folder, item, numlist); 1969 else { 1970 MsgNumberList *elem; 1971 1972 for (elem = numlist; elem != NULL; elem = g_slist_next(elem)) { 1973 MsgInfo *msginfo; 1974 guint num; 1975 1976 num = GPOINTER_TO_INT(elem->data); 1977 msginfo = folder->klass->get_msginfo(folder, item, num); 1978 if (msginfo != NULL) 1979 msglist = g_slist_prepend(msglist, msginfo); 1980 } 1981 } 1982 1983 return msglist; 1984 } 1985 1986 static MsgInfo *get_msginfo(FolderItem *item, guint num) 1987 { 1988 MsgNumberList numlist; 1989 MsgInfoList *msglist; 1990 MsgInfo *msginfo = NULL; 1991 1992 numlist.data = GINT_TO_POINTER(num); 1993 numlist.next = NULL; 1994 msglist = get_msginfos(item, &numlist); 1995 if (msglist != NULL) 1996 msginfo = procmsg_msginfo_new_ref(msglist->data); 1997 procmsg_msg_list_free(msglist); 1998 1999 return msginfo; 2000 } 2001 2002 gint folder_item_scan_full(FolderItem *item, gboolean filtering) 2003 { 2004 Folder *folder; 2005 GSList *folder_list = NULL, *cache_list = NULL; 2006 GSList *folder_list_cur, *cache_list_cur, *new_list = NULL; 2007 GSList *exists_list = NULL, *elem; 2008 GSList *newmsg_list = NULL; 2009 guint newcnt = 0, unreadcnt = 0, totalcnt = 0; 2010 guint markedcnt = 0, unreadmarkedcnt = 0; 2011 guint repliedcnt = 0, forwardedcnt = 0; 2012 guint lockedcnt = 0, ignoredcnt = 0, watchedcnt = 0; 2013 2014 guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num; 2015 gboolean update_flags = 0, old_uids_valid = FALSE; 2016 GHashTable *subject_table = NULL; 2017 2018 cm_return_val_if_fail(item != NULL, -1); 2019 if (item->path == NULL) return -1; 2020 2021 folder = item->folder; 2022 2023 cm_return_val_if_fail(folder != NULL, -1); 2024 cm_return_val_if_fail(folder->klass->get_num_list != NULL, -1); 2025 2026 item->scanning = ITEM_SCANNING_WITH_FLAGS; 2027 2028 debug_print("Scanning folder %s for cache changes.\n", item->path ? item->path : "(null)"); 2029 2030 /* Get list of messages for folder and cache */ 2031 if (folder->klass->get_num_list(item->folder, item, &folder_list, &old_uids_valid) < 0) { 2032 debug_print("Error fetching list of message numbers\n"); 2033 item->scanning = ITEM_NOT_SCANNING; 2034 return(-1); 2035 } 2036 2037 if(prefs_common.thread_by_subject) { 2038 subject_table = g_hash_table_new(g_str_hash, g_str_equal); 2039 } 2040 2041 if (old_uids_valid) { 2042 if (!item->cache) 2043 folder_item_read_cache(item); 2044 cache_list = msgcache_get_msg_list(item->cache); 2045 } else { 2046 if (item->cache) 2047 msgcache_destroy(item->cache); 2048 item->cache = msgcache_new(); 2049 item->cache_dirty = TRUE; 2050 item->mark_dirty = TRUE; 2051 item->tags_dirty = TRUE; 2052 cache_list = NULL; 2053 } 2054 2055 /* Sort both lists */ 2056 cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum); 2057 folder_list = g_slist_sort(folder_list, folder_sort_folder_list); 2058 2059 cache_list_cur = cache_list; 2060 folder_list_cur = folder_list; 2061 2062 if (cache_list_cur != NULL) { 2063 GSList *cache_list_last; 2064 2065 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum; 2066 cache_list_last = g_slist_last(cache_list); 2067 cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum; 2068 } else { 2069 cache_cur_num = G_MAXUINT; 2070 cache_max_num = 0; 2071 } 2072 2073 if (folder_list_cur != NULL) { 2074 GSList *folder_list_last; 2075 2076 folder_cur_num = GPOINTER_TO_UINT(folder_list_cur->data); 2077 folder_list_last = g_slist_last(folder_list); 2078 folder_max_num = GPOINTER_TO_UINT(folder_list_last->data); 2079 } else { 2080 folder_cur_num = G_MAXUINT; 2081 folder_max_num = 0; 2082 } 2083 2084 while ((cache_cur_num != G_MAXUINT) || (folder_cur_num != G_MAXUINT)) { 2085 /* 2086 * Message only exists in the folder 2087 * Remember message for fetching 2088 */ 2089 if (folder_cur_num < cache_cur_num) { 2090 gboolean add = FALSE; 2091 2092 switch(FOLDER_TYPE(folder)) { 2093 case F_NEWS: 2094 if (folder_cur_num < cache_max_num) 2095 break; 2096 2097 if (folder->account->max_articles == 0) { 2098 add = TRUE; 2099 } 2100 2101 if (folder_max_num <= folder->account->max_articles) { 2102 add = TRUE; 2103 } else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) { 2104 add = TRUE; 2105 } 2106 break; 2107 default: 2108 add = TRUE; 2109 break; 2110 } 2111 2112 if (add) { 2113 new_list = g_slist_prepend(new_list, GUINT_TO_POINTER(folder_cur_num)); 2114 debug_print("Remembered message %u for fetching\n", folder_cur_num); 2115 } 2116 2117 /* Move to next folder number */ 2118 if (folder_list_cur) 2119 folder_list_cur = folder_list_cur->next; 2120 2121 if (folder_list_cur != NULL) 2122 folder_cur_num = GPOINTER_TO_UINT(folder_list_cur->data); 2123 else 2124 folder_cur_num = G_MAXUINT; 2125 2126 continue; 2127 } 2128 2129 /* 2130 * Message only exists in the cache 2131 * Remove the message from the cache 2132 */ 2133 if (cache_cur_num < folder_cur_num) { 2134 msgcache_remove_msg(item->cache, cache_cur_num); 2135 debug_print("Removed message %u from cache.\n", cache_cur_num); 2136 2137 /* Move to next cache number */ 2138 if (cache_list_cur) 2139 cache_list_cur = cache_list_cur->next; 2140 2141 if (cache_list_cur != NULL) 2142 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum; 2143 else 2144 cache_cur_num = G_MAXUINT; 2145 2146 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT; 2147 2148 continue; 2149 } 2150 2151 /* 2152 * Message number exists in folder and cache! 2153 * Check if the message has been modified 2154 */ 2155 if (cache_cur_num == folder_cur_num) { 2156 MsgInfo *msginfo; 2157 2158 msginfo = msgcache_get_msg(item->cache, folder_cur_num); 2159 if (msginfo && folder->klass->is_msg_changed && folder->klass->is_msg_changed(folder, item, msginfo)) { 2160 msgcache_remove_msg(item->cache, msginfo->msgnum); 2161 new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum)); 2162 procmsg_msginfo_free(&msginfo); 2163 2164 debug_print("Remembering message %u to update...\n", folder_cur_num); 2165 } else if (msginfo) { 2166 exists_list = g_slist_prepend(exists_list, msginfo); 2167 2168 if(prefs_common.thread_by_subject && 2169 MSG_IS_IGNORE_THREAD(msginfo->flags) && 2170 !subject_table_lookup(subject_table, msginfo->subject)) { 2171 subject_table_insert(subject_table, msginfo->subject, msginfo); 2172 } 2173 } 2174 2175 /* Move to next folder and cache number */ 2176 if (cache_list_cur) 2177 cache_list_cur = cache_list_cur->next; 2178 2179 if (folder_list_cur) 2180 folder_list_cur = folder_list_cur->next; 2181 2182 if (cache_list_cur != NULL) 2183 cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum; 2184 else 2185 cache_cur_num = G_MAXUINT; 2186 2187 if (folder_list_cur != NULL) 2188 folder_cur_num = GPOINTER_TO_UINT(folder_list_cur->data); 2189 else 2190 folder_cur_num = G_MAXUINT; 2191 2192 continue; 2193 } 2194 } 2195 2196 for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur)) 2197 procmsg_msginfo_free((MsgInfo **)&(cache_list_cur->data)); 2198 2199 g_slist_free(cache_list); 2200 g_slist_free(folder_list); 2201 2202 if (new_list != NULL) { 2203 GSList *tmp_list = NULL; 2204 newmsg_list = get_msginfos(item, new_list); 2205 g_slist_free(new_list); 2206 tmp_list = g_slist_concat(g_slist_copy(exists_list), g_slist_copy(newmsg_list)); 2207 syncronize_flags(item, tmp_list); 2208 g_slist_free(tmp_list); 2209 } else { 2210 syncronize_flags(item, exists_list); 2211 } 2212 2213 folder_item_update_freeze(); 2214 2215 item->scanning = ITEM_SCANNING; 2216 2217 if (newmsg_list != NULL) { 2218 GSList *elem = NULL; 2219 for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) { 2220 MsgInfo *msginfo = (MsgInfo *) elem->data; 2221 2222 msgcache_add_msg(item->cache, msginfo); 2223 exists_list = g_slist_prepend(exists_list, msginfo); 2224 2225 if(prefs_common.thread_by_subject && 2226 MSG_IS_IGNORE_THREAD(msginfo->flags) && 2227 !subject_table_lookup(subject_table, msginfo->subject)) { 2228 subject_table_insert(subject_table, msginfo->subject, msginfo); 2229 } 2230 } 2231 2232 if (prefs_common.real_time_sync) 2233 folder_item_synchronise(item); 2234 2235 g_slist_free(newmsg_list); 2236 2237 update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT; 2238 } 2239 2240 folder_item_set_batch(item, TRUE); 2241 for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) { 2242 MsgInfo *msginfo, *parent_msginfo; 2243 2244 msginfo = elem->data; 2245 if (MSG_IS_IGNORE_THREAD(msginfo->flags) && (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags))) 2246 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0); 2247 if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) { 2248 procmsg_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0); 2249 } 2250 if (!MSG_IS_WATCH_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_WATCH_THREAD)) { 2251 procmsg_msginfo_set_flags(msginfo, MSG_WATCH_THREAD, 0); 2252 } 2253 if(prefs_common.thread_by_subject && !msginfo->inreplyto && 2254 !msginfo->references && !MSG_IS_IGNORE_THREAD(msginfo->flags) && 2255 (parent_msginfo = subject_table_lookup(subject_table, msginfo->subject))) 2256 { 2257 if(MSG_IS_IGNORE_THREAD(parent_msginfo->flags)) { 2258 procmsg_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, 2259 MSG_NEW | MSG_UNREAD, 0); 2260 } 2261 } 2262 if ((folder_has_parent_of_type(item, F_OUTBOX) || 2263 folder_has_parent_of_type(item, F_QUEUE) || 2264 folder_has_parent_of_type(item, F_TRASH)) && 2265 (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags))) 2266 procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0); 2267 if (MSG_IS_NEW(msginfo->flags)) 2268 newcnt++; 2269 if (MSG_IS_UNREAD(msginfo->flags)) 2270 unreadcnt++; 2271 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo)) 2272 unreadmarkedcnt++; 2273 if (MSG_IS_MARKED(msginfo->flags)) 2274 markedcnt++; 2275 if (MSG_IS_REPLIED(msginfo->flags)) 2276 repliedcnt++; 2277 if (MSG_IS_FORWARDED(msginfo->flags)) 2278 forwardedcnt++; 2279 if (MSG_IS_LOCKED(msginfo->flags)) 2280 lockedcnt++; 2281 if (MSG_IS_IGNORE_THREAD(msginfo->flags)) 2282 ignoredcnt++; 2283 if (MSG_IS_WATCH_THREAD(msginfo->flags)) 2284 watchedcnt++; 2285 2286 totalcnt++; 2287 2288 procmsg_msginfo_free(&msginfo); 2289 } 2290 folder_item_set_batch(item, FALSE); 2291 g_slist_free(exists_list); 2292 2293 if(prefs_common.thread_by_subject) { 2294 g_hash_table_destroy(subject_table); 2295 } 2296 2297 if (item->new_msgs != newcnt || item->unread_msgs != unreadcnt 2298 || item->total_msgs != totalcnt || item->marked_msgs != markedcnt 2299 || item->unreadmarked_msgs != unreadmarkedcnt 2300 || item->replied_msgs != repliedcnt || item->forwarded_msgs != forwardedcnt 2301 || item->locked_msgs != lockedcnt || item->ignored_msgs != ignoredcnt 2302 || item->watched_msgs != watchedcnt) { 2303 update_flags |= F_ITEM_UPDATE_CONTENT; 2304 } 2305 2306 item->new_msgs = newcnt; 2307 item->unread_msgs = unreadcnt; 2308 item->total_msgs = totalcnt; 2309 item->unreadmarked_msgs = unreadmarkedcnt; 2310 item->marked_msgs = markedcnt; 2311 item->replied_msgs = repliedcnt; 2312 item->forwarded_msgs = forwardedcnt; 2313 item->locked_msgs = lockedcnt; 2314 item->ignored_msgs = ignoredcnt; 2315 item->watched_msgs = watchedcnt; 2316 2317 update_flags |= F_ITEM_UPDATE_MSGCNT; 2318 2319 folder_item_update(item, update_flags); 2320 folder_item_update_thaw(); 2321 2322 item->scanning = ITEM_NOT_SCANNING; 2323 2324 return 0; 2325 } 2326 2327 gint folder_item_scan(FolderItem *item) 2328 { 2329 return folder_item_scan_full(item, TRUE); 2330 } 2331 2332 static void folder_count_total_cache_memusage(FolderItem *item, gpointer data) 2333 { 2334 gint *memusage = (gint *)data; 2335 2336 if (item->cache == NULL) 2337 return; 2338 2339 *memusage += msgcache_get_memory_usage(item->cache); 2340 } 2341 2342 static gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b) 2343 { 2344 FolderItem *fa = (FolderItem *)a; 2345 FolderItem *fb = (FolderItem *)b; 2346 2347 return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache)); 2348 } 2349 2350 static void folder_find_expired_caches(FolderItem *item, gpointer data) 2351 { 2352 GSList **folder_item_list = (GSList **)data; 2353 gint difftime, expiretime; 2354 2355 if (item->cache == NULL) 2356 return; 2357 2358 if (item->opened > 0) 2359 return; 2360 2361 difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache)); 2362 expiretime = prefs_common.cache_min_keep_time * 60; 2363 // debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime); 2364 2365 if (difftime > expiretime && !item->opened && !item->processing_pending) { 2366 *folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func); 2367 } 2368 } 2369 2370 gboolean folder_item_free_cache(FolderItem *item, gboolean force) 2371 { 2372 cm_return_val_if_fail(item != NULL, TRUE); 2373 2374 if (item->cache == NULL) 2375 return TRUE; 2376 2377 if (item->opened > 0 && !force) 2378 return FALSE; 2379 2380 folder_item_write_cache(item); 2381 msgcache_destroy(item->cache); 2382 item->cache = NULL; 2383 return TRUE; 2384 } 2385 2386 void folder_clean_cache_memory_force(void) 2387 { 2388 int old_cache_max_mem_usage = prefs_common.cache_max_mem_usage; 2389 int old_cache_min_keep_time = prefs_common.cache_min_keep_time; 2390 2391 prefs_common.cache_max_mem_usage = 0; 2392 prefs_common.cache_min_keep_time = 0; 2393 2394 folder_clean_cache_memory(NULL); 2395 2396 prefs_common.cache_max_mem_usage = old_cache_max_mem_usage; 2397 prefs_common.cache_min_keep_time = old_cache_min_keep_time; 2398 } 2399 2400 void folder_clean_cache_memory(FolderItem *protected_item) 2401 { 2402 gint memusage = 0; 2403 2404 folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage); 2405 debug_print("Total cache memory usage: %d\n", memusage); 2406 2407 if (memusage > (prefs_common.cache_max_mem_usage * 1024)) { 2408 GSList *folder_item_list = NULL, *listitem; 2409 2410 debug_print("Trying to free cache memory\n"); 2411 2412 folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list); 2413 listitem = folder_item_list; 2414 while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) { 2415 FolderItem *item = (FolderItem *)(listitem->data); 2416 gint cache_size = 0; 2417 if (item == protected_item) { 2418 listitem = listitem->next; 2419 continue; 2420 } 2421 debug_print("Freeing cache memory for %s\n", item->path ? item->path : item->name); 2422 cache_size = msgcache_get_memory_usage(item->cache); 2423 if (folder_item_free_cache(item, FALSE)) 2424 memusage -= cache_size; 2425 2426 listitem = listitem->next; 2427 } 2428 g_slist_free(folder_item_list); 2429 } 2430 } 2431 2432 static void folder_item_remove_cached_msg(FolderItem *item, MsgInfo *msginfo) 2433 { 2434 Folder *folder = item->folder; 2435 2436 cm_return_if_fail(folder != NULL); 2437 2438 if (folder->klass->remove_cached_msg == NULL) 2439 return; 2440 2441 folder->klass->remove_cached_msg(folder, item, msginfo); 2442 } 2443 2444 static void folder_item_clean_local_files(FolderItem *item, gint days) 2445 { 2446 cm_return_if_fail(item != NULL); 2447 cm_return_if_fail(item->folder != NULL); 2448 2449 if (FOLDER_TYPE(item->folder) == F_IMAP || 2450 FOLDER_TYPE(item->folder) == F_NEWS) { 2451 GSList *msglist = folder_item_get_msg_list(item); 2452 GSList *cur; 2453 time_t t = time(NULL); 2454 for (cur = msglist; cur; cur = cur->next) { 2455 MsgInfo *msginfo = (MsgInfo *)cur->data; 2456 gint age = (t - msginfo->date_t) / (60*60*24); 2457 if (age > days) 2458 folder_item_remove_cached_msg(item, msginfo); 2459 } 2460 procmsg_msg_list_free(msglist); 2461 } 2462 } 2463 2464 static void folder_item_read_cache(FolderItem *item) 2465 { 2466 gchar *cache_file, *mark_file, *tags_file; 2467 cm_return_if_fail(item != NULL); 2468 2469 if (item->path != NULL) { 2470 cache_file = folder_item_get_cache_file(item); 2471 mark_file = folder_item_get_mark_file(item); 2472 tags_file = folder_item_get_tags_file(item); 2473 item->cache = msgcache_read_cache(item, cache_file); 2474 item->cache_dirty = FALSE; 2475 item->mark_dirty = FALSE; 2476 item->tags_dirty = FALSE; 2477 if (!item->cache) { 2478 MsgInfoList *list, *cur; 2479 guint newcnt = 0, unreadcnt = 0; 2480 guint markedcnt = 0, unreadmarkedcnt = 0; 2481 guint repliedcnt = 0, forwardedcnt = 0; 2482 guint lockedcnt = 0, ignoredcnt = 0; 2483 guint watchedcnt = 0; 2484 MsgInfo *msginfo; 2485 2486 item->cache = msgcache_new(); 2487 item->cache_dirty = TRUE; 2488 item->mark_dirty = TRUE; 2489 item->tags_dirty = TRUE; 2490 folder_item_scan_full(item, TRUE); 2491 2492 msgcache_read_mark(item->cache, mark_file); 2493 2494 list = msgcache_get_msg_list(item->cache); 2495 for (cur = list; cur != NULL; cur = g_slist_next(cur)) { 2496 msginfo = cur->data; 2497 2498 if (MSG_IS_NEW(msginfo->flags)) 2499 newcnt++; 2500 if (MSG_IS_UNREAD(msginfo->flags)) 2501 unreadcnt++; 2502 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo)) 2503 unreadmarkedcnt++; 2504 if (MSG_IS_MARKED(msginfo->flags)) 2505 markedcnt++; 2506 if (MSG_IS_REPLIED(msginfo->flags)) 2507 repliedcnt++; 2508 if (MSG_IS_FORWARDED(msginfo->flags)) 2509 forwardedcnt++; 2510 if (MSG_IS_LOCKED(msginfo->flags)) 2511 lockedcnt++; 2512 if (MSG_IS_IGNORE_THREAD(msginfo->flags)) 2513 ignoredcnt++; 2514 if (MSG_IS_WATCH_THREAD(msginfo->flags)) 2515 watchedcnt++; 2516 procmsg_msginfo_unset_flags(msginfo, MSG_FULLY_CACHED, 0); 2517 } 2518 item->new_msgs = newcnt; 2519 item->unread_msgs = unreadcnt; 2520 item->unreadmarked_msgs = unreadmarkedcnt; 2521 item->marked_msgs = markedcnt; 2522 item->replied_msgs = repliedcnt; 2523 item->forwarded_msgs = forwardedcnt; 2524 item->locked_msgs = lockedcnt; 2525 item->ignored_msgs = ignoredcnt; 2526 item->watched_msgs = watchedcnt; 2527 procmsg_msg_list_free(list); 2528 } else 2529 msgcache_read_mark(item->cache, mark_file); 2530 2531 g_free(cache_file); 2532 g_free(mark_file); 2533 g_free(tags_file); 2534 } else { 2535 item->cache = msgcache_new(); 2536 item->cache_dirty = TRUE; 2537 item->mark_dirty = TRUE; 2538 item->tags_dirty = TRUE; 2539 } 2540 2541 folder_clean_cache_memory(item); 2542 } 2543 2544 void folder_item_write_cache(FolderItem *item) 2545 { 2546 gchar *cache_file = NULL, *mark_file = NULL, *tags_file = NULL; 2547 FolderItemPrefs *prefs; 2548 gint filemode = 0; 2549 gchar *id; 2550 time_t last_mtime = (time_t)0; 2551 gboolean need_scan = FALSE; 2552 2553 if (!item || !item->path || !item->cache) 2554 return; 2555 2556 last_mtime = item->mtime; 2557 if (item->folder->klass->set_mtime) { 2558 if (item->folder->klass->scan_required) 2559 need_scan = item->folder->klass->scan_required(item->folder, item); 2560 else 2561 need_scan = TRUE; 2562 } 2563 2564 id = folder_item_get_identifier(item); 2565 debug_print("Save cache for folder %s\n", id); 2566 g_free(id); 2567 2568 if (item->cache_dirty) 2569 cache_file = folder_item_get_cache_file(item); 2570 if (item->cache_dirty || item->mark_dirty) 2571 mark_file = folder_item_get_mark_file(item); 2572 if (item->cache_dirty || item->tags_dirty) 2573 tags_file = folder_item_get_tags_file(item); 2574 if (msgcache_write(cache_file, mark_file, tags_file, item->cache) == 0) { 2575 prefs = item->prefs; 2576 if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) { 2577 filemode = prefs->folder_chmod; 2578 if (filemode & S_IRGRP) filemode |= S_IWGRP; 2579 if (filemode & S_IROTH) filemode |= S_IWOTH; 2580 if (cache_file) { 2581 if (chmod(cache_file, filemode) < 0) 2582 FILE_OP_ERROR(cache_file, "chmod"); 2583 } 2584 if (mark_file) { 2585 if (chmod(mark_file, filemode) < 0) 2586 FILE_OP_ERROR(mark_file, "chmod"); 2587 } 2588 } 2589 } else { 2590 item->cache_dirty = TRUE; 2591 item->mark_dirty = TRUE; 2592 item->tags_dirty = TRUE; 2593 } 2594 2595 if (!need_scan && item->folder->klass->set_mtime) { 2596 if (item->mtime == last_mtime) { 2597 item->folder->klass->set_mtime(item->folder, item); 2598 } 2599 } 2600 2601 g_free(cache_file); 2602 g_free(mark_file); 2603 g_free(tags_file); 2604 } 2605 2606 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num) 2607 { 2608 MsgInfo *msginfo = NULL; 2609 2610 cm_return_val_if_fail(item != NULL, NULL); 2611 if (item->no_select) 2612 return NULL; 2613 if (!item->cache) 2614 folder_item_read_cache(item); 2615 2616 if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL) 2617 return msginfo; 2618 2619 msginfo = get_msginfo(item, num); 2620 if (msginfo != NULL) { 2621 msgcache_add_msg(item->cache, msginfo); 2622 return msginfo; 2623 } 2624 2625 return NULL; 2626 } 2627 2628 MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid) 2629 { 2630 MsgInfo *msginfo; 2631 2632 cm_return_val_if_fail(item != NULL, NULL); 2633 cm_return_val_if_fail(msgid != NULL, NULL); 2634 if (item->no_select) 2635 return NULL; 2636 2637 if (!item->cache) 2638 folder_item_read_cache(item); 2639 2640 if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL) 2641 return msginfo; 2642 2643 return NULL; 2644 } 2645 2646 GSList *folder_item_get_msg_list(FolderItem *item) 2647 { 2648 cm_return_val_if_fail(item != NULL, NULL); 2649 if (item->no_select) 2650 return NULL; 2651 2652 if (item->cache == 0) 2653 folder_item_read_cache(item); 2654 2655 cm_return_val_if_fail(item->cache != NULL, NULL); 2656 2657 return msgcache_get_msg_list(item->cache); 2658 } 2659 2660 static void msginfo_set_mime_flags(GNode *node, gpointer data) 2661 { 2662 MsgInfo *msginfo = data; 2663 MimeInfo *mimeinfo = node->data; 2664 2665 if (mimeinfo->disposition == DISPOSITIONTYPE_ATTACHMENT && 2666 (!mimeinfo->subtype || (strcmp(mimeinfo->subtype, "pgp-signature") && 2667 strcmp(mimeinfo->subtype, "x-pkcs7-signature") && 2668 strcmp(mimeinfo->subtype, "pkcs7-signature")))) { 2669 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT); 2670 } else if (mimeinfo->disposition == DISPOSITIONTYPE_UNKNOWN && 2671 mimeinfo->id == NULL && 2672 mimeinfo->type != MIMETYPE_TEXT && 2673 mimeinfo->type != MIMETYPE_MULTIPART) { 2674 if (!mimeinfo->subtype 2675 || (strcmp(mimeinfo->subtype, "pgp-signature") && 2676 strcmp(mimeinfo->subtype, "x-pkcs7-signature") && 2677 strcmp(mimeinfo->subtype, "pkcs7-signature"))) 2678 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT); 2679 } else if (mimeinfo->disposition == DISPOSITIONTYPE_INLINE && 2680 mimeinfo->id == NULL && 2681 (strcmp(mimeinfo->subtype, "pgp-signature") && 2682 strcmp(mimeinfo->subtype, "x-pkcs7-signature") && 2683 strcmp(mimeinfo->subtype, "pkcs7-signature")) && 2684 (procmime_mimeinfo_get_parameter(mimeinfo, "name") != NULL || 2685 procmime_mimeinfo_get_parameter(mimeinfo, "filename") != NULL)) { 2686 procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT); 2687 } 2688 2689 /* don't descend below top level message for signed and encrypted info */ 2690 if (mimeinfo->type == MIMETYPE_MESSAGE) 2691 return; 2692 2693 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo); 2694 } 2695 2696 gchar *folder_item_fetch_msg(FolderItem *item, gint num) 2697 { 2698 Folder *folder; 2699 gchar *msgfile; 2700 MsgInfo *msginfo; 2701 2702 cm_return_val_if_fail(item != NULL, NULL); 2703 2704 folder = item->folder; 2705 2706 cm_return_val_if_fail(folder->klass->fetch_msg != NULL, NULL); 2707 if (item->no_select) 2708 return NULL; 2709 2710 msgfile = folder->klass->fetch_msg(folder, item, num); 2711 2712 if (msgfile != NULL) { 2713 msginfo = folder_item_get_msginfo(item, num); 2714 if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) { 2715 MimeInfo *mimeinfo; 2716 2717 item->cache_dirty = TRUE; 2718 item->mark_dirty = TRUE; 2719 item->tags_dirty = TRUE; 2720 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) && 2721 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) 2722 mimeinfo = procmime_scan_file(msgfile); 2723 else 2724 mimeinfo = procmime_scan_queue_file(msgfile); 2725 /* check for attachments */ 2726 if (mimeinfo != NULL) { 2727 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo); 2728 procmime_mimeinfo_free_all(&mimeinfo); 2729 2730 procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED); 2731 } 2732 } 2733 procmsg_msginfo_free(&msginfo); 2734 } 2735 2736 return msgfile; 2737 } 2738 2739 gchar *folder_item_fetch_msg_full(FolderItem *item, gint num, gboolean headers, 2740 gboolean body) 2741 { 2742 Folder *folder; 2743 gchar *msgfile; 2744 MsgInfo *msginfo; 2745 2746 cm_return_val_if_fail(item != NULL, NULL); 2747 if (item->no_select) 2748 return NULL; 2749 2750 folder = item->folder; 2751 2752 if (folder->klass->fetch_msg_full == NULL) 2753 return folder_item_fetch_msg(item, num); 2754 2755 if (item->prefs->offlinesync) 2756 msgfile = folder->klass->fetch_msg_full(folder, item, num, 2757 TRUE, TRUE); 2758 else 2759 msgfile = folder->klass->fetch_msg_full(folder, item, num, 2760 headers, body); 2761 2762 if (msgfile != NULL) { 2763 msginfo = folder_item_get_msginfo(item, num); 2764 if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) { 2765 MimeInfo *mimeinfo; 2766 2767 item->cache_dirty = TRUE; 2768 item->mark_dirty = TRUE; 2769 item->tags_dirty = TRUE; 2770 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) && 2771 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) 2772 mimeinfo = procmime_scan_file(msgfile); 2773 else 2774 mimeinfo = procmime_scan_queue_file(msgfile); 2775 /* check for attachments */ 2776 if (mimeinfo != NULL) { 2777 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo); 2778 procmime_mimeinfo_free_all(&mimeinfo); 2779 2780 procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED); 2781 } 2782 } 2783 procmsg_msginfo_free(&msginfo); 2784 } 2785 2786 return msgfile; 2787 } 2788 2789 2790 static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file) 2791 { 2792 static HeaderEntry hentry[] = {{"Message-ID:", NULL, TRUE}, 2793 {NULL, NULL, FALSE}}; 2794 FILE *fp; 2795 MsgInfo *msginfo; 2796 gint msgnum = 0; 2797 gchar buf[BUFFSIZE]; 2798 2799 if ((fp = g_fopen(file, "rb")) == NULL) 2800 return 0; 2801 2802 if ((folder_has_parent_of_type(dest, F_QUEUE)) || 2803 (folder_has_parent_of_type(dest, F_DRAFT))) 2804 while (fgets(buf, sizeof(buf), fp) != NULL) { 2805 /* new way */ 2806 if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1", 2807 strlen("X-Claws-End-Special-Headers:"))) || 2808 (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1", 2809 strlen("X-Sylpheed-End-Special-Headers:")))) 2810 break; 2811 /* old way */ 2812 if (buf[0] == '\r' || buf[0] == '\n') break; 2813 /* from other mailers */ 2814 if (!strncmp(buf, "Date: ", 6) 2815 || !strncmp(buf, "To: ", 4) 2816 || !strncmp(buf, "From: ", 6) 2817 || !strncmp(buf, "Subject: ", 9)) { 2818 rewind(fp); 2819 break; 2820 } 2821 } 2822 2823 procheader_get_header_fields(fp, hentry); 2824 debug_print("looking for %s\n", hentry[0].body); 2825 if (hentry[0].body) { 2826 extract_parenthesis(hentry[0].body, '<', '>'); 2827 remove_space(hentry[0].body); 2828 if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) { 2829 msgnum = msginfo->msgnum; 2830 procmsg_msginfo_free(&msginfo); 2831 2832 debug_print("found message as uid %d\n", msgnum); 2833 } 2834 } 2835 2836 g_free(hentry[0].body); 2837 hentry[0].body = NULL; 2838 fclose(fp); 2839 2840 return msgnum; 2841 } 2842 2843 static void copy_msginfo_flags(MsgInfo *source, MsgInfo *dest) 2844 { 2845 MsgPermFlags perm_flags = 0; 2846 MsgTmpFlags tmp_flags = 0; 2847 2848 /* create new flags */ 2849 if (source != NULL) { 2850 /* copy original flags */ 2851 perm_flags = source->flags.perm_flags; 2852 tmp_flags = source->flags.tmp_flags; 2853 } else { 2854 perm_flags = dest->flags.perm_flags; 2855 tmp_flags = dest->flags.tmp_flags; 2856 } 2857 2858 /* remove new, unread and deleted in special folders */ 2859 if (folder_has_parent_of_type(dest->folder, F_OUTBOX) || 2860 folder_has_parent_of_type(dest->folder, F_QUEUE) || 2861 folder_has_parent_of_type(dest->folder, F_DRAFT) || 2862 folder_has_parent_of_type(dest->folder, F_TRASH)) 2863 perm_flags &= ~(MSG_NEW | MSG_UNREAD | MSG_DELETED); 2864 2865 /* set ignore flag of ignored parent exists */ 2866 if (procmsg_msg_has_flagged_parent(dest, MSG_IGNORE_THREAD)) 2867 perm_flags |= MSG_IGNORE_THREAD; 2868 2869 /* unset FULLY_CACHED flags */ 2870 perm_flags &= ~MSG_FULLY_CACHED; 2871 2872 if (procmsg_msg_has_flagged_parent(dest, MSG_WATCH_THREAD)) 2873 perm_flags |= MSG_WATCH_THREAD; 2874 2875 /* Unset tmp flags that should not be copied */ 2876 tmp_flags &= ~(MSG_MOVE | MSG_COPY | MSG_MOVE_DONE); 2877 2878 /* unset flags that are set but should not */ 2879 /* and set new flags */ 2880 procmsg_msginfo_change_flags(dest, 2881 ~dest->flags.perm_flags & perm_flags, 2882 ~dest->flags.tmp_flags & tmp_flags, 2883 dest->flags.perm_flags & ~perm_flags, 2884 dest->flags.tmp_flags & ~tmp_flags); 2885 2886 if (source && source->tags) { 2887 g_slist_free(dest->tags); 2888 dest->tags = g_slist_copy(source->tags); 2889 folder_item_commit_tags(dest->folder, dest, dest->tags, NULL); 2890 } 2891 } 2892 2893 static void add_msginfo_to_cache(FolderItem *item, MsgInfo *newmsginfo, MsgInfo *flagsource) 2894 { 2895 /* update folder stats */ 2896 if (MSG_IS_NEW(newmsginfo->flags)) 2897 item->new_msgs++; 2898 if (MSG_IS_UNREAD(newmsginfo->flags)) 2899 item->unread_msgs++; 2900 if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo)) 2901 item->unreadmarked_msgs++; 2902 if (MSG_IS_MARKED(newmsginfo->flags)) 2903 item->marked_msgs++; 2904 if (MSG_IS_REPLIED(newmsginfo->flags)) 2905 item->replied_msgs++; 2906 if (MSG_IS_FORWARDED(newmsginfo->flags)) 2907 item->forwarded_msgs++; 2908 if (MSG_IS_LOCKED(newmsginfo->flags)) 2909 item->locked_msgs++; 2910 if (MSG_IS_IGNORE_THREAD(newmsginfo->flags)) 2911 item->ignored_msgs++; 2912 if (MSG_IS_WATCH_THREAD(newmsginfo->flags)) 2913 item->watched_msgs++; 2914 item->total_msgs++; 2915 2916 folder_item_update_freeze(); 2917 2918 if (!item->cache) 2919 folder_item_read_cache(item); 2920 2921 msgcache_add_msg(item->cache, newmsginfo); 2922 copy_msginfo_flags(flagsource, newmsginfo); 2923 folder_item_update_with_msg(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_ADDMSG, newmsginfo); 2924 folder_item_update_thaw(); 2925 } 2926 2927 static void remove_msginfo_from_cache(FolderItem *item, MsgInfo *msginfo) 2928 { 2929 MsgInfoUpdate msginfo_update; 2930 2931 if (!item->cache) 2932 folder_item_read_cache(item); 2933 2934 if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) 2935 msginfo->folder->new_msgs--; 2936 if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags)) 2937 msginfo->folder->unread_msgs--; 2938 if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo)) 2939 msginfo->folder->unreadmarked_msgs--; 2940 if (MSG_IS_MARKED(msginfo->flags)) 2941 item->marked_msgs--; 2942 if (MSG_IS_REPLIED(msginfo->flags)) 2943 item->replied_msgs--; 2944 if (MSG_IS_FORWARDED(msginfo->flags)) 2945 item->forwarded_msgs--; 2946 if (MSG_IS_LOCKED(msginfo->flags)) 2947 item->locked_msgs--; 2948 if (MSG_IS_IGNORE_THREAD(msginfo->flags)) 2949 item->ignored_msgs--; 2950 if (MSG_IS_WATCH_THREAD(msginfo->flags)) 2951 item->watched_msgs--; 2952 2953 msginfo->folder->total_msgs--; 2954 2955 msginfo_update.msginfo = msginfo; 2956 msginfo_update.flags = MSGINFO_UPDATE_DELETED; 2957 hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update); 2958 2959 msgcache_remove_msg(item->cache, msginfo->msgnum); 2960 folder_item_update_with_msg(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_REMOVEMSG, msginfo); 2961 } 2962 2963 gint folder_item_add_msg(FolderItem *dest, const gchar *file, 2964 MsgFlags *flags, gboolean remove_source) 2965 { 2966 GSList file_list; 2967 MsgFileInfo fileinfo; 2968 2969 cm_return_val_if_fail(dest != NULL, -1); 2970 cm_return_val_if_fail(file != NULL, -1); 2971 2972 fileinfo.msginfo = NULL; 2973 fileinfo.file = (gchar *)file; 2974 fileinfo.flags = flags; 2975 file_list.data = &fileinfo; 2976 file_list.next = NULL; 2977 2978 return folder_item_add_msgs(dest, &file_list, remove_source); 2979 } 2980 2981 gint folder_item_add_msgs(FolderItem *dest, GSList *file_list, 2982 gboolean remove_source) 2983 { 2984 Folder *folder; 2985 gint ret, num, lastnum = -1; 2986 GSList *file_cur; 2987 GHashTable *relation; 2988 MsgFileInfo *fileinfo = NULL; 2989 gboolean folderscan = FALSE; 2990 2991 cm_return_val_if_fail(dest != NULL, -1); 2992 cm_return_val_if_fail(file_list != NULL, -1); 2993 cm_return_val_if_fail(dest->folder != NULL, -1); 2994 if (dest->no_select) 2995 return -1; 2996 2997 folder = dest->folder; 2998 2999 relation = g_hash_table_new(g_direct_hash, g_direct_equal); 3000 3001 if (folder->klass->add_msgs != NULL) { 3002 ret = folder->klass->add_msgs(folder, dest, file_list, relation); 3003 if (ret < 0) { 3004 g_hash_table_destroy(relation); 3005 return ret; 3006 } 3007 } else { 3008 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) { 3009 fileinfo = (MsgFileInfo *) file_cur->data; 3010 3011 ret = folder->klass->add_msg(folder, dest, fileinfo->file, fileinfo->flags); 3012 if (ret < 0) { 3013 g_hash_table_destroy(relation); 3014 return ret; 3015 } 3016 g_hash_table_insert(relation, fileinfo, GINT_TO_POINTER(ret)); 3017 } 3018 } 3019 3020 for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) { 3021 gpointer data, old_key; 3022 3023 fileinfo = (MsgFileInfo *) file_cur->data; 3024 if (g_hash_table_lookup_extended(relation, fileinfo, &old_key, &data)) 3025 num = GPOINTER_TO_INT(data); 3026 else 3027 num = -1; 3028 3029 if (num >= 0) { 3030 MsgInfo *newmsginfo; 3031 3032 if (num == 0) { 3033 if (!folderscan) { 3034 folder_item_scan_full(dest, FALSE); 3035 folderscan = TRUE; 3036 } 3037 num = folder_item_get_msg_num_by_file(dest, fileinfo->file); 3038 debug_print("got num %d\n", num); 3039 } 3040 3041 if (num > lastnum) 3042 lastnum = num; 3043 3044 if (num >= 0 && remove_source) { 3045 if (unlink(fileinfo->file) < 0) 3046 FILE_OP_ERROR(fileinfo->file, "unlink"); 3047 } 3048 3049 if (num == 0) 3050 continue; 3051 3052 if (!folderscan && 3053 ((newmsginfo = get_msginfo(dest, num)) != NULL)) { 3054 add_msginfo_to_cache(dest, newmsginfo, NULL); 3055 procmsg_msginfo_free(&newmsginfo); 3056 } else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) { 3057 /* TODO: set default flags */ 3058 procmsg_msginfo_free(&newmsginfo); 3059 } 3060 } 3061 } 3062 3063 g_hash_table_destroy(relation); 3064 3065 return lastnum; 3066 } 3067 3068 static FolderItem *folder_item_move_recursive(FolderItem *src, FolderItem *dest, gboolean copy) 3069 { 3070 GSList *mlist; 3071 FolderItem *new_item; 3072 FolderItem *next_item; 3073 GNode *srcnode; 3074 gchar *old_id, *new_id; 3075 FolderUpdateData hookdata; 3076 3077 /* move messages */ 3078 debug_print("%s %s to %s\n", copy?"Copying":"Moving", src->path, dest->path); 3079 new_item = folder_create_folder(dest, src->name); 3080 if (new_item == NULL) { 3081 g_print("Can't create folder\n"); 3082 return NULL; 3083 } 3084 3085 if (new_item->folder == NULL) 3086 new_item->folder = dest->folder; 3087 3088 /* move messages */ 3089 log_message(LOG_PROTOCOL, copy ?_("Copying %s to %s...\n"):_("Moving %s to %s...\n"), 3090 src->name, new_item->path); 3091 3092 /*copy prefs*/ 3093 folder_item_prefs_copy_prefs(src, new_item); 3094 3095 /* copy internal data */ 3096 if (src->folder->klass == new_item->folder->klass && 3097 src->folder->klass->copy_private_data != NULL) 3098 src->folder->klass->copy_private_data(src->folder, 3099 src, new_item); 3100 new_item->collapsed = src->collapsed; 3101 new_item->thread_collapsed = src->thread_collapsed; 3102 new_item->threaded = src->threaded; 3103 new_item->hide_read_msgs = src->hide_read_msgs; 3104 new_item->hide_del_msgs = src->hide_del_msgs; 3105 new_item->hide_read_threads = src->hide_read_threads; 3106 new_item->sort_key = src->sort_key; 3107 new_item->sort_type = src->sort_type; 3108 3109 mlist = folder_item_get_msg_list(src); 3110 3111 if (mlist != NULL) { 3112 if (copy) 3113 folder_item_copy_msgs(new_item, mlist); 3114 else 3115 folder_item_move_msgs(new_item, mlist); 3116 procmsg_msg_list_free(mlist); 3117 } 3118 3119 /* recurse */ 3120 srcnode = src->folder->node; 3121 srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src); 3122 srcnode = srcnode->children; 3123 while (srcnode != NULL) { 3124 if (srcnode && srcnode->data) { 3125 next_item = (FolderItem*) srcnode->data; 3126 srcnode = srcnode->next; 3127 if (folder_item_move_recursive(next_item, new_item, copy) == NULL) { 3128 return NULL; 3129 } 3130 } 3131 } 3132 old_id = folder_item_get_identifier(src); 3133 new_id = folder_item_get_identifier(new_item); 3134 3135 hookdata.folder = src->folder; 3136 hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_MOVE_FOLDERITEM; 3137 hookdata.item = src; 3138 hookdata.item2 = new_item; 3139 hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata); 3140 3141 /* if src supports removing, otherwise only copy folder */ 3142 if (src->folder->klass->remove_folder != NULL && !copy) 3143 src->folder->klass->remove_folder(src->folder, src); 3144 folder_write_list(); 3145 3146 if (!copy) { 3147 debug_print("updating rules : %s => %s\n", old_id, new_id); 3148 if (old_id != NULL && new_id != NULL) { 3149 account_rename_path(old_id, new_id); 3150 } 3151 } 3152 g_free(old_id); 3153 g_free(new_id); 3154 3155 return new_item; 3156 } 3157 3158 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item, gboolean copy) 3159 { 3160 FolderItem *tmp = folder_item_parent(dest); 3161 gchar * src_identifier, * dst_identifier; 3162 gchar * phys_srcpath, * phys_dstpath, *tmppath, *tmpname; 3163 3164 while (tmp) { 3165 if (tmp == src) { 3166 return F_MOVE_FAILED_DEST_IS_CHILD; 3167 } 3168 tmp = folder_item_parent(tmp); 3169 } 3170 3171 /* both dst and src can be root folders */ 3172 src_identifier = folder_item_get_identifier(src); 3173 if (src_identifier == NULL && src->folder && folder_item_parent(src) == NULL) { 3174 src_identifier = folder_get_identifier(src->folder); 3175 } 3176 3177 dst_identifier = folder_item_get_identifier(dest); 3178 if(dst_identifier == NULL && dest->folder && folder_item_parent(dest) == NULL) { 3179 dst_identifier = folder_get_identifier(dest->folder); 3180 } 3181 3182 if (src_identifier == NULL || dst_identifier == NULL) { 3183 debug_print("Can't get identifiers\n"); 3184 if (src_identifier) 3185 g_free(src_identifier); 3186 if (dst_identifier) 3187 g_free(dst_identifier); 3188 return F_MOVE_FAILED; 3189 } 3190 3191 if (src->folder != dest->folder && !copy) { 3192 g_free(src_identifier); 3193 g_free(dst_identifier); 3194 return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX; 3195 } 3196 g_free(src_identifier); 3197 g_free(dst_identifier); 3198 3199 phys_srcpath = folder_item_get_path(src); 3200 tmppath = folder_item_get_path(dest); 3201 tmpname = g_path_get_basename(phys_srcpath); 3202 phys_dstpath = g_strconcat(tmppath, 3203 G_DIR_SEPARATOR_S, 3204 tmpname, 3205 NULL); 3206 g_free(tmpname); 3207 g_free(tmppath); 3208 3209 if (folder_item_parent(src) == dest || src == dest) { 3210 g_free(phys_srcpath); 3211 g_free(phys_dstpath); 3212 return F_MOVE_FAILED_DEST_IS_PARENT; 3213 } 3214 debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath); 3215 if ((tmp = folder_item_move_recursive(src, dest, copy)) == NULL) { 3216 g_free(phys_srcpath); 3217 g_free(phys_dstpath); 3218 return F_MOVE_FAILED; 3219 } 3220 3221 g_free(phys_srcpath); 3222 g_free(phys_dstpath); 3223 3224 *new_item = tmp; 3225 3226 return F_MOVE_OK; 3227 } 3228 3229 struct find_data 3230 { 3231 gboolean found; 3232 }; 3233 static void find_num(gpointer key, gpointer value, gpointer data) 3234 { 3235 struct find_data *fdata = (struct find_data *)data; 3236 if (GPOINTER_TO_INT(value) == 0) 3237 fdata->found = TRUE; 3238 } 3239 3240 static gboolean some_msgs_have_zero_num(GHashTable *hashtable) 3241 { 3242 struct find_data fdata; 3243 3244 fdata.found = FALSE; 3245 g_hash_table_foreach(hashtable, find_num, &fdata); 3246 3247 return fdata.found; 3248 } 3249 3250 /** 3251 * Copy a list of message to a new folder and remove 3252 * source messages if wanted 3253 */ 3254 static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_source) 3255 { 3256 Folder *folder; 3257 GSList *l; 3258 gint num, lastnum = -1; 3259 gboolean folderscan = FALSE; 3260 GHashTable *relation; 3261 GSList *not_moved = NULL; 3262 gint total = 0, curmsg = 0; 3263 MsgInfo *msginfo = NULL; 3264 3265 cm_return_val_if_fail(dest != NULL, -1); 3266 cm_return_val_if_fail(msglist != NULL, -1); 3267 3268 folder = dest->folder; 3269 3270 cm_return_val_if_fail(folder->klass->copy_msg != NULL, -1); 3271 if (dest->no_select) 3272 return -1; 3273 3274 msginfo = (MsgInfo *)msglist->data; 3275 3276 if (!msginfo) 3277 return -1; 3278 3279 if (!MSG_IS_QUEUED(msginfo->flags) && 3280 MSG_IS_DRAFT(msginfo->flags) && 3281 folder_has_parent_of_type(dest, F_QUEUE)) { 3282 GSList *cur = msglist; 3283 gboolean queue_err = FALSE; 3284 for (; cur; cur = cur->next) { 3285 Compose *compose = NULL; 3286 FolderItem *queue = dest; 3287 ComposeQueueResult val = COMPOSE_QUEUE_SUCCESS; 3288 3289 msginfo = (MsgInfo *)cur->data; 3290 compose = compose_reedit(msginfo, TRUE); 3291 if (compose == NULL) { 3292 queue_err = TRUE; 3293 continue; 3294 } 3295 val = compose_queue(compose, NULL, &queue, NULL, 3296 FALSE); 3297 if (val != COMPOSE_QUEUE_SUCCESS) { 3298 queue_err = TRUE; 3299 } else if (remove_source) { 3300 folder_item_remove_msg(msginfo->folder, msginfo->msgnum); 3301 } 3302 if (val == COMPOSE_QUEUE_SUCCESS) 3303 compose_close(compose); 3304 } 3305 return queue_err ? -1:0; 3306 } 3307 3308 relation = g_hash_table_new(g_direct_hash, g_direct_equal); 3309 3310 for (l = msglist ; l != NULL ; l = g_slist_next(l)) { 3311 MsgInfo * msginfo = (MsgInfo *) l->data; 3312 3313 if (msginfo->planned_download != 0) { 3314 int old_planned = msginfo->planned_download; 3315 /* little hack to reenable after */ 3316 msginfo->planned_download = old_planned; 3317 } 3318 } 3319 3320 /* 3321 * Copy messages to destination folder and 3322 * store new message numbers in newmsgnums 3323 */ 3324 if (folder->klass->copy_msgs != NULL) { 3325 if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) { 3326 g_hash_table_destroy(relation); 3327 return -1; 3328 } 3329 } else { 3330 MsgInfo * msginfo; 3331 l = msglist; 3332 3333 /* immediately stop if src and dest folders are identical */ 3334 if (l != NULL) { 3335 msginfo = (MsgInfo *) l->data; 3336 if (msginfo != NULL && msginfo->folder == dest) { 3337 g_hash_table_destroy(relation); 3338 return -1; 3339 } 3340 } 3341 3342 for (; l != NULL ; l = g_slist_next(l)) { 3343 msginfo = (MsgInfo *) l->data; 3344 3345 num = folder->klass->copy_msg(folder, dest, msginfo); 3346 if (num > 0) 3347 g_hash_table_insert(relation, msginfo, GINT_TO_POINTER(num)); 3348 else 3349 not_moved = g_slist_prepend(not_moved, msginfo); 3350 } 3351 } 3352 3353 if (remove_source) { 3354 MsgInfo *msginfo = (MsgInfo *) msglist->data; 3355 FolderItem *item = msginfo->folder; 3356 /* 3357 * Remove source messages from their folders if 3358 * copying was successfull and update folder 3359 * message counts 3360 */ 3361 if (not_moved == NULL && item->folder->klass->remove_msgs) { 3362 item->folder->klass->remove_msgs(item->folder, 3363 msginfo->folder, 3364 msglist, 3365 relation); 3366 } 3367 for (l = msglist; l != NULL; l = g_slist_next(l)) { 3368 gpointer old_key, data; 3369 msginfo = (MsgInfo *) l->data; 3370 item = msginfo->folder; 3371 3372 if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data)) 3373 num = GPOINTER_TO_INT(data); 3374 else 3375 num = 0; 3376 3377 if (g_slist_find(not_moved, msginfo)) 3378 continue; 3379 3380 if ((num >= 0) && (item->folder->klass->remove_msg != NULL)) { 3381 if (!item->folder->klass->remove_msgs) 3382 item->folder->klass->remove_msg(item->folder, 3383 msginfo->folder, 3384 msginfo->msgnum); 3385 remove_msginfo_from_cache(item, msginfo); 3386 } 3387 } 3388 } 3389 3390 /* Read cache for dest folder */ 3391 if (!dest->cache) folder_item_read_cache(dest); 3392 3393 /* 3394 * Fetch new MsgInfos for new messages in dest folder, 3395 * add them to the msgcache and update folder message counts 3396 */ 3397 if (some_msgs_have_zero_num(relation)) { 3398 folder_item_scan_full(dest, FALSE); 3399 folderscan = TRUE; 3400 } 3401 3402 statusbar_print_all(_("Updating cache for %s..."), dest->path ? dest->path : "(null)"); 3403 total = g_slist_length(msglist); 3404 3405 if (FOLDER_TYPE(dest->folder) == F_IMAP && total > 1) { 3406 folder_item_scan_full(dest, FALSE); 3407 folderscan = TRUE; 3408 } 3409 folder_item_set_batch(dest, TRUE); 3410 for (l = msglist; l != NULL; l = g_slist_next(l)) { 3411 MsgInfo *msginfo = (MsgInfo *) l->data; 3412 gpointer data, old_key; 3413 3414 if (!msginfo) 3415 continue; 3416 3417 if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data)) 3418 num = GPOINTER_TO_INT(data); 3419 else 3420 num = 0; 3421 3422 statusbar_progress_all(curmsg++,total, 100); 3423 if (curmsg % 100 == 0) 3424 GTK_EVENTS_FLUSH(); 3425 3426 if (num >= 0) { 3427 MsgInfo *newmsginfo = NULL; 3428 3429 if (!folderscan && num > 0) { 3430 newmsginfo = get_msginfo(dest, num); 3431 if (newmsginfo != NULL) { 3432 add_msginfo_to_cache(dest, newmsginfo, msginfo); 3433 } 3434 } 3435 if (newmsginfo == NULL) { 3436 if (!folderscan) { 3437 folder_item_scan_full(dest, FALSE); 3438 folderscan = TRUE; 3439 } 3440 if (msginfo->msgid != NULL) { 3441 newmsginfo = folder_item_get_msginfo_by_msgid(dest, msginfo->msgid); 3442 if (newmsginfo != NULL) { 3443 copy_msginfo_flags(msginfo, newmsginfo); 3444 num = newmsginfo->msgnum; 3445 } 3446 } 3447 } 3448 3449 if (!MSG_IS_POSTFILTERED (msginfo->flags)) { 3450 procmsg_msginfo_set_flags ( msginfo, MSG_POSTFILTERED, 0); 3451 if (newmsginfo) { 3452 procmsg_msginfo_set_flags (newmsginfo, MSG_POSTFILTERED, 0); 3453 hooks_invoke (MAIL_POSTFILTERING_HOOKLIST, newmsginfo); 3454 } 3455 } 3456 procmsg_msginfo_free(&newmsginfo); 3457 3458 if (num > lastnum) 3459 lastnum = num; 3460 } 3461 } 3462 folder_item_set_batch(dest, FALSE); 3463 statusbar_progress_all(0,0,0); 3464 statusbar_pop_all(); 3465 3466 g_hash_table_destroy(relation); 3467 if (not_moved != NULL) { 3468 g_slist_free(not_moved); 3469 return -1; 3470 } else 3471 return lastnum; 3472 } 3473 3474 /** 3475 * Move a message to a new folder. 3476 * 3477 * \param dest Destination folder 3478 * \param msginfo The message 3479 */ 3480 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo) 3481 { 3482 GSList list; 3483 3484 cm_return_val_if_fail(dest != NULL, -1); 3485 cm_return_val_if_fail(msginfo != NULL, -1); 3486 3487 list.data = msginfo; 3488 list.next = NULL; 3489 3490 return do_copy_msgs(dest, &list, TRUE); 3491 } 3492 3493 /** 3494 * Move a list of messages to a new folder. 3495 * 3496 * \param dest Destination folder 3497 * \param msglist List of messages 3498 */ 3499 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist) 3500 { 3501 gint result = -1; 3502 cm_return_val_if_fail(dest != NULL, -1); 3503 cm_return_val_if_fail(msglist != NULL, -1); 3504 inc_lock(); 3505 result = do_copy_msgs(dest, msglist, TRUE); 3506 inc_unlock(); 3507 return result; 3508 } 3509 3510 /** 3511 * Copy a message to a new folder. 3512 * 3513 * \param dest Destination folder 3514 * \param msginfo The message 3515 */ 3516 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo) 3517 { 3518 GSList list; 3519 3520 cm_return_val_if_fail(dest != NULL, -1); 3521 cm_return_val_if_fail(msginfo != NULL, -1); 3522 3523 list.data = msginfo; 3524 list.next = NULL; 3525 3526 return do_copy_msgs(dest, &list, FALSE); 3527 } 3528 3529 /** 3530 * Copy a list of messages to a new folder. 3531 * 3532 * \param dest Destination folder 3533 * \param msglist List of messages 3534 */ 3535 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist) 3536 { 3537 gint result; 3538 cm_return_val_if_fail(dest != NULL, -1); 3539 cm_return_val_if_fail(msglist != NULL, -1); 3540 3541 inc_lock(); 3542 result = do_copy_msgs(dest, msglist, FALSE); 3543 inc_unlock(); 3544 3545 return result; 3546 } 3547 3548 gint folder_item_remove_msg(FolderItem *item, gint num) 3549 { 3550 Folder *folder; 3551 gint ret; 3552 MsgInfo *msginfo; 3553 3554 cm_return_val_if_fail(item != NULL, -1); 3555 folder = item->folder; 3556 cm_return_val_if_fail(folder->klass->remove_msg != NULL, -1); 3557 if (item->no_select) 3558 return -1; 3559 3560 if (!item->cache) folder_item_read_cache(item); 3561 3562 msginfo = msgcache_get_msg(item->cache, num); 3563 if (msginfo && MSG_IS_LOCKED(msginfo->flags)) { 3564 procmsg_msginfo_free(&msginfo); 3565 return -1; 3566 } 3567 ret = folder->klass->remove_msg(folder, item, num); 3568 3569 if (msginfo != NULL) { 3570 if (ret == 0) 3571 remove_msginfo_from_cache(item, msginfo); 3572 procmsg_msginfo_free(&msginfo); 3573 } 3574 3575 return ret; 3576 } 3577 3578 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist) 3579 { 3580 Folder *folder; 3581 gint ret = 0; 3582 GSList *real_list = NULL, *cur = NULL; 3583 3584 cm_return_val_if_fail(item != NULL, -1); 3585 folder = item->folder; 3586 cm_return_val_if_fail(folder != NULL, -1); 3587 if (item->no_select) 3588 return -1; 3589 inc_lock(); 3590 if (!item->cache) folder_item_read_cache(item); 3591 3592 folder_item_update_freeze(); 3593 3594 /* filter out locked mails */ 3595 for (cur = msglist; cur; cur = cur->next) { 3596 MsgInfo *info = (MsgInfo *)cur->data; 3597 if (!MSG_IS_LOCKED(info->flags)) 3598 real_list = g_slist_prepend(real_list, info); 3599 } 3600 3601 real_list = g_slist_reverse(real_list); 3602 3603 if (item->folder->klass->remove_msgs) { 3604 ret = item->folder->klass->remove_msgs(item->folder, 3605 item, 3606 real_list, 3607 NULL); 3608 } 3609 cur = real_list; 3610 while (ret == 0 && cur != NULL) { 3611 MsgInfo *msginfo = (MsgInfo *)cur->data; 3612 if (msginfo && MSG_IS_LOCKED(msginfo->flags)) { 3613 cur = cur->next; 3614 continue; 3615 } 3616 if (!item->folder->klass->remove_msgs) 3617 ret = folder_item_remove_msg(item, msginfo->msgnum); 3618 if (ret != 0) break; 3619 msgcache_remove_msg(item->cache, msginfo->msgnum); 3620 cur = cur->next; 3621 } 3622 g_slist_free(real_list); 3623 folder_item_scan_full(item, FALSE); 3624 folder_item_update_thaw(); 3625 inc_unlock(); 3626 return ret; 3627 } 3628 3629 gint folder_item_expunge(FolderItem *item) 3630 { 3631 Folder *folder = item->folder; 3632 gint result = 0; 3633 if (folder == NULL) 3634 return -1; 3635 if (folder->klass->expunge) { 3636 GSList *msglist = folder_item_get_msg_list(item); 3637 GSList *cur; 3638 result = folder->klass->expunge(folder, item); 3639 if (result == 0) { 3640 for (cur = msglist; cur; cur = cur->next) { 3641 MsgInfo *msginfo = (MsgInfo *)cur->data; 3642 if (MSG_IS_DELETED(msginfo->flags)) { 3643 remove_msginfo_from_cache(item, msginfo); 3644 } 3645 } 3646 } 3647 procmsg_msg_list_free(msglist); 3648 } 3649 return result; 3650 } 3651 3652 gint folder_item_remove_all_msg(FolderItem *item) 3653 { 3654 Folder *folder; 3655 gint result; 3656 3657 cm_return_val_if_fail(item != NULL, -1); 3658 if (item->no_select) 3659 return -1; 3660 3661 folder = item->folder; 3662 3663 inc_lock(); 3664 if (folder->klass->remove_all_msg != NULL) { 3665 result = folder->klass->remove_all_msg(folder, item); 3666 3667 if (result == 0) { 3668 folder_item_free_cache(item, TRUE); 3669 item->cache = msgcache_new(); 3670 item->cache_dirty = TRUE; 3671 item->mark_dirty = TRUE; 3672 item->tags_dirty = TRUE; 3673 } 3674 } else { 3675 MsgInfoList *msglist; 3676 3677 msglist = folder_item_get_msg_list(item); 3678 result = folder_item_remove_msgs(item, msglist); 3679 procmsg_msg_list_free(msglist); 3680 } 3681 3682 if (result == 0) { 3683 item->new_msgs = 0; 3684 item->unread_msgs = 0; 3685 item->unreadmarked_msgs = 0; 3686 item->marked_msgs = 0; 3687 item->total_msgs = 0; 3688 item->replied_msgs = 0; 3689 item->forwarded_msgs = 0; 3690 item->locked_msgs = 0; 3691 item->ignored_msgs = 0; 3692 item->watched_msgs = 0; 3693 folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT); 3694 } 3695 3696 inc_unlock(); 3697 return result; 3698 } 3699 3700 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags) 3701 { 3702 cm_return_if_fail(item != NULL); 3703 cm_return_if_fail(msginfo != NULL); 3704 3705 item->mark_dirty = TRUE; 3706 3707 if (item->no_select) 3708 return; 3709 3710 if (item->folder->klass->change_flags != NULL && item->scanning != ITEM_SCANNING_WITH_FLAGS) { 3711 item->folder->klass->change_flags(item->folder, item, msginfo, newflags); 3712 } else { 3713 msginfo->flags.perm_flags = newflags; 3714 } 3715 } 3716 3717 void folder_item_commit_tags(FolderItem *item, MsgInfo *msginfo, GSList *tags_set, GSList *tags_unset) 3718 { 3719 Folder *folder = NULL; 3720 3721 if (!msginfo) 3722 return; 3723 if (!item) 3724 return; 3725 if (!tags_set && !tags_unset) 3726 return; 3727 3728 folder = item->folder; 3729 if (!folder) 3730 return; 3731 3732 item->tags_dirty = TRUE; 3733 3734 if (folder->klass->commit_tags == NULL) 3735 return; 3736 3737 folder->klass->commit_tags(item, msginfo, tags_set, tags_unset); 3738 } 3739 3740 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo) 3741 { 3742 Folder *folder; 3743 3744 cm_return_val_if_fail(item != NULL, FALSE); 3745 if (item->no_select) 3746 return FALSE; 3747 3748 folder = item->folder; 3749 3750 cm_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1); 3751 3752 return folder->klass->is_msg_changed(folder, item, msginfo); 3753 } 3754 3755 void folder_item_discard_cache(FolderItem *item) 3756 { 3757 gchar *dir; 3758 gchar *cache; 3759 3760 if (!item) 3761 return; 3762 3763 if (item->cache) { 3764 msgcache_destroy(item->cache); 3765 item->cache = NULL; 3766 } 3767 dir = folder_item_get_path(item); 3768 if (is_dir_exist(dir)) 3769 remove_all_numbered_files(dir); 3770 g_free(dir); 3771 3772 cache = folder_item_get_cache_file(item); 3773 unlink(cache); 3774 g_free(cache); 3775 3776 } 3777 3778 static char *folder_item_get_cache_file(FolderItem *item) 3779 { 3780 if (!item) 3781 return NULL; 3782 3783 char *path = folder_item_get_path(item); 3784 if (!path) 3785 return NULL; 3786 if (!is_dir_exist(path)) 3787 make_dir_hier(path); 3788 3789 char file[PATH_MAX]; 3790 strlcpy(file, path, sizeof(file)); 3791 strlcat(file, "/.claws_cache", sizeof(file)); 3792 free(path); 3793 return strdup(file); 3794 } 3795 3796 static gchar *folder_item_get_mark_file(FolderItem *item) 3797 { 3798 gchar *path; 3799 gchar *file; 3800 3801 cm_return_val_if_fail(item != NULL, NULL); 3802 cm_return_val_if_fail(item->path != NULL, NULL); 3803 3804 path = folder_item_get_path(item); 3805 cm_return_val_if_fail(path != NULL, NULL); 3806 if (!is_dir_exist(path)) 3807 make_dir_hier(path); 3808 file = g_strconcat(path, "/.claws_mark", NULL); 3809 g_free(path); 3810 3811 return file; 3812 } 3813 3814 const char *tags_dir = ".claws_tags"; 3815 3816 static gchar *folder_item_get_tags_file(FolderItem *item) 3817 { 3818 gchar *path; 3819 gchar *identifier; 3820 gchar *file; 3821 3822 /* we save tags files in rc_dir, because tagsrc is there too, 3823 * and storing tags directly in the mailboxes would give strange 3824 * result when using another Claws mailbox from another install 3825 * with different tags. */ 3826 3827 cm_return_val_if_fail(item != NULL, NULL); 3828 3829 identifier = folder_item_get_identifier(item); 3830 cm_return_val_if_fail(identifier != NULL, NULL); 3831 3832 path = g_strconcat(get_rc_dir(), "/tagsdb/", identifier, NULL); 3833 g_free(identifier); 3834 if (!is_dir_exist(path)) 3835 make_dir_hier(path); 3836 file = g_strconcat(path, "/.claws_tags", NULL); 3837 3838 g_free(path); 3839 3840 return file; 3841 } 3842 3843 static gpointer xml_to_folder_item(gpointer nodedata, gpointer data) 3844 { 3845 XMLNode *xmlnode = (XMLNode *) nodedata; 3846 Folder *folder = (Folder *) data; 3847 FolderItem *item; 3848 3849 cm_return_val_if_fail(xmlnode != NULL, NULL); 3850 cm_return_val_if_fail(folder != NULL, NULL); 3851 3852 if (g_strcmp0(xmlnode->tag->tag, "folderitem") != 0) { 3853 g_warning("tag name != \"folderitem\""); 3854 return NULL; 3855 } 3856 3857 item = folder_item_new(folder, "", ""); 3858 g_node_destroy(item->node); 3859 if (folder->klass->item_set_xml != NULL) 3860 folder->klass->item_set_xml(folder, item, xmlnode->tag); 3861 else 3862 folder_item_set_xml(folder, item, xmlnode->tag); 3863 3864 item->folder = folder; 3865 3866 switch (item->stype) { 3867 case F_INBOX: folder->inbox = item; break; 3868 case F_OUTBOX: folder->outbox = item; break; 3869 case F_DRAFT: folder->draft = item; break; 3870 case F_QUEUE: folder->queue = item; break; 3871 case F_TRASH: folder->trash = item; break; 3872 default: break; 3873 } 3874 folder_item_prefs_read_config(item); 3875 3876 return item; 3877 } 3878 3879 static gboolean folder_item_set_node(GNode *node, gpointer data) 3880 { 3881 cm_return_val_if_fail(node->data != NULL, -1); 3882 3883 FolderItem *item = (FolderItem *) node->data; 3884 item->node = node; 3885 3886 return FALSE; 3887 } 3888 3889 static Folder *folder_get_from_xml(GNode *node) 3890 { 3891 Folder *folder; 3892 XMLNode *xmlnode; 3893 GList *list; 3894 FolderClass *klass = NULL; 3895 GNode *cur; 3896 3897 cm_return_val_if_fail(node->data != NULL, NULL); 3898 3899 xmlnode = node->data; 3900 if (g_strcmp0(xmlnode->tag->tag, "folder") != 0) { 3901 g_warning("tag name != \"folder\""); 3902 return NULL; 3903 } 3904 list = xmlnode->tag->attr; 3905 for (; list != NULL; list = list->next) { 3906 XMLAttr *attr = list->data; 3907 3908 if (!attr || !attr->name || !attr->value) continue; 3909 if (!strcmp(attr->name, "type")) 3910 klass = folder_get_class_from_string(attr->value); 3911 } 3912 if (klass == NULL) 3913 return NULL; 3914 3915 folder = folder_new(klass, "", ""); 3916 cm_return_val_if_fail(folder != NULL, NULL); 3917 3918 if (klass->set_xml) 3919 klass->set_xml(folder, xmlnode->tag); 3920 else 3921 folder_set_xml(folder, xmlnode->tag); 3922 3923 cur = node->children; 3924 while (cur != NULL) { 3925 GNode *itemnode; 3926 3927 itemnode = g_node_map(cur, xml_to_folder_item, (gpointer) folder); 3928 g_node_append(folder->node, itemnode); 3929 cur = cur->next; 3930 } 3931 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, folder_item_set_node, NULL); 3932 3933 return folder; 3934 } 3935 3936 gchar *folder_get_list_path(void) 3937 { 3938 static gchar *filename = NULL; 3939 3940 if (!filename) 3941 filename = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, 3942 FOLDER_LIST, NULL); 3943 3944 return filename; 3945 } 3946 3947 static gpointer folder_item_to_xml(gpointer nodedata, gpointer data) 3948 { 3949 FolderItem *item = (FolderItem *) nodedata; 3950 XMLTag *tag; 3951 3952 cm_return_val_if_fail(item != NULL, NULL); 3953 3954 if (item->folder->klass->item_get_xml != NULL) 3955 tag = item->folder->klass->item_get_xml(item->folder, item); 3956 else 3957 tag = folder_item_get_xml(item->folder, item); 3958 3959 return xml_node_new(tag, NULL); 3960 } 3961 3962 static GNode *folder_get_xml_node(Folder *folder) 3963 { 3964 GNode *node; 3965 XMLNode *xmlnode; 3966 XMLTag *tag; 3967 3968 cm_return_val_if_fail(folder != NULL, NULL); 3969 3970 if (folder->klass->get_xml != NULL) 3971 tag = folder->klass->get_xml(folder); 3972 else 3973 tag = folder_get_xml(folder); 3974 3975 xml_tag_add_attr(tag, xml_attr_new("type", folder->klass->idstr)); 3976 3977 xmlnode = xml_node_new(tag, NULL); 3978 3979 node = g_node_new(xmlnode); 3980 3981 cm_return_val_if_fail (folder->node != NULL, NULL); 3982 3983 if (folder->node->children) { 3984 GNode *cur; 3985 3986 cur = folder->node->children; 3987 while (cur) { 3988 GNode *xmlnode; 3989 3990 xmlnode = g_node_map(cur, folder_item_to_xml, (gpointer) folder); 3991 g_node_append(node, xmlnode); 3992 cur = cur->next; 3993 } 3994 } 3995 3996 return node; 3997 } 3998 3999 static void folder_update_op_count_rec(GNode *node) 4000 { 4001 FolderItem *fitem = FOLDER_ITEM(node->data); 4002 4003 if (g_node_depth(node) > 0) { 4004 if (fitem->op_count > 0) { 4005 fitem->op_count = 0; 4006 folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT); 4007 } 4008 if (node->children) { 4009 GNode *child; 4010 4011 child = node->children; 4012 while (child) { 4013 GNode *cur; 4014 4015 cur = child; 4016 child = cur->next; 4017 folder_update_op_count_rec(cur); 4018 } 4019 } 4020 } 4021 } 4022 4023 void folder_update_op_count(void) 4024 { 4025 GList *cur; 4026 Folder *folder; 4027 4028 for (cur = folder_list; cur != NULL; cur = cur->next) { 4029 folder = cur->data; 4030 folder_update_op_count_rec(folder->node); 4031 } 4032 } 4033 4034 typedef struct _type_str { 4035 gchar * str; 4036 gint type; 4037 } type_str; 4038 4039 4040 /* 4041 static gchar * folder_item_get_tree_identifier(FolderItem * item) 4042 { 4043 if (item->parent != NULL) { 4044 gchar * path; 4045 gchar * id; 4046 4047 path = folder_item_get_tree_identifier(item->parent); 4048 if (path == NULL) 4049 return NULL; 4050 4051 id = g_strconcat(path, "/", item->name, NULL); 4052 g_free(path); 4053 4054 return id; 4055 } 4056 else { 4057 return g_strconcat("/", item->name, NULL); 4058 } 4059 } 4060 */ 4061 4062 /* CLAWS: temporary local folder for filtering */ 4063 #define TEMP_FOLDER "TEMP_FOLDER" 4064 #define PROCESSING_FOLDER_ITEM "processing" 4065 4066 static FolderItem *folder_create_processing_folder(int account_id) 4067 { 4068 static Folder *processing_folder = NULL; 4069 FolderItem *processing_folder_item; 4070 4071 gchar *processing_folder_item_name = NULL; 4072 4073 processing_folder_item_name = g_strdup_printf("%s-%d", PROCESSING_FOLDER_ITEM, account_id); 4074 4075 if (processing_folder == NULL) { 4076 gchar *tmppath = 4077 g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, 4078 "tempfolder", NULL); 4079 processing_folder = 4080 folder_new(mh_get_class(), TEMP_FOLDER, tmppath); 4081 g_free(tmppath); 4082 tmppath = NULL; 4083 4084 g_assert(processing_folder != NULL); 4085 processing_folder->klass->scan_tree(processing_folder); 4086 } 4087 g_assert(processing_folder != NULL); 4088 4089 processing_folder_item = folder_find_child_item_by_name(FOLDER_ITEM(processing_folder->node->data), 4090 processing_folder_item_name); 4091 if (processing_folder_item) { 4092 debug_print("*TMP* already created %s\n", folder_item_get_path(processing_folder_item)); 4093 } else { 4094 processing_folder_item = processing_folder->klass->create_folder(processing_folder, 4095 processing_folder->node->data, 4096 processing_folder_item_name); 4097 folder_item_append(FOLDER_ITEM(processing_folder->node->data), processing_folder_item); 4098 debug_print("*TMP* creating %s\n", folder_item_get_path(processing_folder_item)); 4099 } 4100 g_free(processing_folder_item_name); 4101 g_assert(processing_folder_item != NULL); 4102 4103 return(processing_folder_item); 4104 } 4105 4106 FolderItem *folder_get_default_processing(int account_id) 4107 { 4108 return folder_create_processing_folder(account_id); 4109 } 4110 4111 /* 4112 * functions for handling FolderItem content changes 4113 */ 4114 static gint folder_item_update_freeze_cnt = 0; 4115 4116 static void folder_item_update_with_msg(FolderItem *item, FolderItemUpdateFlags update_flags, MsgInfo *msg) 4117 { 4118 if (folder_item_update_freeze_cnt == 0 /* || (msg != NULL && item->opened) */) { 4119 FolderItemUpdateData source; 4120 4121 source.item = item; 4122 source.update_flags = update_flags; 4123 source.msg = msg; 4124 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source); 4125 } else { 4126 item->update_flags |= update_flags & ~(F_ITEM_UPDATE_ADDMSG | F_ITEM_UPDATE_REMOVEMSG); 4127 } 4128 } 4129 4130 /** 4131 * Notify the folder system about changes to a folder. If the 4132 * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will 4133 * be invoked, otherwise the changes will be remebered until 4134 * the folder system is thawed. 4135 * 4136 * \param item The FolderItem that was changed 4137 * \param update_flags Type of changed that was made 4138 */ 4139 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags) 4140 { 4141 folder_item_update_with_msg(item, update_flags, NULL); 4142 } 4143 4144 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags) 4145 { 4146 GNode *node = item->folder->node; 4147 4148 node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item); 4149 node = node->children; 4150 4151 folder_item_update(item, update_flags); 4152 while (node != NULL) { 4153 if (node && node->data) { 4154 FolderItem *next_item = (FolderItem*) node->data; 4155 4156 folder_item_update(next_item, update_flags); 4157 } 4158 node = node->next; 4159 } 4160 } 4161 4162 void folder_item_update_freeze(void) 4163 { 4164 folder_item_update_freeze_cnt++; 4165 } 4166 4167 static void folder_item_update_func(FolderItem *item, gpointer data) 4168 { 4169 FolderItemUpdateData source; 4170 4171 if (item->update_flags) { 4172 source.item = item; 4173 source.update_flags = item->update_flags; 4174 source.msg = NULL; 4175 hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source); 4176 item->update_flags = 0; 4177 } 4178 } 4179 4180 void folder_item_update_thaw(void) 4181 { 4182 if (folder_item_update_freeze_cnt > 0) 4183 folder_item_update_freeze_cnt--; 4184 if (folder_item_update_freeze_cnt == 0) { 4185 /* Update all folders */ 4186 folder_func_to_all_folders(folder_item_update_func, NULL); 4187 } 4188 } 4189 4190 void folder_item_synchronise(FolderItem *item) 4191 { 4192 if (!item) 4193 return; 4194 if (item->prefs->offlinesync && item->folder->klass->synchronise) { 4195 statusbar_print_all(_("Synchronising %s for offline use...\n"), item->path ? item->path : "(null)"); 4196 item->folder->klass->synchronise(item, 4197 item->prefs->offlinesync_days); 4198 if (item->prefs->offlinesync_days > 0 && 4199 item->prefs->remove_old_bodies) 4200 folder_item_clean_local_files(item, item->prefs->offlinesync_days); 4201 statusbar_pop_all(); 4202 } 4203 } 4204 4205 static void folder_item_synchronise_func(FolderItem *item, gpointer data) 4206 { 4207 Folder *folder = (Folder *)data; 4208 if (folder == NULL || item->folder == folder) { 4209 folder_item_synchronise(item); 4210 } 4211 } 4212 4213 void folder_synchronise(Folder *folder) 4214 { 4215 folder_func_to_all_folders(folder_item_synchronise_func, folder); 4216 } 4217 4218 typedef struct _WantSyncData { 4219 Folder *folder; 4220 gboolean want_sync; 4221 } WantSyncData; 4222 4223 static void folder_item_want_synchronise_func(FolderItem *item, gpointer data) 4224 { 4225 WantSyncData *want_sync_data = (WantSyncData *)data; 4226 4227 if (want_sync_data->folder == NULL || item->folder == want_sync_data->folder) { 4228 if (item->prefs->offlinesync && item->folder->klass->synchronise) 4229 want_sync_data->want_sync |= TRUE; 4230 } 4231 } 4232 4233 gboolean folder_want_synchronise(Folder *folder) 4234 { 4235 WantSyncData *want_sync_data = g_new0(WantSyncData, 1); 4236 gboolean result; 4237 want_sync_data->folder = folder; 4238 want_sync_data->want_sync = FALSE; 4239 4240 folder_func_to_all_folders(folder_item_want_synchronise_func, want_sync_data); 4241 result = want_sync_data->want_sync; 4242 g_free(want_sync_data); 4243 if (result > 0) 4244 debug_print("Folder %s wants sync\n", folder->name); 4245 return result; 4246 } 4247 4248 void folder_item_set_batch (FolderItem *item, gboolean batch) 4249 { 4250 if (!item || !item->folder) 4251 return; 4252 if (item->folder->klass->set_batch) { 4253 item->folder->klass->set_batch(item->folder, item, batch); 4254 } 4255 } 4256 4257 gboolean folder_has_parent_of_type(FolderItem *item, 4258 SpecialFolderItemType type) 4259 { 4260 FolderItem *cur = item; 4261 4262 if (!item) 4263 return FALSE; 4264 /* if we already know it, make it short */ 4265 if (item->parent_stype != -1) { 4266 return (item->parent_stype == type); 4267 } 4268 4269 /* if we don't, find the type from the first possible parent, 4270 * and set our parent type to be faster next time */ 4271 while (cur) { 4272 if (cur->stype == type || cur->parent_stype == type) { 4273 item->parent_stype = type; 4274 return TRUE; 4275 } 4276 cur = folder_item_parent(cur); 4277 } 4278 4279 /* if we didn't match what was asked, we didn't return. If our 4280 * parent type is unknown, we may as well find it now to be faster 4281 * later. */ 4282 if (item->parent_stype == -1) { 4283 cur = item; 4284 while (cur) { 4285 /* here's an exception: Inbox subfolders are normal. */ 4286 if (item->parent_stype == -1 && cur->stype == F_INBOX 4287 && item != cur) { 4288 item->parent_stype = F_NORMAL; 4289 break; 4290 } 4291 /* ah, we know this parent's parent's type, we may as 4292 * well copy it instead of going up the full way */ 4293 if (cur->parent_stype != -1) { 4294 item->parent_stype = cur->parent_stype; 4295 break; 4296 } 4297 /* we found a parent that has a special type. That's 4298 * our parent type. */ 4299 if (cur->stype != F_NORMAL) { 4300 cur->parent_stype = cur->stype; 4301 item->parent_stype = cur->stype; 4302 break; 4303 } 4304 /* if we didn't find anything, go up once more */ 4305 cur = folder_item_parent(cur); 4306 } 4307 /* as we still didn't find anything, our parents must all be 4308 * normal. */ 4309 if (item->parent_stype == -1) { 4310 item->parent_stype = F_NORMAL; 4311 } 4312 } 4313 return FALSE; 4314 } 4315 4316 gboolean folder_is_child_of(FolderItem *item, FolderItem *parent) 4317 { 4318 if (item == NULL || parent == NULL) 4319 return FALSE; 4320 4321 while (item != NULL) { 4322 if (parent == item) 4323 return TRUE; 4324 4325 item = folder_item_parent(item); 4326 } 4327 4328 return FALSE; 4329 } 4330 4331 4332 gboolean folder_subscribe (const gchar *uri) 4333 { 4334 GList *cur; 4335 for (cur = folder_get_list(); cur != NULL; cur = g_list_next(cur)) { 4336 Folder *folder = (Folder *) cur->data; 4337 4338 if (folder->klass->subscribe 4339 && folder->klass->subscribe(folder, uri)) { 4340 return TRUE; 4341 } 4342 } 4343 return FALSE; 4344 4345 } 4346 4347 gboolean folder_get_sort_type (Folder *folder, 4348 FolderSortKey *sort_key, 4349 FolderSortType *sort_type) 4350 { 4351 if (!folder || !sort_key || !sort_type) 4352 return FALSE; 4353 if (folder->klass->get_sort_type == NULL) 4354 return FALSE; 4355 folder->klass->get_sort_type(folder, sort_key, sort_type); 4356 return TRUE; 4357 } 4358 4359 MsgNumberList *folder_item_get_number_list(FolderItem *item) 4360 { 4361 GSList *nums = NULL; 4362 GSList *msglist = folder_item_get_msg_list(item); 4363 4364 nums = procmsg_get_number_list_for_msgs(msglist); 4365 procmsg_msg_list_free(msglist); 4366 4367 return nums; 4368 } 4369 4370 /* Tests if a local (on disk) folder name is acceptable. */ 4371 gboolean folder_local_name_ok(const gchar *name) 4372 { 4373 return TRUE; 4374 }