imap.c (147130B)
1 /* 2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client 3 * Copyright (C) 1999-2024 the Claws Mail team and Hiroyuki Yamamoto 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 */ 18 19 #include "defs.h" 20 21 #include <err.h> 22 #include <glib.h> 23 #include <glib/gi18n.h> 24 #include <stdio.h> 25 #include <string.h> 26 #include "imap.h" 27 #include "imap_gtk.h" 28 #include "inc.h" 29 #include "xml.h" 30 #include "alertpanel.h" 31 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <ctype.h> 35 #include <time.h> 36 #include <errno.h> 37 #include <iconv.h> 38 39 #include "ssl.h" 40 41 #include "folder.h" 42 #include "session.h" 43 #include "procmsg.h" 44 #include "socket.h" 45 #include "procheader.h" 46 #include "prefs_account.h" 47 #include "codeconv.h" 48 #include "utils.h" 49 #include "prefs_common.h" 50 #include "inputdialog.h" 51 #include "log.h" 52 #include "remotefolder.h" 53 #include "claws.h" 54 #include "statusbar.h" 55 #include "msgcache.h" 56 #include "etpan/imap-thread.h" 57 #include "account.h" 58 #include "tags.h" 59 #include "main.h" 60 #include "passwordstore.h" 61 #include "file-utils.h" 62 63 typedef struct _IMAPFolder IMAPFolder; 64 typedef struct _IMAPSession IMAPSession; 65 typedef struct _IMAPNameSpace IMAPNameSpace; 66 typedef struct _IMAPFolderItem IMAPFolderItem; 67 68 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj) 69 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj) 70 #define IMAP_SESSION(obj) ((IMAPSession *)obj) 71 72 struct _IMAPFolder 73 { 74 RemoteFolder rfolder; 75 76 /* list of IMAPNameSpace */ 77 GList *ns_personal; 78 GList *ns_others; 79 GList *ns_shared; 80 gchar last_seen_separator; 81 guint refcnt; 82 guint max_set_size; 83 gchar *search_charset; 84 gboolean search_charset_supported; 85 }; 86 87 struct _IMAPSession 88 { 89 Session session; 90 91 gboolean authenticated; 92 93 GSList *capability; 94 gboolean uidplus; 95 96 gchar *mbox; 97 guint cmd_count; 98 99 /* CLAWS */ 100 gboolean folder_content_changed; 101 guint exists; 102 guint recent; 103 guint expunge; 104 guint unseen; 105 guint uid_validity; 106 guint uid_next; 107 108 Folder * folder; 109 gboolean busy; 110 gboolean cancelled; 111 gboolean sens_update_block; 112 gboolean do_destroy; 113 114 gint scan_tree_recurs_depth; 115 }; 116 117 struct _IMAPNameSpace 118 { 119 gchar *name; 120 gchar separator; 121 }; 122 123 #define IMAPBUFSIZE 8192 124 125 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0) 126 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0) 127 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0) 128 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0) 129 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0) 130 #define IMAP_IS_FORWARDED(flags) ((flags & IMAP_FLAG_FORWARDED) != 0) 131 #define IMAP_IS_SPAM(flags) ((flags & IMAP_FLAG_SPAM) != 0) 132 #define IMAP_IS_HAM(flags) ((flags & IMAP_FLAG_HAM) != 0) 133 134 135 #define IMAP4_PORT 143 136 #define IMAPS_PORT 993 137 138 #define IMAP_CMD_LIMIT 1000 139 140 enum { 141 ITEM_CAN_CREATE_FLAGS_UNKNOWN = 0, 142 ITEM_CAN_CREATE_FLAGS, 143 ITEM_CANNOT_CREATE_FLAGS 144 }; 145 146 struct _IMAPFolderItem 147 { 148 FolderItem item; 149 150 guint lastuid; 151 guint uid_next; 152 GSList *uid_list; 153 gboolean batching; 154 155 GHashTable *flags_set_table; 156 GHashTable *flags_unset_table; 157 time_t last_change; 158 time_t last_sync; 159 gboolean should_update; 160 gboolean should_trash_cache; 161 gint can_create_flags; 162 163 GSList *ok_flags; 164 165 }; 166 167 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item); 168 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag); 169 170 static Folder *imap_folder_new (const gchar *name, 171 const gchar *path); 172 static void imap_folder_destroy (Folder *folder); 173 174 static IMAPSession *imap_session_new (Folder *folder, 175 const PrefsAccount *account); 176 static gint imap_session_authenticate(IMAPSession *session, 177 PrefsAccount *account); 178 static void imap_session_destroy (Session *session); 179 180 static gchar *imap_fetch_msg (Folder *folder, 181 FolderItem *item, 182 gint uid); 183 static gchar *imap_fetch_msg_full (Folder *folder, 184 FolderItem *item, 185 gint uid, 186 gboolean headers, 187 gboolean body); 188 static void imap_remove_cached_msg (Folder *folder, 189 FolderItem *item, 190 MsgInfo *msginfo); 191 static gint imap_add_msg (Folder *folder, 192 FolderItem *dest, 193 const gchar *file, 194 MsgFlags *flags); 195 static gint imap_add_msgs (Folder *folder, 196 FolderItem *dest, 197 GSList *file_list, 198 GHashTable *relation); 199 200 static gint imap_copy_msg (Folder *folder, 201 FolderItem *dest, 202 MsgInfo *msginfo); 203 static gint imap_copy_msgs (Folder *folder, 204 FolderItem *dest, 205 MsgInfoList *msglist, 206 GHashTable *relation); 207 208 static gint imap_remove_msg (Folder *folder, 209 FolderItem *item, 210 gint uid); 211 static gint imap_remove_msgs (Folder *folder, 212 FolderItem *dest, 213 MsgInfoList *msglist, 214 GHashTable *relation); 215 static gint imap_expunge (Folder *folder, 216 FolderItem *dest); 217 static gint imap_remove_all_msg (Folder *folder, 218 FolderItem *item); 219 220 static gboolean imap_is_msg_changed (Folder *folder, 221 FolderItem *item, 222 MsgInfo *msginfo); 223 224 static gint imap_close (Folder *folder, 225 FolderItem *item); 226 227 static gint imap_scan_tree (Folder *folder); 228 229 static gint imap_create_tree (Folder *folder); 230 231 static FolderItem *imap_create_folder (Folder *folder, 232 FolderItem *parent, 233 const gchar *name); 234 static gint imap_rename_folder (Folder *folder, 235 FolderItem *item, 236 const gchar *name); 237 static gint imap_remove_folder (Folder *folder, 238 FolderItem *item); 239 240 static FolderItem *imap_folder_item_new (Folder *folder); 241 static void imap_folder_item_destroy (Folder *folder, 242 FolderItem *item); 243 244 static IMAPSession *imap_session_get (Folder *folder); 245 246 static gint imap_auth (IMAPSession *session, 247 const gchar *user, 248 const gchar *pass, 249 IMAPAuthType type); 250 251 static gint imap_scan_tree_recursive (IMAPSession *session, 252 FolderItem *item, 253 gboolean subs_only); 254 static gint imap_scan_tree_recursive_dive (IMAPSession *session, 255 FolderItem *item, 256 gboolean subs_only); 257 258 static void imap_create_missing_folders (Folder *folder); 259 static FolderItem *imap_create_special_folder 260 (Folder *folder, 261 SpecialFolderItemType stype, 262 const gchar *name); 263 264 static gint imap_do_copy_msgs (Folder *folder, 265 FolderItem *dest, 266 MsgInfoList *msglist, 267 GHashTable *relation, 268 gboolean same_dest_ok); 269 270 static gint imap_do_remove_msgs (Folder *folder, 271 FolderItem *dest, 272 MsgInfoList *msglist, 273 GHashTable *relation); 274 275 static void imap_delete_all_cached_messages (FolderItem *item); 276 static void imap_set_batch (Folder *folder, 277 FolderItem *item, 278 gboolean batch); 279 static gint imap_set_message_flags (IMAPSession *session, 280 IMAPFolderItem *item, 281 MsgNumberList *numlist, 282 IMAPFlags flags, 283 GSList *tags, 284 gboolean is_set); 285 static gint imap_select (IMAPSession *session, 286 IMAPFolder *folder, 287 FolderItem *item, 288 gint *exists, 289 gint *recent, 290 gint *unseen, 291 guint32 *uid_validity, 292 gint *can_create_flags, 293 gboolean block); 294 static gint imap_status (IMAPSession *session, 295 IMAPFolder *folder, 296 const gchar *path, 297 IMAPFolderItem *item, 298 gint *messages, 299 guint32 *uid_next, 300 guint32 *uid_validity, 301 gint *unseen, 302 gboolean block); 303 304 static gchar imap_get_path_separator (IMAPSession *session, 305 IMAPFolder *folder, 306 const gchar *path, 307 gint *ok); 308 static gchar *imap_get_real_path (IMAPSession *session, 309 IMAPFolder *folder, 310 const gchar *path, 311 gint *ok); 312 static void imap_synchronise (FolderItem *item, gint days); 313 static gboolean imap_is_busy (Folder *folder); 314 315 static void imap_free_capabilities (IMAPSession *session); 316 317 /* low-level IMAP4rev1 commands */ 318 static gint imap_cmd_login (IMAPSession *session, 319 const gchar *user, 320 const gchar *pass, 321 const gchar *type); 322 static gint imap_cmd_noop (IMAPSession *session); 323 static gint imap_cmd_starttls (IMAPSession *session); 324 static gint imap_cmd_select (IMAPSession *session, 325 const gchar *folder, 326 gint *exists, 327 gint *recent, 328 gint *unseen, 329 guint32 *uid_validity, 330 gint *can_create_flags, 331 GSList **ok_flags, 332 gboolean block); 333 static gint imap_cmd_close (IMAPSession *session); 334 static gint imap_cmd_examine (IMAPSession *session, 335 const gchar *folder, 336 gint *exists, 337 gint *recent, 338 gint *unseen, 339 guint32 *uid_validity, 340 gboolean block); 341 static gint imap_cmd_create (IMAPSession *sock, 342 const gchar *folder); 343 static gint imap_cmd_rename (IMAPSession *sock, 344 const gchar *oldfolder, 345 const gchar *newfolder); 346 static gint imap_cmd_delete (IMAPSession *session, 347 const gchar *folder); 348 static gint imap_cmd_fetch (IMAPSession *sock, 349 guint32 uid, 350 const gchar *filename, 351 gboolean headers, 352 gboolean body); 353 static gint imap_cmd_append (IMAPSession *session, 354 IMAPFolderItem *item, 355 const gchar *destfolder, 356 const gchar *file, 357 IMAPFlags flags, 358 guint32 *new_uid); 359 static gint imap_cmd_copy (IMAPSession *session, 360 struct mailimap_set * set, 361 const gchar *destfolder, 362 struct mailimap_set ** source, 363 struct mailimap_set ** dest); 364 static gint imap_cmd_store (IMAPSession *session, 365 IMAPFolderItem *item, 366 struct mailimap_set * set, 367 IMAPFlags flags, 368 GSList *tags, 369 int do_add); 370 static gint imap_cmd_expunge (IMAPSession *session); 371 372 static void imap_path_separator_subst (gchar *str, 373 gchar separator); 374 375 static gboolean imap_rename_folder_func (GNode *node, 376 gpointer data); 377 static gint imap_get_num_list (Folder *folder, 378 FolderItem *item, 379 GSList **list, 380 gboolean *old_uids_valid); 381 static GSList *imap_get_msginfos (Folder *folder, 382 FolderItem *item, 383 GSList *msgnum_list); 384 static MsgInfo *imap_get_msginfo (Folder *folder, 385 FolderItem *item, 386 gint num); 387 static gboolean imap_scan_required (Folder *folder, 388 FolderItem *item); 389 static void imap_change_flags (Folder *folder, 390 FolderItem *item, 391 MsgInfo *msginfo, 392 MsgPermFlags newflags); 393 static gint imap_get_flags (Folder *folder, 394 FolderItem *item, 395 MsgInfoList *msglist, 396 GHashTable *msgflags); 397 static gchar *imap_item_get_path (Folder *folder, 398 FolderItem *item); 399 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item); 400 401 402 /* data types conversion libetpan <-> claws */ 403 static GSList * imap_list_from_lep(IMAPFolder * folder, 404 clist * list, const gchar * real_path, gboolean all); 405 static GSList * imap_get_lep_set_from_numlist(IMAPFolder *folder, MsgNumberList *numlist); 406 static GSList * imap_get_lep_set_from_msglist(IMAPFolder *folder, MsgInfoList *msglist); 407 static GSList * imap_uid_list_from_lep(clist * list, gint* length); 408 static GSList * imap_uid_list_from_lep_tab(carray * list); 409 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list, 410 GHashTable * hash, 411 GHashTable *tags_hash); 412 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info, 413 FolderItem *item); 414 static void imap_lep_set_free(GSList *seq_list); 415 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFolderItem *item, IMAPFlags flags, GSList *tags); 416 417 typedef struct _hashtable_data { 418 GSList *msglist; 419 IMAPFolderItem *item; 420 } hashtable_data; 421 422 static FolderClass imap_class; 423 424 FolderClass *imap_get_class(void) 425 { 426 if (imap_class.idstr == NULL) { 427 imap_class.type = F_IMAP; 428 imap_class.idstr = "imap"; 429 imap_class.uistr = "IMAP"; 430 431 /* Folder functions */ 432 imap_class.new_folder = imap_folder_new; 433 imap_class.destroy_folder = imap_folder_destroy; 434 imap_class.scan_tree = imap_scan_tree; 435 imap_class.create_tree = imap_create_tree; 436 437 /* FolderItem functions */ 438 imap_class.item_new = imap_folder_item_new; 439 imap_class.item_destroy = imap_folder_item_destroy; 440 imap_class.item_get_path = imap_item_get_path; 441 imap_class.create_folder = imap_create_folder; 442 imap_class.rename_folder = imap_rename_folder; 443 imap_class.remove_folder = imap_remove_folder; 444 imap_class.close = imap_close; 445 imap_class.get_num_list = imap_get_num_list; 446 imap_class.scan_required = imap_scan_required; 447 imap_class.set_xml = folder_set_xml; 448 imap_class.get_xml = folder_get_xml; 449 imap_class.item_set_xml = imap_item_set_xml; 450 imap_class.item_get_xml = imap_item_get_xml; 451 452 /* Message functions */ 453 imap_class.get_msginfo = imap_get_msginfo; 454 imap_class.get_msginfos = imap_get_msginfos; 455 imap_class.fetch_msg = imap_fetch_msg; 456 imap_class.fetch_msg_full = imap_fetch_msg_full; 457 imap_class.add_msg = imap_add_msg; 458 imap_class.add_msgs = imap_add_msgs; 459 imap_class.copy_msg = imap_copy_msg; 460 imap_class.copy_msgs = imap_copy_msgs; 461 imap_class.remove_msg = imap_remove_msg; 462 imap_class.remove_msgs = imap_remove_msgs; 463 imap_class.expunge = imap_expunge; 464 imap_class.remove_all_msg = imap_remove_all_msg; 465 imap_class.is_msg_changed = imap_is_msg_changed; 466 imap_class.change_flags = imap_change_flags; 467 imap_class.get_flags = imap_get_flags; 468 imap_class.set_batch = imap_set_batch; 469 imap_class.synchronise = imap_synchronise; 470 imap_class.remove_cached_msg = imap_remove_cached_msg; 471 } 472 473 return &imap_class; 474 } 475 476 static void imap_refresh_sensitivity (IMAPSession *session) 477 { 478 MainWindow *mainwin; 479 480 if (session->sens_update_block) 481 return; 482 mainwin = mainwindow_get_mainwindow(); 483 if (mainwin) { 484 toolbar_main_set_sensitive(mainwin); 485 main_window_set_menu_sensitive(mainwin); 486 } 487 } 488 489 static void lock_session(IMAPSession *session) 490 { 491 if (session) { 492 debug_print("locking session %p (%d)\n", session, session->busy); 493 if (session->busy) 494 debug_print(" SESSION WAS LOCKED !! \n"); 495 session->busy = TRUE; 496 imap_refresh_sensitivity(session); 497 } else { 498 debug_print("can't lock null session\n"); 499 } 500 } 501 502 static void unlock_session(IMAPSession *session) 503 { 504 if (session) { 505 debug_print("unlocking session %p\n", session); 506 session->busy = FALSE; 507 imap_refresh_sensitivity(session); 508 } else { 509 debug_print("can't unlock null session\n"); 510 } 511 } 512 513 static gboolean imap_ping(gpointer data) 514 { 515 Session *session = (Session *)data; 516 IMAPSession *imap_session = IMAP_SESSION(session); 517 int r; 518 519 if (session->state != SESSION_READY) 520 return FALSE; 521 if (imap_session->busy || !imap_session->authenticated) 522 return TRUE; 523 524 lock_session(imap_session); 525 r = imap_cmd_noop(imap_session); 526 unlock_session(imap_session); 527 528 return r == MAILIMAP_NO_ERROR; 529 } 530 531 static void imap_disc_session_destroy(IMAPSession *session) 532 { 533 RemoteFolder *rfolder = NULL; 534 535 if (session == NULL) 536 return; 537 538 rfolder = REMOTE_FOLDER(IMAP_SESSION(session)->folder); 539 540 if (rfolder == NULL) 541 return; 542 log_warning(LOG_PROTOCOL, _("IMAP connection broken\n")); 543 SESSION(session)->state = SESSION_DISCONNECTED; 544 SESSION(session)->sock = NULL; 545 } 546 547 static void imap_safe_destroy(IMAPSession *session) 548 { 549 if (!session->busy) 550 session_destroy(SESSION(session)); 551 else 552 session->do_destroy = TRUE; 553 } 554 555 static gboolean is_fatal(int libetpan_errcode) 556 { 557 switch(libetpan_errcode) { 558 case MAILIMAP_ERROR_STREAM: 559 case MAILIMAP_ERROR_PROTOCOL: 560 case MAILIMAP_ERROR_PARSE: 561 case MAILIMAP_ERROR_BAD_STATE: 562 return TRUE; 563 default: 564 return FALSE; 565 } 566 } 567 568 #define MY_LOG_WARNING(concat_cmd, ...) \ 569 msg = concat_cmd; \ 570 log_warning(LOG_PROTOCOL, msg, __VA_ARGS__); \ 571 g_free(msg); 572 573 static void imap_handle_error(Session *session, const gchar *server, int libetpan_errcode) 574 { 575 const gchar *session_server = (session ? session->server : NULL); 576 gchar *msg; 577 578 if (session_server == NULL) 579 session_server = server; 580 if (session_server == NULL) 581 session_server = "(null)"; 582 583 switch(libetpan_errcode) { 584 case MAILIMAP_NO_ERROR: 585 return; 586 case MAILIMAP_NO_ERROR_AUTHENTICATED: 587 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("authenticated"), "\n", NULL), session_server) 588 break; 589 case MAILIMAP_NO_ERROR_NON_AUTHENTICATED: 590 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("not authenticated"), "\n", NULL), session_server) 591 break; 592 case MAILIMAP_ERROR_BAD_STATE: 593 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("bad state"), "\n", NULL), session_server) 594 break; 595 case MAILIMAP_ERROR_STREAM: 596 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("stream error"), "\n", NULL), session_server) 597 break; 598 case MAILIMAP_ERROR_PARSE: 599 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("parse error " 600 "(very probably non-RFC compliance from the server)"), "\n", NULL), session_server) 601 break; 602 case MAILIMAP_ERROR_CONNECTION_REFUSED: 603 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("connection refused"), "\n", NULL), session_server) 604 break; 605 case MAILIMAP_ERROR_MEMORY: 606 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("memory error"), "\n", NULL), session_server) 607 break; 608 case MAILIMAP_ERROR_FATAL: 609 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("fatal error"), "\n", NULL), session_server) 610 break; 611 case MAILIMAP_ERROR_PROTOCOL: 612 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("protocol error " 613 "(very probably non-RFC compliance from the server)"), "\n", NULL), session_server) 614 break; 615 case MAILIMAP_ERROR_DONT_ACCEPT_CONNECTION: 616 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("connection not accepted"), "\n", NULL), session_server) 617 break; 618 case MAILIMAP_ERROR_APPEND: 619 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("APPEND error"), "\n", NULL), session_server) 620 break; 621 case MAILIMAP_ERROR_NOOP: 622 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("NOOP error"), "\n", NULL), session_server) 623 break; 624 case MAILIMAP_ERROR_LOGOUT: 625 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("LOGOUT error"), "\n", NULL), session_server) 626 break; 627 case MAILIMAP_ERROR_CAPABILITY: 628 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("CAPABILITY error"), "\n", NULL), session_server) 629 break; 630 case MAILIMAP_ERROR_CHECK: 631 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("CHECK error"), "\n", NULL), session_server) 632 break; 633 case MAILIMAP_ERROR_CLOSE: 634 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("CLOSE error"), "\n", NULL), session_server) 635 break; 636 case MAILIMAP_ERROR_EXPUNGE: 637 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("EXPUNGE error"), "\n", NULL), session_server) 638 break; 639 case MAILIMAP_ERROR_COPY: 640 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("COPY error"), "\n", NULL), session_server) 641 break; 642 case MAILIMAP_ERROR_UID_COPY: 643 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("UID COPY error"), "\n", NULL), session_server) 644 break; 645 case MAILIMAP_ERROR_CREATE: 646 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("CREATE error"), "\n", NULL), session_server) 647 break; 648 case MAILIMAP_ERROR_DELETE: 649 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("DELETE error"), "\n", NULL), session_server) 650 break; 651 case MAILIMAP_ERROR_EXAMINE: 652 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("EXAMINE error"), "\n", NULL), session_server) 653 break; 654 case MAILIMAP_ERROR_FETCH: 655 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("FETCH error"), "\n", NULL), session_server) 656 break; 657 case MAILIMAP_ERROR_UID_FETCH: 658 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("UID FETCH error"), "\n", NULL), session_server) 659 break; 660 case MAILIMAP_ERROR_LIST: 661 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("LIST error"), "\n", NULL), session_server) 662 break; 663 case MAILIMAP_ERROR_LOGIN: 664 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("LOGIN error"), "\n", NULL), session_server) 665 break; 666 case MAILIMAP_ERROR_LSUB: 667 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("LSUB error"), "\n", NULL), session_server) 668 break; 669 case MAILIMAP_ERROR_RENAME: 670 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("RENAME error"), "\n", NULL), session_server) 671 break; 672 case MAILIMAP_ERROR_SEARCH: 673 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("SEARCH error"), "\n", NULL), session_server) 674 break; 675 case MAILIMAP_ERROR_UID_SEARCH: 676 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("UID SEARCH error"), "\n", NULL), session_server) 677 break; 678 case MAILIMAP_ERROR_SELECT: 679 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("SELECT error"), "\n", NULL), session_server) 680 break; 681 case MAILIMAP_ERROR_STATUS: 682 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("STATUS error"), "\n", NULL), session_server) 683 break; 684 case MAILIMAP_ERROR_STORE: 685 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("STORE error"), "\n", NULL), session_server) 686 break; 687 case MAILIMAP_ERROR_UID_STORE: 688 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("UID STORE error"), "\n", NULL), session_server) 689 break; 690 case MAILIMAP_ERROR_SUBSCRIBE: 691 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("SUBSCRIBE error"), "\n", NULL), session_server) 692 break; 693 case MAILIMAP_ERROR_UNSUBSCRIBE: 694 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("UNSUBSCRIBE error"), "\n", NULL), session_server) 695 break; 696 case MAILIMAP_ERROR_STARTTLS: 697 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("STARTTLS error"), "\n", NULL), session_server) 698 break; 699 case MAILIMAP_ERROR_INVAL: 700 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("INVAL error"), "\n", NULL), session_server) 701 break; 702 case MAILIMAP_ERROR_EXTENSION: 703 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("EXTENSION error"), "\n", NULL), session_server) 704 break; 705 case MAILIMAP_ERROR_SASL: 706 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("SASL error"), "\n", NULL), session_server) 707 break; 708 case MAILIMAP_ERROR_SSL: 709 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("TLS error"), "\n", NULL), session_server) 710 break; 711 default: 712 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("Unknown error [%d]"), "\n", NULL), 713 session_server, libetpan_errcode) 714 break; 715 } 716 717 if (session && is_fatal(libetpan_errcode)) { 718 imap_disc_session_destroy(IMAP_SESSION(session)); 719 } else if (session && !is_fatal(libetpan_errcode)) { 720 if (IMAP_SESSION(session)->busy) 721 unlock_session(IMAP_SESSION(session)); 722 } 723 } 724 725 #undef MY_LOG_WARNING 726 727 static Folder *imap_folder_new(const gchar *name, const gchar *path) 728 { 729 Folder *folder; 730 731 folder = (Folder *)g_new0(IMAPFolder, 1); 732 folder->klass = &imap_class; 733 folder_init(folder, name); 734 REMOTE_FOLDER(folder)->session = NULL; 735 IMAP_FOLDER(folder)->search_charset_supported = TRUE; 736 IMAP_FOLDER(folder)->search_charset = g_strdup(conv_get_locale_charset_str_no_utf8()); 737 return folder; 738 } 739 740 static void imap_folder_destroy(Folder *folder) 741 { 742 while (imap_folder_get_refcnt(folder) > 0) 743 gtk_main_iteration(); 744 745 g_free(IMAP_FOLDER(folder)->search_charset); 746 747 RemoteFolder *rfolder = REMOTE_FOLDER(folder); 748 if (rfolder->session) 749 session_destroy(rfolder->session); 750 imap_done(folder); 751 } 752 753 static FolderItem *imap_folder_item_new(Folder *folder) 754 { 755 IMAPFolderItem *item; 756 757 item = g_new0(IMAPFolderItem, 1); 758 item->lastuid = 0; 759 item->uid_next = 0; 760 item->uid_list = NULL; 761 762 return (FolderItem *)item; 763 } 764 765 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item) 766 { 767 IMAPFolderItem *item = (IMAPFolderItem *)_item; 768 769 g_return_if_fail(item != NULL); 770 g_slist_free(item->uid_list); 771 772 g_free(_item); 773 } 774 775 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data) 776 { 777 IMAPFolderItem *item = (IMAPFolderItem *)node->data; 778 779 item->lastuid = 0; 780 g_slist_free(item->uid_list); 781 item->uid_list = NULL; 782 783 return FALSE; 784 } 785 786 static void imap_reset_uid_lists(Folder *folder) 787 { 788 if(folder->node == NULL) 789 return; 790 791 /* Destroy all uid lists and rest last uid */ 792 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL); 793 } 794 795 static int imap_get_capabilities(IMAPSession *session) 796 { 797 struct mailimap_capability_data *capabilities = NULL; 798 clistiter *cur; 799 int result; 800 801 if (session->capability != NULL) 802 return MAILIMAP_NO_ERROR; 803 804 result = imap_threaded_capability(session->folder, &capabilities); 805 806 if (result != MAILIMAP_NO_ERROR) { 807 return result; 808 } 809 810 if (capabilities == NULL || capabilities->cap_list == NULL) { 811 return MAILIMAP_NO_ERROR; 812 } 813 814 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ; 815 cur = clist_next(cur)) { 816 struct mailimap_capability * cap = 817 clist_content(cur); 818 if (!cap || cap->cap_data.cap_name == NULL) 819 continue; 820 session->capability = g_slist_append 821 (session->capability, 822 g_strdup(cap->cap_data.cap_name)); 823 debug_print("got capa %s\n", cap->cap_data.cap_name); 824 } 825 mailimap_capability_data_free(capabilities); 826 return MAILIMAP_NO_ERROR; 827 } 828 829 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap) 830 { 831 GSList *cur; 832 for (cur = session->capability; cur; cur = cur->next) { 833 if (!g_ascii_strcasecmp(cur->data, cap)) 834 return TRUE; 835 } 836 return FALSE; 837 } 838 839 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass, 840 IMAPAuthType type) 841 { 842 gint ok = MAILIMAP_ERROR_LOGIN; 843 static time_t last_login_err = 0; 844 gchar *ext_info = ""; 845 int r; 846 gchar *server = NULL; 847 if ((r = imap_get_capabilities(session)) != MAILIMAP_NO_ERROR) { 848 imap_handle_error(SESSION(session), NULL, r); 849 return r; 850 } 851 server = g_strdup(SESSION(session)->server); 852 switch(type) { 853 case IMAP_AUTH_ANON: 854 ok = imap_cmd_login(session, user, pass, "ANONYMOUS"); 855 break; 856 case IMAP_AUTH_SCRAM_SHA1: 857 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-1"); 858 break; 859 case IMAP_AUTH_SCRAM_SHA224: 860 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-224"); 861 break; 862 case IMAP_AUTH_SCRAM_SHA256: 863 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-256"); 864 break; 865 case IMAP_AUTH_SCRAM_SHA384: 866 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-384"); 867 break; 868 case IMAP_AUTH_SCRAM_SHA512: 869 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-512"); 870 break; 871 case IMAP_AUTH_PLAIN: 872 ok = imap_cmd_login(session, user, pass, "PLAIN"); 873 break; 874 case IMAP_AUTH_OAUTH2: 875 ok = imap_cmd_login(session, user, pass, "XOAUTH2"); 876 break; 877 case IMAP_AUTH_LOGIN: 878 ok = imap_cmd_login(session, user, pass, "LOGIN"); 879 break; 880 case IMAP_AUTH_PLAINTEXT: 881 ok = imap_cmd_login(session, user, pass, "plaintext"); 882 break; 883 case IMAP_AUTH_GSSAPI: 884 ok = imap_cmd_login(session, user, pass, "GSSAPI"); 885 break; 886 default: 887 debug_print("capabilities:\n" 888 "\t ANONYMOUS %d\n" 889 "\t SCRAM-SHA-1 %d\n" 890 "\t SCRAM-SHA-224 %d\n" 891 "\t SCRAM-SHA-256 %d\n" 892 "\t SCRAM-SHA-384 %d\n" 893 "\t SCRAM-SHA-512 %d\n" 894 "\t PLAIN %d\n" 895 "\t OAUTH2 %d\n" 896 "\t LOGIN %d\n" 897 "\t GSSAPI %d\n", 898 imap_has_capability(session, "ANONYMOUS"), 899 imap_has_capability(session, "SCRAM-SHA-1"), 900 imap_has_capability(session, "SCRAM-SHA-224"), 901 imap_has_capability(session, "SCRAM-SHA-256"), 902 imap_has_capability(session, "SCRAM-SHA-384"), 903 imap_has_capability(session, "SCRAM-SHA-512"), 904 imap_has_capability(session, "PLAIN"), 905 imap_has_capability(session, "XOAUTH2"), 906 imap_has_capability(session, "LOGIN"), 907 imap_has_capability(session, "GSSAPI")); 908 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "SCRAM-SHA-512")) 909 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-512"); 910 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "SCRAM-SHA-384")) 911 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-384"); 912 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "SCRAM-SHA-256")) 913 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-256"); 914 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "SCRAM-SHA-224")) 915 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-224"); 916 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "SCRAM-SHA-1")) 917 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-1"); 918 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "PLAIN")) 919 ok = imap_cmd_login(session, user, pass, "PLAIN"); 920 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "LOGIN")) 921 ok = imap_cmd_login(session, user, pass, "LOGIN"); 922 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "GSSAPI")) 923 ok = imap_cmd_login(session, user, pass, "GSSAPI"); 924 if (ok == MAILIMAP_ERROR_LOGIN) /* we always try plaintext login before giving up */ 925 ok = imap_cmd_login(session, user, pass, "plaintext"); 926 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "XOAUTH2")) 927 ok = imap_cmd_login(session, user, pass, "XOAUTH2"); 928 } 929 930 if (ok == MAILIMAP_NO_ERROR) 931 session->authenticated = TRUE; 932 else { 933 if (type == IMAP_AUTH_SCRAM_SHA1) { 934 ext_info = _("\n\nSCRAM-SHA-1 logins only work if libetpan has been " 935 "compiled with SASL support and the " 936 "SCRAM SASL plugin is installed."); 937 } 938 939 if (type == IMAP_AUTH_SCRAM_SHA224) { 940 ext_info = _("\n\nSCRAM-SHA-224 logins only work if libetpan has been " 941 "compiled with SASL support and the " 942 "SCRAM SASL plugin is installed."); 943 } 944 945 if (type == IMAP_AUTH_SCRAM_SHA256) { 946 ext_info = _("\n\nSCRAM-SHA-256 logins only work if libetpan has been " 947 "compiled with SASL support and the " 948 "SCRAM SASL plugin is installed."); 949 } 950 951 if (type == IMAP_AUTH_SCRAM_SHA384) { 952 ext_info = _("\n\nSCRAM-SHA-384 logins only work if libetpan has been " 953 "compiled with SASL support and the " 954 "SCRAM SASL plugin is installed."); 955 } 956 957 if (type == IMAP_AUTH_SCRAM_SHA512) { 958 ext_info = _("\n\nSCRAM-SHA-512 logins only work if libetpan has been " 959 "compiled with SASL support and the " 960 "SCRAM SASL plugin is installed."); 961 } 962 963 if (type == IMAP_AUTH_PLAIN) { 964 ext_info = _("\n\nPLAIN logins only work if libetpan has been " 965 "compiled with SASL support and the " 966 "PLAIN SASL plugin is installed."); 967 } 968 969 if (type == IMAP_AUTH_LOGIN) { 970 ext_info = _("\n\nLOGIN logins only work if libetpan has been " 971 "compiled with SASL support and the " 972 "LOGIN SASL plugin is installed."); 973 } 974 if (type == IMAP_AUTH_OAUTH2) { 975 ext_info = _("\n\nOAuth2 error. Check and correct your OAuth2 " 976 "account preferences."); 977 } 978 if (time(NULL) - last_login_err > 10) { 979 char msg[BUFSIZ]; 980 snprintf(msg, sizeof(msg), "login to %s: login refused: %s", server, ext_info); 981 alertpanel_error_log("%s", msg); 982 } 983 last_login_err = time(NULL); 984 } 985 g_free(server); 986 return ok; 987 } 988 989 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session) 990 { 991 RemoteFolder *rfolder = REMOTE_FOLDER(folder); 992 /* Check if this is the first try to establish a 993 connection, if yes we don't try to reconnect */ 994 debug_print("reconnecting\n"); 995 if (rfolder->session == NULL) { 996 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"), 997 folder->account->recv_server); 998 SESSION(session)->sock = NULL; 999 imap_safe_destroy(session); 1000 session = NULL; 1001 } else { 1002 rfolder->session = NULL; 1003 log_warning(LOG_PROTOCOL, _("IMAP connection to %s has been" 1004 " disconnected. Reconnecting...\n"), 1005 folder->account->recv_server); 1006 statusbar_print_all(_("IMAP connection to %s has been" 1007 " disconnected. Reconnecting...\n"), 1008 folder->account->recv_server); 1009 SESSION(session)->state = SESSION_DISCONNECTED; 1010 SESSION(session)->sock = NULL; 1011 imap_safe_destroy(session); 1012 /* Clear folders session to make imap_session_get create 1013 a new session, because of rfolder->session == NULL 1014 it will not try to reconnect again and so avoid an 1015 endless loop */ 1016 debug_print("getting session...\n"); 1017 session = imap_session_get(folder); 1018 rfolder->session = SESSION(session); 1019 statusbar_pop_all(); 1020 } 1021 return session; 1022 } 1023 1024 static IMAPSession *imap_session_get(Folder *folder) 1025 { 1026 RemoteFolder *rfolder = REMOTE_FOLDER(folder); 1027 IMAPSession *session = NULL; 1028 gint r = MAILIMAP_NO_ERROR; 1029 1030 g_return_val_if_fail(folder != NULL, NULL); 1031 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL); 1032 g_return_val_if_fail(folder->account != NULL, NULL); 1033 1034 if (prefs_common.work_offline && 1035 !inc_offline_should_override(FALSE, 1036 _("Claws Mail needs network access in order " 1037 "to access the IMAP server."))) { 1038 return NULL; 1039 } 1040 1041 /* check for deferred destroy */ 1042 if (rfolder->session != NULL) { 1043 session = IMAP_SESSION(rfolder->session); 1044 if (!session->busy && session->do_destroy) { 1045 rfolder->session = NULL; 1046 imap_safe_destroy(session); 1047 session = NULL; 1048 } 1049 } 1050 1051 /* Make sure we have a session */ 1052 if (rfolder->session != NULL && rfolder->session->state != SESSION_DISCONNECTED) { 1053 session = IMAP_SESSION(rfolder->session); 1054 } else if (rfolder->session != NULL && rfolder->session->state == SESSION_DISCONNECTED) { 1055 imap_safe_destroy(IMAP_SESSION(rfolder->session)); 1056 rfolder->session = NULL; 1057 goto new_conn; 1058 } else if (rfolder->connecting) { 1059 debug_print("already connecting\n"); 1060 return NULL; 1061 } else { 1062 new_conn: 1063 imap_reset_uid_lists(folder); 1064 if (time(NULL) - rfolder->last_failure <= 2) 1065 return NULL; 1066 rfolder->connecting = TRUE; 1067 session = imap_session_new(folder, folder->account); 1068 } 1069 if(session == NULL) { 1070 rfolder->last_failure = time(NULL); 1071 rfolder->connecting = FALSE; 1072 return NULL; 1073 } 1074 1075 /* Make sure session is authenticated */ 1076 if (!IMAP_SESSION(session)->authenticated) 1077 r = imap_session_authenticate(IMAP_SESSION(session), folder->account); 1078 1079 if (r != MAILIMAP_NO_ERROR || (!is_fatal(r) && !IMAP_SESSION(session)->authenticated)) { 1080 rfolder->session = NULL; 1081 if (!is_fatal(r)) { 1082 imap_threaded_disconnect(session->folder); 1083 } 1084 SESSION(session)->state = SESSION_DISCONNECTED; 1085 SESSION(session)->sock = NULL; 1086 imap_safe_destroy(session); 1087 rfolder->last_failure = time(NULL); 1088 rfolder->connecting = FALSE; 1089 return NULL; 1090 } 1091 1092 /* I think the point of this code is to avoid sending a 1093 * keepalive if we've used the session recently and therefore 1094 * think it's still alive. Unfortunately, most of the code 1095 * does not yet check for errors on the socket, and so if the 1096 * connection drops we don't notice until the timeout expires. 1097 * A better solution than sending a NOOP every time would be 1098 * for every command to be prepared to retry until it is 1099 * successfully sent. -- mbp */ 1100 uint timeout = 60; // seconds 1101 if ((time(NULL) - SESSION(session)->last_access_time > timeout) || session->cancelled) { 1102 /* verify that the session is still alive */ 1103 r = imap_cmd_noop(session); 1104 1105 if (r != MAILIMAP_NO_ERROR) { 1106 debug_print("disconnected!\n"); 1107 if (!is_fatal(r)) 1108 session = imap_reconnect_if_possible(folder, session); 1109 else { 1110 rfolder->session = NULL; 1111 rfolder->connecting = FALSE; 1112 SESSION(session)->state = SESSION_DISCONNECTED; 1113 SESSION(session)->sock = NULL; 1114 imap_safe_destroy(session); 1115 session = imap_session_get(folder); 1116 } 1117 } 1118 if (session) 1119 session->cancelled = FALSE; 1120 } 1121 1122 rfolder->session = SESSION(session); 1123 rfolder->connecting = FALSE; 1124 1125 return IMAP_SESSION(session); 1126 } 1127 1128 static IMAPSession *imap_session_new(Folder * folder, 1129 const PrefsAccount *account) 1130 { 1131 IMAPSession *session; 1132 gushort port; 1133 int r; 1134 int authenticated = FALSE; 1135 gchar *buf; 1136 1137 SSLType ssl_type; 1138 1139 port = account->set_imapport ? account->imapport 1140 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT; 1141 ssl_type = account->ssl_imap; 1142 1143 imap_init(folder); 1144 IMAP_FOLDER(folder)->max_set_size = account->imap_batch_size; 1145 buf = g_strdup_printf(_("Account '%s': Connecting to IMAP server: %s:%d..."), 1146 folder->account->account_name, folder->account->recv_server, 1147 port); 1148 statusbar_print_all("%s", buf); 1149 log_message(LOG_PROTOCOL, "%s\n", buf); 1150 g_free(buf); 1151 1152 if (account->set_tunnelcmd) { 1153 r = imap_threaded_connect_cmd(folder, 1154 account->tunnelcmd, 1155 account->recv_server, 1156 port); 1157 } 1158 else { 1159 if (ssl_type == SSL_TUNNEL) { 1160 r = imap_threaded_connect_ssl(folder, account->recv_server, port); 1161 } else { 1162 r = imap_threaded_connect(folder, account->recv_server, port); 1163 } 1164 } 1165 1166 statusbar_pop_all(); 1167 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) { 1168 authenticated = TRUE; 1169 } 1170 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) { 1171 authenticated = FALSE; 1172 } 1173 else { 1174 if (r == MAILIMAP_ERROR_SSL) 1175 log_error(LOG_PROTOCOL, _("TLS handshake failed\n")); 1176 else 1177 imap_handle_error(NULL, account->recv_server, r); 1178 1179 alertpanel_error_log("Cannot connect to IMAP server: %s:%d", account->recv_server, port); 1180 return NULL; 1181 } 1182 1183 session = g_new0(IMAPSession, 1); 1184 session_init(SESSION(session), account, FALSE); 1185 SESSION(session)->type = SESSION_IMAP; 1186 SESSION(session)->server = g_strdup(account->recv_server); 1187 SESSION(session)->port = port; 1188 SESSION(session)->sock = NULL; 1189 SESSION(session)->destroy = imap_session_destroy; 1190 1191 session->capability = NULL; 1192 session->authenticated = authenticated; 1193 session->mbox = NULL; 1194 session->exists = 0; 1195 session->recent = 0; 1196 session->expunge = 0; 1197 session->cmd_count = 0; 1198 session->folder = folder; 1199 IMAP_FOLDER(session->folder)->last_seen_separator = 0; 1200 1201 if (account->ssl_imap == SSL_STARTTLS) { 1202 gint ok; 1203 1204 ok = imap_cmd_starttls(session); 1205 if (ok != MAILIMAP_NO_ERROR) { 1206 log_warning(LOG_PROTOCOL, _("Can't start STARTTLS session.\n")); 1207 if (!is_fatal(ok)) { 1208 SESSION(session)->sock = NULL; 1209 imap_safe_destroy(session); 1210 } 1211 return NULL; 1212 } 1213 1214 imap_free_capabilities(session); 1215 session->authenticated = FALSE; 1216 session->uidplus = FALSE; 1217 session->cmd_count = 1; 1218 } 1219 SESSION(session)->use_tls_sni = account->use_tls_sni; 1220 1221 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n", 1222 (session->authenticated) ? "pre" : "un"); 1223 1224 session_register_ping(SESSION(session), imap_ping); 1225 1226 return session; 1227 } 1228 1229 static gint imap_session_authenticate(IMAPSession *session, 1230 PrefsAccount *account) 1231 { 1232 gchar *pass, *acc_pass = NULL; 1233 gboolean failed = FALSE; 1234 gint ok = MAILIMAP_NO_ERROR; 1235 g_return_val_if_fail(account->userid != NULL, MAILIMAP_ERROR_BAD_STATE); 1236 if (!password_get(account->userid, account->recv_server, "imap", 1237 SESSION(session)->port, &acc_pass)) { 1238 acc_pass = passwd_store_get_account(account->account_id, 1239 PWS_ACCOUNT_RECV); 1240 } 1241 1242 try_again: 1243 pass = acc_pass; 1244 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON && account->imap_auth_type != IMAP_AUTH_GSSAPI) { 1245 gchar *tmp_pass; 1246 tmp_pass = input_dialog_query_password_keep(account->recv_server, 1247 account->userid, 1248 &(account->session_passwd)); 1249 if (!tmp_pass) { 1250 return MAILIMAP_NO_ERROR; 1251 } 1252 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return MAILIMAP_NO_ERROR;}); 1253 g_free(tmp_pass); 1254 } else if (account->imap_auth_type == IMAP_AUTH_ANON || account->imap_auth_type == IMAP_AUTH_GSSAPI) { 1255 pass = ""; 1256 } 1257 if ((ok = imap_auth(session, account->userid, pass, account->imap_auth_type)) != MAILIMAP_NO_ERROR) { 1258 1259 if (!failed && !is_fatal(ok)) { 1260 if (acc_pass != NULL) { 1261 memset(acc_pass, 0, strlen(acc_pass)); 1262 g_free(acc_pass); 1263 acc_pass = NULL; 1264 } 1265 failed = TRUE; 1266 if (account->session_passwd != NULL) { 1267 g_free(account->session_passwd); 1268 account->session_passwd = NULL; 1269 } 1270 goto try_again; 1271 } else { 1272 alertpanel_error_log("Cannot login to IMAP server %s", account->recv_server); 1273 } 1274 1275 if (acc_pass != NULL) { 1276 memset(acc_pass, 0, strlen(acc_pass)); 1277 g_free(acc_pass); 1278 acc_pass = NULL; 1279 } 1280 1281 return ok; 1282 } 1283 1284 if (acc_pass) { 1285 memset(acc_pass, 0, strlen(acc_pass)); 1286 g_free(acc_pass); 1287 } 1288 statusbar_pop_all(); 1289 session->authenticated = TRUE; 1290 return MAILIMAP_NO_ERROR; 1291 } 1292 1293 static void imap_session_destroy(Session *session) 1294 { 1295 if (session->state != SESSION_DISCONNECTED) 1296 imap_threaded_disconnect(IMAP_SESSION(session)->folder); 1297 1298 imap_free_capabilities(IMAP_SESSION(session)); 1299 g_free(IMAP_SESSION(session)->mbox); 1300 } 1301 1302 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid) 1303 { 1304 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE); 1305 } 1306 1307 static guint get_file_size_with_crs(const gchar *filename) 1308 { 1309 FILE *fp = NULL; 1310 guint cnt = 0; 1311 gchar buf[4096]; 1312 1313 if (filename == NULL) 1314 return -1; 1315 1316 fp = g_fopen(filename, "rb"); 1317 if (!fp) 1318 return -1; 1319 1320 while (fgets(buf, sizeof (buf), fp) != NULL) { 1321 cnt += strlen(buf); 1322 if (!strstr(buf, "\r\n") && strstr(buf, "\n")) 1323 cnt++; 1324 } 1325 1326 fclose(fp); 1327 return cnt; 1328 } 1329 1330 static gchar *imap_get_cached_filename(FolderItem *item, guint msgnum) 1331 { 1332 gchar *path, *filename; 1333 1334 cm_return_val_if_fail(item != NULL, NULL); 1335 1336 path = folder_item_get_path(item); 1337 1338 if (!is_dir_exist(path)) { 1339 g_free(path); 1340 return NULL; 1341 } 1342 1343 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msgnum), NULL); 1344 1345 if (is_dir_exist(filename)) { 1346 g_free(filename); 1347 filename = g_strconcat(path, G_DIR_SEPARATOR_S, ".", itos(msgnum), NULL); 1348 } 1349 g_free(path); 1350 1351 return filename; 1352 } 1353 1354 static void imap_remove_cached_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo) 1355 { 1356 char *name = imap_get_cached_filename(item, msginfo->msgnum); 1357 if (!name) { 1358 warn("cannot get filename for message number %d", msginfo->msgnum); 1359 return; 1360 } 1361 if (remove(name) < 0) 1362 warn("remove %s", name); 1363 free(name); 1364 } 1365 1366 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid, 1367 gboolean headers, gboolean body) 1368 { 1369 gchar *path, *filename; 1370 IMAPSession *session; 1371 gint ok; 1372 1373 g_return_val_if_fail(folder != NULL, NULL); 1374 g_return_val_if_fail(item != NULL, NULL); 1375 1376 if (uid == 0) 1377 return NULL; 1378 1379 path = folder_item_get_path(item); 1380 if (!is_dir_exist(path)) { 1381 if(is_file_exist(path)) 1382 unlink(path); 1383 make_dir_hier(path); 1384 } 1385 g_free(path); 1386 1387 filename = imap_get_cached_filename(item, uid); 1388 debug_print("trying to fetch cached %s\n", filename); 1389 1390 if (is_file_exist(filename)) { 1391 /* see whether the local file represents the whole message 1392 * or not. As the IMAP server reports size with \r chars, 1393 * we have to update the local file (UNIX \n only) size */ 1394 MsgInfo *cached = msgcache_get_msg(item->cache,uid); 1395 guint have_size = -1; 1396 1397 if (cached) 1398 debug_print("message %d has been already %scached.\n", uid, 1399 MSG_IS_FULLY_CACHED(cached->flags) ? "fully ":""); 1400 1401 if (!cached || !MSG_IS_FULLY_CACHED(cached->flags)) { 1402 have_size = get_file_size_with_crs(filename); 1403 if (cached && (cached->size <= have_size || !body)) { 1404 ok = file_strip_crs(filename); 1405 if (ok == 0 && cached && cached->size <= have_size) { 1406 /* we have it all and stripped */ 1407 debug_print("...fully cached in fact (%u/%"G_GOFFSET_FORMAT"); setting flag.\n", 1408 have_size, cached->size); 1409 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0); 1410 } 1411 procmsg_msginfo_free(&cached); 1412 return filename; 1413 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) { 1414 debug_print("message not cached and file recent, considering file complete\n"); 1415 ok = file_strip_crs(filename); 1416 if (ok == 0) 1417 return filename; 1418 } else { 1419 procmsg_msginfo_free(&cached); 1420 } 1421 } 1422 if (cached && MSG_IS_FULLY_CACHED(cached->flags)) { 1423 procmsg_msginfo_free(&cached); 1424 return filename; 1425 } 1426 } else { 1427 MsgInfo *cached = msgcache_get_msg(item->cache,uid); 1428 if (cached) { 1429 procmsg_msginfo_unset_flags(cached, MSG_FULLY_CACHED, 0); 1430 procmsg_msginfo_free(&cached); 1431 } 1432 } 1433 1434 debug_print("getting session...\n"); 1435 session = imap_session_get(folder); 1436 1437 if (!session) { 1438 g_free(filename); 1439 return NULL; 1440 } 1441 session_set_access_time(SESSION(session)); 1442 lock_session(session); /* unlocked later in the function */ 1443 1444 debug_print("IMAP fetching messages\n"); 1445 ok = imap_select(session, IMAP_FOLDER(folder), item, 1446 NULL, NULL, NULL, NULL, NULL, FALSE); 1447 if (ok != MAILIMAP_NO_ERROR) { 1448 g_warning("can't select mailbox %s", item->path); 1449 g_free(filename); 1450 return NULL; 1451 } 1452 1453 session_set_access_time(SESSION(session)); 1454 1455 debug_print("getting message %d...\n", uid); 1456 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body); 1457 1458 if (ok != MAILIMAP_NO_ERROR) { 1459 g_warning("can't fetch message %d", uid); 1460 g_free(filename); 1461 return NULL; 1462 } 1463 1464 session_set_access_time(SESSION(session)); 1465 unlock_session(session); 1466 1467 ok = file_strip_crs(filename); 1468 1469 if (ok == 0 && headers && body) { 1470 MsgInfo *cached = msgcache_get_msg(item->cache,uid); 1471 if (cached) { 1472 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0); 1473 procmsg_msginfo_free(&cached); 1474 } 1475 } else if (ok == -1) { 1476 MsgInfo *cached = msgcache_get_msg(item->cache,uid); 1477 if (cached) { 1478 procmsg_msginfo_unset_flags(cached, MSG_FULLY_CACHED, 0); 1479 procmsg_msginfo_free(&cached); 1480 } 1481 } 1482 return filename; 1483 } 1484 1485 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid) 1486 { 1487 gchar *filename; 1488 guint size = 0; 1489 MsgInfo *cached = msgcache_get_msg(item->cache,uid); 1490 1491 if (!cached) 1492 return FALSE; 1493 1494 if (MSG_IS_FULLY_CACHED(cached->flags)) { 1495 procmsg_msginfo_free(&cached); 1496 return TRUE; 1497 } 1498 1499 filename = imap_get_cached_filename(item, uid); 1500 1501 if (is_file_exist(filename)) { 1502 if (cached && cached->total_size == cached->size) { 1503 /* fast path */ 1504 g_free(filename); 1505 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0); 1506 return TRUE; 1507 } 1508 size = get_file_size_with_crs(filename); 1509 } 1510 g_free(filename); 1511 debug_print("msg %d cached, has size %d, full should be %"G_GOFFSET_FORMAT".\n", uid, size, cached->size); 1512 if (cached && size >= cached->size) { 1513 cached->total_size = cached->size; 1514 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0); 1515 procmsg_msginfo_free(&cached); 1516 return TRUE; 1517 } 1518 if (cached) 1519 procmsg_msginfo_free(&cached); 1520 return FALSE; 1521 } 1522 1523 void imap_cache_msg(FolderItem *item, gint msgnum) 1524 { 1525 Folder *folder = NULL; 1526 1527 if (!item) 1528 return; 1529 folder = item->folder; 1530 1531 if (!imap_is_msg_fully_cached(folder, item, msgnum)) { 1532 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE); 1533 debug_print("fetched %s\n", tmp); 1534 g_free(tmp); 1535 } 1536 } 1537 1538 static gint imap_add_msg(Folder *folder, FolderItem *dest, 1539 const gchar *file, MsgFlags *flags) 1540 { 1541 gint ret; 1542 GSList file_list; 1543 MsgFileInfo fileinfo; 1544 1545 g_return_val_if_fail(file != NULL, -1); 1546 1547 fileinfo.msginfo = NULL; 1548 fileinfo.file = (gchar *)file; 1549 fileinfo.flags = flags; 1550 file_list.data = &fileinfo; 1551 file_list.next = NULL; 1552 1553 ret = imap_add_msgs(folder, dest, &file_list, NULL); 1554 return ret; 1555 } 1556 1557 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list, 1558 GHashTable *relation) 1559 { 1560 gchar *destdir; 1561 IMAPSession *session; 1562 guint32 last_uid = 0; 1563 GSList *cur; 1564 MsgFileInfo *fileinfo; 1565 gint ok = MAILIMAP_NO_ERROR; 1566 gint curnum = 0, total = 0; 1567 gboolean missing_uids = FALSE; 1568 1569 g_return_val_if_fail(folder != NULL, -1); 1570 g_return_val_if_fail(dest != NULL, -1); 1571 g_return_val_if_fail(file_list != NULL, -1); 1572 1573 debug_print("getting session...\n"); 1574 session = imap_session_get(folder); 1575 if (!session) { 1576 return -1; 1577 } 1578 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok); 1579 if (is_fatal(ok)) { 1580 g_free(destdir); 1581 return -1; 1582 } 1583 statusbar_print_all(_("Adding messages...")); 1584 total = g_slist_length(file_list); 1585 for (cur = file_list; cur != NULL; cur = cur->next) { 1586 IMAPFlags iflags = 0; 1587 guint32 new_uid = 0; 1588 gchar *real_file = NULL; 1589 fileinfo = (MsgFileInfo *)cur->data; 1590 1591 statusbar_progress_all(curnum, total, total < 10 ? 1:10); 1592 curnum++; 1593 1594 if (fileinfo->flags) { 1595 if (MSG_IS_MARKED(*fileinfo->flags)) 1596 iflags |= IMAP_FLAG_FLAGGED; 1597 if (MSG_IS_REPLIED(*fileinfo->flags)) 1598 iflags |= IMAP_FLAG_ANSWERED; 1599 if (MSG_IS_FORWARDED(*fileinfo->flags)) 1600 iflags |= IMAP_FLAG_FORWARDED; 1601 if (MSG_IS_SPAM(*fileinfo->flags)) 1602 iflags |= IMAP_FLAG_SPAM; 1603 else 1604 iflags |= IMAP_FLAG_HAM; 1605 if (!MSG_IS_UNREAD(*fileinfo->flags)) 1606 iflags |= IMAP_FLAG_SEEN; 1607 1608 } 1609 1610 if (real_file == NULL) 1611 real_file = g_strdup(fileinfo->file); 1612 1613 if (folder_has_parent_of_type(dest, F_QUEUE) || 1614 folder_has_parent_of_type(dest, F_OUTBOX) || 1615 folder_has_parent_of_type(dest, F_DRAFT) || 1616 folder_has_parent_of_type(dest, F_TRASH)) 1617 iflags |= IMAP_FLAG_SEEN; 1618 1619 ok = imap_cmd_append(session, IMAP_FOLDER_ITEM(dest), destdir, real_file, iflags, 1620 &new_uid); 1621 1622 if (ok != MAILIMAP_NO_ERROR) { 1623 g_warning("can't append message %s", real_file); 1624 g_free(real_file); 1625 g_free(destdir); 1626 statusbar_progress_all(0,0,0); 1627 statusbar_pop_all(); 1628 return -1; 1629 } else { 1630 debug_print("appended new message as %d\n", new_uid); 1631 /* put the local file in the imapcache, so that we don't 1632 * have to fetch it back later. */ 1633 1634 if (new_uid == 0) { 1635 missing_uids = TRUE; 1636 debug_print("Missing UID (0)\n"); 1637 } 1638 if (new_uid > 0) { 1639 gchar *cache_path = folder_item_get_path(dest); 1640 if (!is_dir_exist(cache_path)) 1641 make_dir_hier(cache_path); 1642 if (is_dir_exist(cache_path)) { 1643 gchar *cache_file = g_strconcat( 1644 cache_path, G_DIR_SEPARATOR_S, 1645 itos(new_uid), NULL); 1646 if (copy_file(real_file, cache_file, TRUE) < 0) { 1647 g_warning("can't copy %s to %s.bak", real_file, cache_file); 1648 } else { 1649 debug_print("got UID %d, copied to cache: %s\n", new_uid, cache_file); 1650 } 1651 g_free(cache_file); 1652 } 1653 g_free(cache_path); 1654 } 1655 } 1656 1657 if (relation != NULL) 1658 g_hash_table_insert(relation, fileinfo->msginfo != NULL ? 1659 (gpointer) fileinfo->msginfo : (gpointer) fileinfo, 1660 GINT_TO_POINTER(new_uid)); 1661 if (last_uid < new_uid) { 1662 last_uid = new_uid; 1663 } 1664 1665 g_free(real_file); 1666 } 1667 1668 statusbar_progress_all(0,0,0); 1669 statusbar_pop_all(); 1670 1671 1672 g_free(destdir); 1673 1674 imap_scan_required(folder, dest); 1675 1676 session = imap_session_get(folder); 1677 if (!session) { 1678 return -1; 1679 } 1680 if (missing_uids) { 1681 gint a; 1682 ok = imap_select(session, IMAP_FOLDER(folder), dest, 1683 &a, NULL, NULL, NULL, NULL, FALSE); 1684 } 1685 return last_uid; 1686 } 1687 1688 static GSList *flatten_mailimap_set(struct mailimap_set * set) 1689 { 1690 GSList *result = NULL; 1691 clistiter *list; 1692 guint32 start, end, t; 1693 GSList *cur; 1694 1695 if (!set || !set->set_list) 1696 return NULL; 1697 1698 for (list = clist_begin(set->set_list); list; list = clist_next(list)) { 1699 struct mailimap_set_item *item = (struct mailimap_set_item *)clist_content(list); 1700 start = item->set_first; 1701 end = item->set_last; 1702 if (start <= end) { 1703 for (t = start; t <= end; t++) { 1704 result = g_slist_prepend(result, GINT_TO_POINTER(t)); 1705 } 1706 } 1707 } 1708 result = g_slist_reverse(result); 1709 if (debug_get_mode()) { 1710 debug_print("flat imap set: "); 1711 for (cur = result; cur; cur = cur->next) { 1712 debug_print("%d ", GPOINTER_TO_INT(cur->data)); 1713 } 1714 debug_print("\n"); 1715 } 1716 1717 return result; 1718 } 1719 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, 1720 MsgInfoList *msglist, GHashTable *relation, 1721 gboolean same_dest_ok) 1722 { 1723 FolderItem *src; 1724 gchar *destdir; 1725 GSList *seq_list, *cur; 1726 MsgInfo *msginfo; 1727 IMAPSession *session; 1728 gint ok = MAILIMAP_NO_ERROR; 1729 GHashTable *uid_hash; 1730 gint last_num = 0; 1731 1732 g_return_val_if_fail(folder != NULL, -1); 1733 g_return_val_if_fail(dest != NULL, -1); 1734 g_return_val_if_fail(msglist != NULL, -1); 1735 1736 debug_print("getting session...\n"); 1737 session = imap_session_get(folder); 1738 1739 if (!session) { 1740 return -1; 1741 } 1742 1743 msginfo = (MsgInfo *)msglist->data; 1744 src = msginfo->folder; 1745 if (!same_dest_ok && src == dest) { 1746 g_warning("the src folder is identical to the dest"); 1747 return -1; 1748 } 1749 1750 if (src->folder != dest->folder) { 1751 GSList *infolist = NULL, *cur; 1752 int res = -1; 1753 for (cur = msglist; cur; cur = cur->next) { 1754 msginfo = (MsgInfo *)cur->data; 1755 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1); 1756 fileinfo->file = procmsg_get_message_file(msginfo); 1757 fileinfo->flags = &(msginfo->flags); 1758 infolist = g_slist_prepend(infolist, fileinfo); 1759 } 1760 infolist = g_slist_reverse(infolist); 1761 res = folder_item_add_msgs(dest, infolist, FALSE); 1762 for (cur = infolist; cur; cur = cur->next) { 1763 MsgFileInfo *info = (MsgFileInfo *)cur->data; 1764 g_free(info->file); 1765 g_free(info); 1766 } 1767 g_slist_free(infolist); 1768 return res; 1769 } 1770 1771 lock_session(session); /* unlocked later in the function */ 1772 1773 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder, 1774 NULL, NULL, NULL, NULL, NULL, FALSE); 1775 if (ok != MAILIMAP_NO_ERROR) { 1776 return ok; 1777 } 1778 1779 unlock_session(session); 1780 1781 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok); 1782 1783 if (is_fatal(ok)) { 1784 g_free(destdir); 1785 return ok; 1786 } 1787 1788 seq_list = imap_get_lep_set_from_msglist(IMAP_FOLDER(folder), msglist); 1789 uid_hash = g_hash_table_new(g_direct_hash, g_direct_equal); 1790 1791 statusbar_print_all(_("Copying messages...")); 1792 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) { 1793 struct mailimap_set * seq_set; 1794 struct mailimap_set * source = NULL; 1795 struct mailimap_set * dest = NULL; 1796 seq_set = cur->data; 1797 1798 debug_print("Copying messages from %s to %s ...\n", 1799 src->path, destdir); 1800 1801 lock_session(session); /* unlocked later in the function */ 1802 ok = imap_cmd_copy(session, seq_set, destdir, 1803 &source, &dest); 1804 1805 if (is_fatal(ok)) { 1806 session = NULL; 1807 } 1808 1809 if (ok == MAILIMAP_NO_ERROR) { 1810 unlock_session(session); 1811 if (relation && source && dest) { 1812 GSList *s_list = flatten_mailimap_set(source); 1813 GSList *d_list = flatten_mailimap_set(dest); 1814 GSList *s_cur, *d_cur; 1815 if (g_slist_length(s_list) == g_slist_length(d_list)) { 1816 1817 for (s_cur = s_list, d_cur = d_list; 1818 s_cur && d_cur; 1819 s_cur = s_cur->next, d_cur = d_cur->next) { 1820 g_hash_table_insert(uid_hash, s_cur->data, d_cur->data); 1821 } 1822 1823 } else { 1824 debug_print("hhhmm, source list length != dest list length.\n"); 1825 } 1826 g_slist_free(s_list); 1827 g_slist_free(d_list); 1828 } 1829 } 1830 1831 1832 if (source) 1833 mailimap_set_free(source); 1834 if (dest) 1835 mailimap_set_free(dest); 1836 1837 if (ok != MAILIMAP_NO_ERROR) { 1838 g_hash_table_destroy(uid_hash); 1839 imap_lep_set_free(seq_list); 1840 statusbar_pop_all(); 1841 return -1; 1842 } 1843 } 1844 1845 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) { 1846 MsgInfo *msginfo = (MsgInfo *)cur->data; 1847 gpointer hashval; 1848 1849 hashval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(msginfo->msgnum)); 1850 1851 if (hashval != NULL) { 1852 gint num = GPOINTER_TO_INT(hashval); 1853 g_hash_table_insert(relation, msginfo, 1854 GINT_TO_POINTER(num)); 1855 if (num > last_num) 1856 last_num = num; 1857 debug_print("copied message %d as %d\n", msginfo->msgnum, num); 1858 /* put the local file in the imapcache, so that we don't 1859 * have to fetch it back later. */ 1860 if (num > 0) { 1861 gchar *cache_path = folder_item_get_path(msginfo->folder); 1862 gchar *real_file = g_strconcat( 1863 cache_path, G_DIR_SEPARATOR_S, 1864 itos(msginfo->msgnum), NULL); 1865 gchar *cache_file = NULL; 1866 g_free(cache_path); 1867 cache_path = folder_item_get_path(dest); 1868 cache_file = g_strconcat( 1869 cache_path, G_DIR_SEPARATOR_S, 1870 itos(num), NULL); 1871 if (!is_dir_exist(cache_path)) 1872 make_dir_hier(cache_path); 1873 if (is_file_exist(real_file) && is_dir_exist(cache_path)) { 1874 if (copy_file(real_file, cache_file, TRUE) < 0) 1875 debug_print("couldn't cache to %s: %s\n", cache_file, 1876 strerror(errno)); 1877 else 1878 debug_print("copied to cache: %s\n", cache_file); 1879 } 1880 g_free(real_file); 1881 g_free(cache_file); 1882 g_free(cache_path); 1883 } 1884 } else 1885 g_hash_table_insert(relation, msginfo, 1886 GINT_TO_POINTER(0)); 1887 } 1888 statusbar_pop_all(); 1889 1890 g_hash_table_destroy(uid_hash); 1891 imap_lep_set_free(seq_list); 1892 1893 g_free(destdir); 1894 1895 IMAP_FOLDER_ITEM(dest)->lastuid = 0; 1896 IMAP_FOLDER_ITEM(dest)->uid_next = 0; 1897 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list); 1898 IMAP_FOLDER_ITEM(dest)->uid_list = NULL; 1899 1900 imap_scan_required(folder, dest); 1901 if (ok == MAILIMAP_NO_ERROR) 1902 return last_num; 1903 else 1904 return -1; 1905 } 1906 1907 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo) 1908 { 1909 GSList msglist; 1910 1911 g_return_val_if_fail(msginfo != NULL, -1); 1912 1913 msglist.data = msginfo; 1914 msglist.next = NULL; 1915 1916 return imap_copy_msgs(folder, dest, &msglist, NULL); 1917 } 1918 1919 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, 1920 MsgInfoList *msglist, GHashTable *relation) 1921 { 1922 MsgInfo *msginfo; 1923 gint ret; 1924 1925 g_return_val_if_fail(folder != NULL, -1); 1926 g_return_val_if_fail(dest != NULL, -1); 1927 g_return_val_if_fail(msglist != NULL, -1); 1928 1929 msginfo = (MsgInfo *)msglist->data; 1930 g_return_val_if_fail(msginfo->folder != NULL, -1); 1931 1932 ret = imap_do_copy_msgs(folder, dest, msglist, relation, FALSE); 1933 return ret; 1934 } 1935 1936 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest, 1937 MsgInfoList *msglist, GHashTable *relation) 1938 { 1939 gchar *destdir, *dir; 1940 GSList *numlist = NULL, *cur; 1941 MsgInfo *msginfo; 1942 IMAPSession *session; 1943 gint ok = MAILIMAP_NO_ERROR; 1944 1945 g_return_val_if_fail(folder != NULL, -1); 1946 g_return_val_if_fail(dest != NULL, -1); 1947 g_return_val_if_fail(msglist != NULL, -1); 1948 1949 debug_print("getting session...\n"); 1950 session = imap_session_get(folder); 1951 if (!session) { 1952 return -1; 1953 } 1954 1955 lock_session(session); /* unlocked later in the function */ 1956 1957 msginfo = (MsgInfo *)msglist->data; 1958 1959 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder, 1960 NULL, NULL, NULL, NULL, NULL, FALSE); 1961 if (ok != MAILIMAP_NO_ERROR) { 1962 return ok; 1963 } 1964 1965 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok); 1966 if (is_fatal(ok)) { 1967 g_free(destdir); 1968 return ok; 1969 } 1970 for (cur = msglist; cur; cur = cur->next) { 1971 msginfo = (MsgInfo *)cur->data; 1972 if (!MSG_IS_DELETED(msginfo->flags)) 1973 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum)); 1974 } 1975 numlist = g_slist_reverse(numlist); 1976 1977 if (numlist != NULL) { 1978 ok = imap_set_message_flags 1979 (session, IMAP_FOLDER_ITEM(msginfo->folder), numlist, IMAP_FLAG_DELETED, NULL, TRUE); 1980 if (ok != MAILIMAP_NO_ERROR) { 1981 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n")); 1982 g_free(destdir); 1983 return ok; 1984 } 1985 } /* else we just need to expunge */ 1986 ok = imap_cmd_expunge(session); 1987 if (ok != MAILIMAP_NO_ERROR) { 1988 log_warning(LOG_PROTOCOL, _("can't expunge\n")); 1989 g_free(destdir); 1990 return ok; 1991 } 1992 1993 session->folder_content_changed = TRUE; 1994 unlock_session(session); 1995 1996 dir = folder_item_get_path(msginfo->folder); 1997 if (is_dir_exist(dir)) { 1998 for (cur = msglist; cur; cur = cur->next) { 1999 msginfo = (MsgInfo *)cur->data; 2000 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum); 2001 } 2002 } 2003 g_free(dir); 2004 2005 g_slist_free(numlist); 2006 2007 imap_scan_required(folder, dest); 2008 2009 g_free(destdir); 2010 2011 return 0; 2012 } 2013 2014 static gint imap_remove_msgs(Folder *folder, FolderItem *dest, 2015 MsgInfoList *msglist, GHashTable *relation) 2016 { 2017 MsgInfo *msginfo; 2018 2019 g_return_val_if_fail(folder != NULL, -1); 2020 g_return_val_if_fail(dest != NULL, -1); 2021 if (msglist == NULL) 2022 return 0; 2023 2024 msginfo = (MsgInfo *)msglist->data; 2025 g_return_val_if_fail(msginfo->folder != NULL, -1); 2026 2027 return imap_do_remove_msgs(folder, dest, msglist, relation); 2028 } 2029 2030 static gint imap_remove_all_msg(Folder *folder, FolderItem *item) 2031 { 2032 GSList *list = folder_item_get_msg_list(item); 2033 gint res = imap_remove_msgs(folder, item, list, NULL); 2034 procmsg_msg_list_free(list); 2035 return res; 2036 } 2037 2038 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, 2039 MsgInfo *msginfo) 2040 { 2041 /* TODO: properly implement this method */ 2042 return FALSE; 2043 } 2044 2045 static gint imap_close(Folder *folder, FolderItem *item) 2046 { 2047 return 0; 2048 } 2049 2050 static gint imap_scan_tree_real(Folder *folder, gboolean subs_only) 2051 { 2052 FolderItem *item = NULL; 2053 IMAPSession *session; 2054 gchar *root_folder = NULL; 2055 2056 g_return_val_if_fail(folder != NULL, -1); 2057 g_return_val_if_fail(folder->account != NULL, -1); 2058 2059 debug_print("getting session...\n"); 2060 session = imap_session_get(folder); 2061 if (!session) { 2062 if (!folder->node) { 2063 folder_tree_destroy(folder); 2064 item = folder_item_new(folder, folder->name, NULL); 2065 item->folder = folder; 2066 folder->node = item->node = g_node_new(item); 2067 } 2068 return -1; 2069 } 2070 2071 if (folder->account->imap_dir && *folder->account->imap_dir) { 2072 gchar *real_path; 2073 int r = MAILIMAP_NO_ERROR; 2074 clist * lep_list; 2075 2076 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;}); 2077 extract_quote(root_folder, '"'); 2078 subst_char(root_folder, 2079 imap_get_path_separator(session, IMAP_FOLDER(folder), 2080 root_folder, &r), 2081 '/'); 2082 if (is_fatal(r)) 2083 return -1; 2084 strtailchomp(root_folder, '/'); 2085 real_path = imap_get_real_path 2086 (session, IMAP_FOLDER(folder), root_folder, &r); 2087 if (is_fatal(r)) { 2088 g_free(real_path); 2089 return -1; 2090 } 2091 debug_print("IMAP root directory: %s\n", real_path); 2092 2093 /* check if root directory exist */ 2094 2095 r = imap_threaded_list(session->folder, "", real_path, 2096 &lep_list); 2097 2098 if (r != MAILIMAP_NO_ERROR) 2099 imap_handle_error(SESSION(session), NULL, r); 2100 2101 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) { 2102 if (!folder->node) { 2103 item = folder_item_new(folder, folder->name, NULL); 2104 item->folder = folder; 2105 folder->node = item->node = g_node_new(item); 2106 } 2107 return -1; 2108 } 2109 mailimap_list_result_free(lep_list); 2110 2111 g_free(real_path); 2112 } 2113 2114 if (folder->node) 2115 item = FOLDER_ITEM(folder->node->data); 2116 2117 if (item && !item->path && root_folder) { 2118 item->path = g_strdup(root_folder); 2119 } 2120 2121 if (!item || ((item->path || root_folder) && 2122 g_strcmp0(item->path, root_folder) != 0)) { 2123 folder_tree_destroy(folder); 2124 item = folder_item_new(folder, folder->name, root_folder); 2125 item->folder = folder; 2126 folder->node = item->node = g_node_new(item); 2127 } 2128 2129 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only); 2130 imap_create_missing_folders(folder); 2131 2132 return 0; 2133 } 2134 2135 static gint imap_scan_tree(Folder *folder) 2136 { 2137 gboolean subs_only = FALSE; 2138 if (folder->account) { 2139 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly); 2140 subs_only = folder->account->imap_subsonly; 2141 } 2142 return imap_scan_tree_real(folder, subs_only); 2143 } 2144 2145 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only) 2146 { 2147 /* reset recursion depth counter */ 2148 session->scan_tree_recurs_depth = 0; 2149 2150 return imap_scan_tree_recursive_dive(session, item, subs_only); 2151 } 2152 2153 static gint imap_scan_tree_recursive_dive(IMAPSession *session, FolderItem *item, gboolean subs_only) 2154 { 2155 Folder *folder; 2156 IMAPFolder *imapfolder; 2157 FolderItem *new_item; 2158 GSList *item_list, *cur; 2159 GNode *node; 2160 gchar *real_path; 2161 gchar *wildcard_path; 2162 gchar separator; 2163 gchar wildcard[3]; 2164 clist * lep_list; 2165 int r = MAILIMAP_NO_ERROR; 2166 2167 g_return_val_if_fail(item != NULL, -1); 2168 g_return_val_if_fail(item->folder != NULL, -1); 2169 g_return_val_if_fail(item->no_sub == FALSE, -1); 2170 2171 /* recursion depth limiter */ 2172 if(session->scan_tree_recurs_depth >= prefs_common.imap_scan_tree_recurs_limit) { 2173 g_warning("IMAP scan tree recursion limit reached (%d, folder '%s')", 2174 prefs_common.imap_scan_tree_recurs_limit, item->folder->name); 2175 return -1; 2176 } 2177 /* entering recursion func: increase depth counter */ 2178 session->scan_tree_recurs_depth++; 2179 2180 folder = item->folder; 2181 imapfolder = IMAP_FOLDER(folder); 2182 2183 separator = imap_get_path_separator(session, imapfolder, item->path, &r); 2184 if (is_fatal(r)) 2185 return r; 2186 2187 if (folder->ui_func) 2188 folder->ui_func(folder, item, folder->ui_func_data); 2189 2190 if (item->path) { 2191 wildcard[0] = separator; 2192 wildcard[1] = '%'; 2193 wildcard[2] = '\0'; 2194 real_path = imap_get_real_path(session, imapfolder, item->path, &r); 2195 if (is_fatal(r)) { 2196 g_free(real_path); 2197 return r; 2198 } 2199 } else { 2200 wildcard[0] = '%'; 2201 wildcard[1] = '\0'; 2202 real_path = g_strdup(""); 2203 } 2204 2205 Xstrcat_a(wildcard_path, real_path, wildcard, 2206 {g_free(real_path); return MAILIMAP_ERROR_BAD_STATE;}); 2207 lep_list = NULL; 2208 2209 if (subs_only) 2210 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list); 2211 else 2212 r = imap_threaded_list(folder, "", wildcard_path, &lep_list); 2213 2214 if (r != MAILIMAP_NO_ERROR) { 2215 imap_handle_error(SESSION(session), NULL, r); 2216 item_list = NULL; 2217 g_free(real_path); 2218 return r; 2219 } 2220 else { 2221 item_list = imap_list_from_lep(imapfolder, 2222 lep_list, real_path, FALSE); 2223 mailimap_list_result_free(lep_list); 2224 } 2225 2226 g_free(real_path); 2227 2228 node = item->node->children; 2229 while (node != NULL) { 2230 FolderItem *old_item = FOLDER_ITEM(node->data); 2231 GNode *next = node->next; 2232 2233 new_item = NULL; 2234 for (cur = item_list; cur != NULL; cur = cur->next) { 2235 FolderItem *cur_item = FOLDER_ITEM(cur->data); 2236 if (!g_strcmp0(old_item->path, cur_item->path)) { 2237 new_item = cur_item; 2238 break; 2239 } 2240 } 2241 if (!new_item) { 2242 if (old_item && old_item->path && !strcasecmp(old_item->path, "INBOX")) { 2243 debug_print("not removing INBOX\n"); 2244 } else { 2245 debug_print("folder '%s' not found. removing...\n", 2246 old_item->path); 2247 folder_item_remove(old_item); 2248 } 2249 } else { 2250 old_item->no_sub = new_item->no_sub; 2251 old_item->no_select = new_item->no_select; 2252 if (old_item->no_sub == TRUE && node->children) { 2253 debug_print("folder '%s' doesn't have " 2254 "subfolders. removing...\n", 2255 old_item->path); 2256 folder_item_remove_children(old_item); 2257 } 2258 } 2259 2260 node = next; 2261 } 2262 2263 for (cur = item_list; cur != NULL; cur = cur->next) { 2264 FolderItem *cur_item = FOLDER_ITEM(cur->data); 2265 new_item = NULL; 2266 2267 for (node = item->node->children; node != NULL; 2268 node = node->next) { 2269 if (!g_strcmp0(FOLDER_ITEM(node->data)->path, 2270 cur_item->path)) { 2271 new_item = FOLDER_ITEM(node->data); 2272 folder_item_destroy(cur_item); 2273 cur_item = NULL; 2274 break; 2275 } 2276 } 2277 if (!new_item) { 2278 new_item = cur_item; 2279 debug_print("new folder '%s' found.\n", new_item->path); 2280 folder_item_append(item, new_item); 2281 } 2282 2283 if (!strcasecmp(new_item->path, "INBOX")) { 2284 new_item->stype = F_INBOX; 2285 folder->inbox = new_item; 2286 } else if (!folder_item_parent(item) || item->stype == F_INBOX) { 2287 gchar *base; 2288 2289 base = g_path_get_basename(new_item->path); 2290 2291 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) { 2292 new_item->stype = F_OUTBOX; 2293 folder->outbox = new_item; 2294 } else if (!folder->draft && (!g_ascii_strcasecmp(base, "Drafts") || !g_ascii_strcasecmp(base, "Draft"))) { 2295 new_item->stype = F_DRAFT; 2296 folder->draft = new_item; 2297 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) { 2298 new_item->stype = F_QUEUE; 2299 folder->queue = new_item; 2300 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) { 2301 new_item->stype = F_TRASH; 2302 folder->trash = new_item; 2303 } 2304 g_free(base); 2305 } 2306 2307 if (new_item->no_sub == FALSE) { 2308 imap_scan_tree_recursive_dive(session, new_item, subs_only); 2309 2310 /* entering recursion func: increase depth counter */ 2311 session->scan_tree_recurs_depth--; 2312 if (session->scan_tree_recurs_depth < 0) 2313 g_error("IMAP scan tree recursion underflow (%d)", 2314 session->scan_tree_recurs_depth); 2315 } 2316 } 2317 2318 g_slist_free(item_list); 2319 2320 return MAILIMAP_NO_ERROR; 2321 } 2322 2323 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive) 2324 { 2325 IMAPSession *session = imap_session_get(folder); 2326 gchar *real_path; 2327 gchar *wildcard_path; 2328 gchar separator; 2329 gchar wildcard[3]; 2330 clist * lep_list; 2331 GSList *item_list = NULL, *cur; 2332 GList *child_list = NULL, *tmplist = NULL; 2333 GSList *sub_list = NULL; 2334 int r = MAILIMAP_NO_ERROR; 2335 2336 if (!session) 2337 return NULL; 2338 2339 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &r); 2340 if (is_fatal(r)) 2341 return NULL; 2342 2343 if (item->path) { 2344 wildcard[0] = separator; 2345 wildcard[1] = '%'; 2346 wildcard[2] = '\0'; 2347 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &r); 2348 if (is_fatal(r)) { 2349 g_free(real_path); 2350 return NULL; 2351 } 2352 } else { 2353 wildcard[0] = '%'; 2354 wildcard[1] = '\0'; 2355 real_path = g_strdup(""); 2356 } 2357 2358 Xstrcat_a(wildcard_path, real_path, wildcard, 2359 {g_free(real_path); return NULL;}); 2360 lep_list = NULL; 2361 2362 if (unsubs_only) 2363 statusbar_print_all(_("Looking for unsubscribed folders in %s..."), 2364 item->path?item->path:item->name); 2365 else 2366 statusbar_print_all(_("Looking for subfolders of %s..."), 2367 item->path?item->path:item->name); 2368 2369 r = imap_threaded_list(folder, "", wildcard_path, &lep_list); 2370 if (r) { 2371 g_free(real_path); 2372 statusbar_pop_all(); 2373 return NULL; 2374 } 2375 item_list = imap_list_from_lep(IMAP_FOLDER(folder), 2376 lep_list, real_path, FALSE); 2377 mailimap_list_result_free(lep_list); 2378 2379 for (cur = item_list; cur != NULL; cur = cur->next) { 2380 FolderItem *cur_item = FOLDER_ITEM(cur->data); 2381 if (recursive) { 2382 tmplist = imap_scan_subtree(folder, cur_item, 2383 unsubs_only, recursive); 2384 if (tmplist) 2385 child_list = g_list_concat(child_list, tmplist); 2386 } 2387 child_list = g_list_prepend(child_list, 2388 imap_get_real_path(session, 2389 IMAP_FOLDER(folder), cur_item->path, &r)); 2390 if (is_fatal(r)) { 2391 g_free(real_path); 2392 statusbar_pop_all(); 2393 return NULL; 2394 } 2395 folder_item_destroy(cur_item); 2396 } 2397 child_list = g_list_reverse(child_list); 2398 g_slist_free(item_list); 2399 2400 if (unsubs_only) { 2401 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list); 2402 if (r) { 2403 g_free(real_path); 2404 statusbar_pop_all(); 2405 return NULL; 2406 } 2407 sub_list = imap_list_from_lep(IMAP_FOLDER(folder), 2408 lep_list, real_path, FALSE); 2409 mailimap_list_result_free(lep_list); 2410 2411 for (cur = sub_list; cur != NULL; cur = cur->next) { 2412 FolderItem *cur_item = FOLDER_ITEM(cur->data); 2413 GList *oldlitem = NULL; 2414 gchar *tmp = imap_get_real_path(session, 2415 IMAP_FOLDER(folder), cur_item->path, &r); 2416 if (r) { 2417 g_free(real_path); 2418 statusbar_pop_all(); 2419 g_free(tmp); 2420 return NULL; 2421 } 2422 folder_item_destroy(cur_item); 2423 oldlitem = g_list_find_custom( 2424 child_list, tmp, (GCompareFunc)g_strcmp0); 2425 if (oldlitem) { 2426 child_list = g_list_remove_link(child_list, oldlitem); 2427 g_free(oldlitem->data); 2428 g_list_free(oldlitem); 2429 } 2430 g_free(tmp); 2431 } 2432 } 2433 2434 g_free(real_path); 2435 statusbar_pop_all(); 2436 2437 return child_list; 2438 } 2439 2440 static gint imap_create_tree(Folder *folder) 2441 { 2442 g_return_val_if_fail(folder != NULL, -1); 2443 g_return_val_if_fail(folder->node != NULL, -1); 2444 g_return_val_if_fail(folder->node->data != NULL, -1); 2445 g_return_val_if_fail(folder->account != NULL, -1); 2446 2447 imap_scan_tree(folder); 2448 imap_create_missing_folders(folder); 2449 2450 return 0; 2451 } 2452 2453 static void imap_create_missing_folders(Folder *folder) 2454 { 2455 g_return_if_fail(folder != NULL); 2456 2457 if (!folder->inbox) 2458 folder->inbox = imap_create_special_folder 2459 (folder, F_INBOX, "INBOX"); 2460 if (!folder->trash) 2461 folder->trash = imap_create_special_folder 2462 (folder, F_TRASH, "Trash"); 2463 if (!folder->queue) 2464 folder->queue = imap_create_special_folder 2465 (folder, F_QUEUE, "Queue"); 2466 if (!folder->outbox) 2467 folder->outbox = imap_create_special_folder 2468 (folder, F_OUTBOX, "Sent"); 2469 if (!folder->draft) 2470 folder->draft = imap_create_special_folder 2471 (folder, F_DRAFT, "Drafts"); 2472 } 2473 2474 static FolderItem *imap_create_special_folder(Folder *folder, 2475 SpecialFolderItemType stype, 2476 const gchar *name) 2477 { 2478 FolderItem *item; 2479 FolderItem *new_item; 2480 2481 g_return_val_if_fail(folder != NULL, NULL); 2482 g_return_val_if_fail(folder->node != NULL, NULL); 2483 g_return_val_if_fail(folder->node->data != NULL, NULL); 2484 g_return_val_if_fail(folder->account != NULL, NULL); 2485 g_return_val_if_fail(name != NULL, NULL); 2486 2487 item = FOLDER_ITEM(folder->node->data); 2488 new_item = imap_create_folder(folder, item, name); 2489 2490 if (!new_item) { 2491 g_warning("can't create '%s'", name); 2492 if (!folder->inbox) return NULL; 2493 2494 new_item = imap_create_folder(folder, folder->inbox, name); 2495 if (!new_item) 2496 g_warning("can't create '%s' under INBOX", name); 2497 else 2498 new_item->stype = stype; 2499 } else 2500 new_item->stype = stype; 2501 2502 return new_item; 2503 } 2504 2505 char *imap_item_get_path(Folder *folder, FolderItem *item) 2506 { 2507 g_return_val_if_fail(folder != NULL, NULL); 2508 g_return_val_if_fail(folder->account != NULL, NULL); 2509 g_return_val_if_fail(item != NULL, NULL); 2510 2511 char path[PATH_MAX]; 2512 2513 char *dir = prefs_account_cache_dir(folder->account->recv_server, folder->account->userid); 2514 if (g_path_is_absolute(dir)) { 2515 if (item->path) 2516 snprintf(path, sizeof(path), "%s/%s", dir, item->path); 2517 else 2518 strlcpy(path, dir, sizeof(path)); 2519 } else { 2520 if (item->path) 2521 snprintf(path, sizeof(path), "%s/%s/%s", get_home_dir(), dir, item->path); 2522 else 2523 snprintf(path, sizeof(path), "%s/%s", get_home_dir(), dir); 2524 } 2525 free(dir); 2526 return strdup(path); 2527 } 2528 2529 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent, 2530 const gchar *name) 2531 { 2532 gchar *dirpath, *imap_path; 2533 IMAPSession *session; 2534 FolderItem *new_item; 2535 gchar separator; 2536 gchar *new_name; 2537 const gchar *p; 2538 gint ok = MAILIMAP_NO_ERROR; 2539 gboolean no_select = FALSE, no_sub = FALSE; 2540 gboolean exist = FALSE; 2541 2542 g_return_val_if_fail(folder != NULL, NULL); 2543 g_return_val_if_fail(folder->account != NULL, NULL); 2544 g_return_val_if_fail(parent != NULL, NULL); 2545 g_return_val_if_fail(name != NULL, NULL); 2546 2547 if (atoi(name) > 0) { 2548 gchar *cached_msg = imap_get_cached_filename(parent, atoi(name)); 2549 if (is_file_exist(cached_msg)) { 2550 if (unlink(cached_msg) != 0) { 2551 g_free(cached_msg); 2552 return NULL; 2553 } 2554 } 2555 g_free(cached_msg); 2556 } 2557 2558 debug_print("getting session...\n"); 2559 session = imap_session_get(folder); 2560 if (!session) { 2561 return NULL; 2562 } 2563 2564 if (!folder_item_parent(parent) && strcasecmp(name, "INBOX") == 0) { 2565 dirpath = g_strdup(name); 2566 }else if (parent->path) 2567 dirpath = g_strconcat(parent->path, "/", name, NULL); 2568 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0') 2569 dirpath = g_strdup(name); 2570 else if (folder->account->imap_dir && *folder->account->imap_dir) { 2571 gchar *imap_dir; 2572 2573 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;}); 2574 strtailchomp(imap_dir, '/'); 2575 dirpath = g_strconcat(imap_dir, "/", name, NULL); 2576 } else 2577 dirpath = g_strdup(name); 2578 2579 2580 2581 /* keep trailing directory separator to create a folder that contains 2582 sub folder */ 2583 imap_path = imap_utf8_to_modified_utf7(dirpath, FALSE); 2584 2585 strtailchomp(dirpath, '/'); 2586 Xstrdup_a(new_name, name, { 2587 g_free(dirpath); 2588 g_free(imap_path); 2589 return NULL;}); 2590 2591 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path, &ok); 2592 if (is_fatal(ok)) { 2593 g_free(dirpath); 2594 g_free(imap_path); 2595 return NULL; 2596 } 2597 imap_path_separator_subst(imap_path, separator); 2598 /* remove trailing / for display */ 2599 strtailchomp(new_name, '/'); 2600 2601 if (strcasecmp(dirpath, "INBOX") != 0) { 2602 int r; 2603 clist * lep_list; 2604 2605 r = imap_threaded_list(folder, "", imap_path, &lep_list); 2606 if (r != MAILIMAP_NO_ERROR) { 2607 imap_handle_error(SESSION(session), NULL, r); 2608 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n")); 2609 g_free(imap_path); 2610 g_free(dirpath); 2611 return NULL; 2612 } 2613 2614 if (clist_count(lep_list) > 0) 2615 exist = TRUE; 2616 mailimap_list_result_free(lep_list); 2617 lep_list = NULL; 2618 if (!exist) { 2619 ok = imap_cmd_create(session, imap_path); 2620 if (ok != MAILIMAP_NO_ERROR) { 2621 log_warning(LOG_PROTOCOL, _("can't create mailbox\n")); 2622 g_free(imap_path); 2623 g_free(dirpath); 2624 return NULL; 2625 } 2626 r = imap_threaded_list(folder, "", imap_path, &lep_list); 2627 if (r == MAILIMAP_NO_ERROR) { 2628 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder), 2629 lep_list, dirpath, TRUE); 2630 if (item_list) { 2631 FolderItem *cur_item = FOLDER_ITEM(item_list->data); 2632 no_select = cur_item->no_select; 2633 no_sub = cur_item->no_sub; 2634 g_slist_free(item_list); 2635 } 2636 mailimap_list_result_free(lep_list); 2637 } else { 2638 imap_handle_error(SESSION(session), NULL, r); 2639 } 2640 } 2641 imap_threaded_subscribe(folder, imap_path, TRUE); 2642 } else { 2643 clist *lep_list; 2644 int r; 2645 /* just get flags */ 2646 r = imap_threaded_list(folder, "", "INBOX", &lep_list); 2647 if (r == MAILIMAP_NO_ERROR) { 2648 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder), 2649 lep_list, dirpath, TRUE); 2650 if (item_list) { 2651 FolderItem *cur_item = FOLDER_ITEM(item_list->data); 2652 no_select = cur_item->no_select; 2653 no_sub = cur_item->no_sub; 2654 g_slist_free(item_list); 2655 } 2656 mailimap_list_result_free(lep_list); 2657 } else { 2658 imap_handle_error(SESSION(session), NULL, r); 2659 } 2660 } 2661 2662 new_item = folder_item_new(folder, new_name, dirpath); 2663 new_item->no_select = no_select; 2664 new_item->no_sub = no_sub; 2665 folder_item_append(parent, new_item); 2666 g_free(imap_path); 2667 g_free(dirpath); 2668 2669 dirpath = folder_item_get_path(new_item); 2670 if (!is_dir_exist(dirpath)) 2671 make_dir_hier(dirpath); 2672 g_free(dirpath); 2673 2674 if (exist) { 2675 /* folder existed, scan it */ 2676 imap_scan_required(folder, new_item); 2677 folder_item_scan_full(new_item, FALSE); 2678 } 2679 2680 return new_item; 2681 } 2682 2683 static gint imap_rename_folder(Folder *folder, FolderItem *item, 2684 const gchar *name) 2685 { 2686 gchar *dirpath; 2687 gchar *newpath; 2688 gchar *real_oldpath; 2689 gchar *real_newpath; 2690 gchar *paths[2]; 2691 gchar *old_cache_dir; 2692 gchar *new_cache_dir; 2693 IMAPSession *session; 2694 gchar separator; 2695 gint ok = MAILIMAP_NO_ERROR; 2696 gint exists, recent, unseen; 2697 guint32 uid_validity; 2698 2699 g_return_val_if_fail(folder != NULL, -1); 2700 g_return_val_if_fail(item != NULL, -1); 2701 g_return_val_if_fail(item->path != NULL, -1); 2702 g_return_val_if_fail(name != NULL, -1); 2703 2704 debug_print("getting session...\n"); 2705 session = imap_session_get(folder); 2706 if (!session) { 2707 return -1; 2708 } 2709 2710 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &ok)) != NULL || 2711 is_fatal(ok)) { 2712 g_warning("new folder name must not contain the namespace path separator"); 2713 return -1; 2714 } 2715 2716 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &ok); 2717 if (is_fatal(ok)) { 2718 g_free(real_oldpath); 2719 return -1; 2720 } 2721 2722 g_free(session->mbox); 2723 session->mbox = NULL; 2724 session->exists = 0; 2725 session->recent = 0; 2726 session->expunge = 0; 2727 ok = imap_cmd_examine(session, "INBOX", 2728 &exists, &recent, &unseen, &uid_validity, FALSE); 2729 if (ok != MAILIMAP_NO_ERROR) { 2730 g_free(real_oldpath); 2731 return -1; 2732 } 2733 2734 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &ok); 2735 if (is_fatal(ok)) { 2736 g_free(real_oldpath); 2737 return -1; 2738 } 2739 if (strchr(item->path, '/')) { 2740 dirpath = g_path_get_dirname(item->path); 2741 newpath = g_strconcat(dirpath, "/", name, NULL); 2742 g_free(dirpath); 2743 } else 2744 newpath = g_strdup(name); 2745 2746 real_newpath = imap_utf8_to_modified_utf7(newpath, FALSE); 2747 imap_path_separator_subst(real_newpath, separator); 2748 2749 ok = imap_cmd_rename(session, real_oldpath, real_newpath); 2750 if (ok != MAILIMAP_NO_ERROR) { 2751 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"), 2752 real_oldpath, real_newpath); 2753 g_free(real_oldpath); 2754 g_free(newpath); 2755 g_free(real_newpath); 2756 return -1; 2757 } 2758 g_free(item->name); 2759 item->name = g_strdup(name); 2760 2761 old_cache_dir = folder_item_get_path(item); 2762 2763 paths[0] = g_strdup(item->path); 2764 paths[1] = newpath; 2765 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1, 2766 imap_rename_folder_func, paths); 2767 2768 if (is_dir_exist(old_cache_dir)) { 2769 new_cache_dir = folder_item_get_path(item); 2770 if (g_rename(old_cache_dir, new_cache_dir) < 0) { 2771 FILE_OP_ERROR(old_cache_dir, "rename"); 2772 } 2773 g_free(new_cache_dir); 2774 } 2775 2776 g_free(old_cache_dir); 2777 g_free(paths[0]); 2778 g_free(newpath); 2779 g_free(real_oldpath); 2780 g_free(real_newpath); 2781 return 0; 2782 } 2783 2784 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub) 2785 { 2786 gchar *path; 2787 gint r = MAILIMAP_NO_ERROR; 2788 IMAPSession *session; 2789 debug_print("getting session...\n"); 2790 2791 session = imap_session_get(folder); 2792 if (!session) { 2793 return -1; 2794 } 2795 if (item && item->path) { 2796 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &r); 2797 if (!path) 2798 return -1; 2799 if (is_fatal(r)) { 2800 g_free(path); 2801 return -1; 2802 } 2803 if (!strcasecmp(path, "INBOX") && sub == FALSE) { 2804 g_free(path); 2805 return -1; 2806 } 2807 debug_print("%ssubscribing %s\n", sub?"":"un", path); 2808 r = imap_threaded_subscribe(folder, path, sub); 2809 g_free(path); 2810 } else if (rpath) { 2811 r = imap_threaded_subscribe(folder, rpath, sub); 2812 } else 2813 return -1; 2814 return r; 2815 } 2816 2817 static gint imap_remove_folder_real(Folder *folder, FolderItem *item) 2818 { 2819 gint ok = MAILIMAP_NO_ERROR; 2820 IMAPSession *session; 2821 gchar *path; 2822 gchar *cache_dir; 2823 gboolean selected_folder; 2824 2825 g_return_val_if_fail(folder != NULL, -1); 2826 g_return_val_if_fail(item != NULL, -1); 2827 g_return_val_if_fail(item->path != NULL, -1); 2828 2829 debug_print("getting session...\n"); 2830 session = imap_session_get(folder); 2831 if (!session) { 2832 return -1; 2833 } 2834 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &ok); 2835 if (is_fatal(ok)) { 2836 g_free(path); 2837 return -1; 2838 } 2839 imap_threaded_subscribe(folder, path, FALSE); 2840 2841 selected_folder = (session->mbox != NULL) && 2842 (!strcmp(session->mbox, item->path)); 2843 if (selected_folder) { 2844 ok = imap_cmd_close(session); 2845 if (ok != MAILIMAP_NO_ERROR) { 2846 debug_print("close err %d\n", ok); 2847 return ok; 2848 } 2849 } 2850 ok = imap_cmd_delete(session, path); 2851 if (ok != MAILIMAP_NO_ERROR && !is_fatal(ok)) { 2852 gchar *tmp = NULL; 2853 2854 ok = MAILIMAP_NO_ERROR; 2855 tmp = g_strdup_printf("%s%c", path, 2856 imap_get_path_separator(session, IMAP_FOLDER(folder), path, &ok)); 2857 g_free(path); 2858 path = tmp; 2859 if (!is_fatal(ok)) 2860 ok = imap_cmd_delete(session, path); 2861 } 2862 2863 if (ok != MAILIMAP_NO_ERROR) { 2864 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n")); 2865 g_free(path); 2866 return -1; 2867 } 2868 2869 g_free(path); 2870 cache_dir = folder_item_get_path(item); 2871 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0) 2872 g_warning("can't remove directory '%s'", cache_dir); 2873 g_free(cache_dir); 2874 folder_item_remove(item); 2875 return 0; 2876 } 2877 2878 static gint imap_remove_folder(Folder *folder, FolderItem *item) 2879 { 2880 GNode *node, *next; 2881 2882 g_return_val_if_fail(item != NULL, -1); 2883 g_return_val_if_fail(item->folder != NULL, -1); 2884 g_return_val_if_fail(item->node != NULL, -1); 2885 2886 node = item->node->children; 2887 while (node != NULL) { 2888 next = node->next; 2889 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0) 2890 return -1; 2891 node = next; 2892 } 2893 debug_print("IMAP removing %s\n", item->path); 2894 2895 if (imap_remove_all_msg(folder, item) < 0) 2896 return -1; 2897 return imap_remove_folder_real(folder, item); 2898 } 2899 2900 typedef struct _uncached_data { 2901 IMAPSession *session; 2902 FolderItem *item; 2903 MsgNumberList *numlist; 2904 guint cur; 2905 guint total; 2906 gboolean done; 2907 int ok; 2908 } uncached_data; 2909 2910 static void *imap_get_uncached_messages_thread(void *data) 2911 { 2912 uncached_data *stuff = (uncached_data *)data; 2913 IMAPSession *session = stuff->session; 2914 FolderItem *item = stuff->item; 2915 MsgNumberList *numlist = stuff->numlist; 2916 GSList *newlist = NULL; 2917 GSList *llast = NULL; 2918 GSList *seq_list, *cur; 2919 2920 debug_print("uncached_messages\n"); 2921 2922 if (session == NULL || item == NULL || item->folder == NULL 2923 || FOLDER_CLASS(item->folder) != &imap_class) { 2924 stuff->done = TRUE; 2925 return NULL; 2926 } 2927 2928 seq_list = imap_get_lep_set_from_numlist(IMAP_FOLDER(item->folder), numlist); 2929 debug_print("get msgs info\n"); 2930 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) { 2931 struct mailimap_set * imapset; 2932 unsigned int i; 2933 int r; 2934 carray * env_list; 2935 2936 if (session->cancelled) 2937 break; 2938 2939 imapset = cur->data; 2940 2941 r = imap_threaded_fetch_env(session->folder, 2942 imapset, &env_list); 2943 if (r != MAILIMAP_NO_ERROR) { 2944 imap_handle_error(SESSION(session), NULL, r); 2945 if (is_fatal(r)) { 2946 stuff->ok = r; 2947 return NULL; 2948 } 2949 continue; 2950 } 2951 2952 session_set_access_time(SESSION(session)); 2953 2954 for(i = 0 ; i < carray_count(env_list) ; i += 2) { 2955 struct imap_fetch_env_info * info; 2956 MsgInfo * msginfo; 2957 GSList *tags = NULL; 2958 info = carray_get(env_list, i); 2959 tags = carray_get(env_list, i+1); 2960 msginfo = imap_envelope_from_lep(info, item); 2961 if (msginfo == NULL) { 2962 slist_free_strings_full(tags); 2963 continue; 2964 } 2965 g_slist_free(msginfo->tags); 2966 msginfo->tags = NULL; 2967 2968 msginfo->folder = item; 2969 if (!newlist) 2970 llast = newlist = g_slist_append(newlist, msginfo); 2971 else { 2972 llast = g_slist_append(llast, msginfo); 2973 llast = llast->next; 2974 } 2975 } 2976 imap_fetch_env_free(env_list); 2977 } 2978 2979 imap_lep_set_free(seq_list); 2980 2981 session_set_access_time(SESSION(session)); 2982 stuff->done = TRUE; 2983 return newlist; 2984 } 2985 2986 #define MAX_MSG_NUM 50 2987 2988 static GSList *imap_get_uncached_messages(IMAPSession *session, 2989 FolderItem *item, 2990 MsgNumberList *numlist, 2991 int *r) 2992 { 2993 GSList *result = NULL; 2994 GSList * cur; 2995 uncached_data *data = g_new0(uncached_data, 1); 2996 2997 cur = numlist; 2998 data->total = g_slist_length(numlist); 2999 data->ok = MAILIMAP_NO_ERROR; 3000 debug_print("messages list : %i\n", data->total); 3001 3002 while (cur != NULL) { 3003 GSList * partial_result; 3004 int count; 3005 GSList * newlist; 3006 GSList * llast; 3007 3008 llast = NULL; 3009 count = 0; 3010 newlist = NULL; 3011 while (count < MAX_MSG_NUM) { 3012 void * p; 3013 3014 p = cur->data; 3015 3016 if (newlist == NULL) 3017 llast = newlist = g_slist_append(newlist, p); 3018 else { 3019 llast = g_slist_append(llast, p); 3020 llast = llast->next; 3021 } 3022 count ++; 3023 3024 cur = cur->next; 3025 if (cur == NULL) 3026 break; 3027 } 3028 3029 data->done = FALSE; 3030 data->session = session; 3031 data->item = item; 3032 data->numlist = newlist; 3033 data->cur += count; 3034 3035 if (prefs_common.work_offline && 3036 !inc_offline_should_override(FALSE, 3037 _("Claws Mail needs network access in order " 3038 "to access the IMAP server."))) { 3039 g_free(data); 3040 return NULL; 3041 } 3042 3043 partial_result = 3044 (GSList *)imap_get_uncached_messages_thread(data); 3045 *r = data->ok; 3046 if (data->ok != MAILIMAP_NO_ERROR) { 3047 goto bail; 3048 } 3049 statusbar_progress_all(data->cur,data->total, 1); 3050 3051 g_slist_free(newlist); 3052 3053 result = g_slist_concat(result, partial_result); 3054 } 3055 bail: 3056 g_free(data); 3057 3058 statusbar_progress_all(0,0,0); 3059 statusbar_pop_all(); 3060 3061 return result; 3062 } 3063 3064 static void imap_delete_all_cached_messages(FolderItem *item) 3065 { 3066 gchar *dir; 3067 3068 g_return_if_fail(item != NULL); 3069 g_return_if_fail(item->folder != NULL); 3070 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class); 3071 3072 debug_print("Deleting all cached messages...\n"); 3073 3074 dir = folder_item_get_path(item); 3075 if (is_dir_exist(dir)) 3076 remove_all_numbered_files(dir); 3077 g_free(dir); 3078 3079 debug_print("Deleting all cached messages done.\n"); 3080 } 3081 3082 gchar imap_get_path_separator_for_item(FolderItem *item) 3083 { 3084 Folder *folder = NULL; 3085 IMAPFolder *imap_folder = NULL; 3086 IMAPSession *session = NULL; 3087 gchar result = '/'; 3088 gint ok = MAILIMAP_NO_ERROR; 3089 if (!item) 3090 return '/'; 3091 folder = item->folder; 3092 3093 if (!folder) 3094 return '/'; 3095 3096 imap_folder = IMAP_FOLDER(folder); 3097 3098 debug_print("getting session...\n"); 3099 session = imap_session_get(FOLDER(folder)); 3100 result = imap_get_path_separator(session, imap_folder, item->path, &ok); 3101 return result; 3102 } 3103 3104 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder, gint *ok) 3105 { 3106 clist * lep_list; 3107 int r; 3108 gchar separator = '\0'; 3109 3110 g_return_val_if_fail(session != NULL, '/'); 3111 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list); 3112 3113 if (r != MAILIMAP_NO_ERROR) { 3114 imap_handle_error(SESSION(session), NULL, r); 3115 log_warning(LOG_PROTOCOL, _("LIST failed\n")); 3116 *ok = r; 3117 return '\0'; 3118 } 3119 3120 if (lep_list != NULL && clist_count(lep_list) > 0) { 3121 clistiter * iter = clist_begin(lep_list); 3122 struct mailimap_mailbox_list * mb; 3123 mb = clist_content(iter); 3124 3125 separator = mb->mb_delimiter; 3126 debug_print("got separator: %c\n", folder->last_seen_separator); 3127 } 3128 *ok = MAILIMAP_NO_ERROR; 3129 mailimap_list_result_free(lep_list); 3130 return separator; 3131 } 3132 3133 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path, gint *ok) 3134 { 3135 gchar separator = '/'; 3136 *ok = MAILIMAP_NO_ERROR; 3137 if (folder->last_seen_separator == 0) { 3138 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "", ok); 3139 } 3140 3141 if (folder->last_seen_separator == 0) { 3142 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX", ok); 3143 } 3144 3145 if (folder->last_seen_separator != 0) { 3146 debug_print("using separator: %c\n", folder->last_seen_separator); 3147 return folder->last_seen_separator; 3148 } 3149 3150 return separator; 3151 } 3152 3153 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path, gint *ok) 3154 { 3155 gchar *real_path = NULL; 3156 gchar separator; 3157 3158 g_return_val_if_fail(folder != NULL, NULL); 3159 g_return_val_if_fail(path != NULL, NULL); 3160 3161 *ok = MAILIMAP_NO_ERROR; 3162 3163 real_path = imap_utf8_to_modified_utf7(path, FALSE); 3164 separator = imap_get_path_separator(session, folder, path, ok); 3165 if (*ok == MAILIMAP_NO_ERROR) 3166 imap_path_separator_subst(real_path, separator); 3167 3168 return real_path; 3169 } 3170 3171 static gint imap_set_message_flags(IMAPSession *session, 3172 IMAPFolderItem *item, 3173 MsgNumberList *numlist, 3174 IMAPFlags flags, 3175 GSList *tags, 3176 gboolean is_set) 3177 { 3178 gint ok = 0; 3179 GSList *seq_list; 3180 GSList * cur; 3181 gint total = 0; 3182 IMAPFolder *folder = NULL; 3183 GSList *sorted_list = NULL; 3184 3185 if (numlist == NULL || session == NULL) 3186 return MAILIMAP_ERROR_BAD_STATE; 3187 3188 folder = IMAP_FOLDER(session->folder); 3189 3190 sorted_list = g_slist_copy(numlist); 3191 sorted_list = g_slist_sort(sorted_list, g_int_compare); 3192 3193 cur = g_slist_last(sorted_list); 3194 3195 if (cur) 3196 total = GPOINTER_TO_INT(cur->data); 3197 3198 seq_list = imap_get_lep_set_from_numlist(IMAP_FOLDER(session->folder), sorted_list); 3199 3200 statusbar_print_all(_("Flagging messages...")); 3201 3202 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) { 3203 struct mailimap_set * imapset = (struct mailimap_set *)cur->data; 3204 struct mailimap_set_item *set_item = NULL; 3205 3206 if (imapset->set_list) 3207 set_item = clist_content(clist_begin(imapset->set_list)); 3208 else 3209 continue; 3210 3211 if (set_item == NULL) 3212 continue; 3213 3214 statusbar_progress_all(set_item->set_first, total, 1); 3215 3216 ok = imap_cmd_store(session, item, imapset, 3217 flags, tags, is_set); 3218 statusbar_progress_all(set_item->set_last, total, 1); 3219 if (ok != MAILIMAP_NO_ERROR && folder->max_set_size > 20) { 3220 /* reduce max set size */ 3221 folder->max_set_size /= 2; 3222 } 3223 if (ok != MAILIMAP_NO_ERROR && is_fatal(ok)) { 3224 break; 3225 } 3226 } 3227 3228 g_slist_free(sorted_list); 3229 3230 statusbar_progress_all(0,0,0); 3231 statusbar_pop_all(); 3232 3233 imap_lep_set_free(seq_list); 3234 3235 return ok; 3236 } 3237 3238 typedef struct _select_data { 3239 IMAPSession *session; 3240 gchar *real_path; 3241 gint *exists; 3242 gint *recent; 3243 gint *unseen; 3244 guint32 *uid_validity; 3245 gboolean done; 3246 } select_data; 3247 3248 static gint imap_select(IMAPSession *session, IMAPFolder *folder, 3249 FolderItem *item, 3250 gint *exists, gint *recent, gint *unseen, 3251 guint32 *uid_validity, gint *can_create_flags, 3252 gboolean block) 3253 { 3254 gchar *real_path; 3255 gint ok = MAILIMAP_NO_ERROR; 3256 gint exists_, recent_, unseen_; 3257 guint32 uid_validity_; 3258 gint can_create_flags_; 3259 const gchar *path = item ? item->path:NULL; 3260 3261 if (!item) { 3262 return MAILIMAP_ERROR_BAD_STATE; 3263 } 3264 3265 if (!exists && !recent && !unseen && !uid_validity && !can_create_flags) { 3266 if (session->mbox && strcmp(session->mbox, path) == 0) 3267 return MAILIMAP_NO_ERROR; 3268 } 3269 if (!exists && !recent && !unseen && !uid_validity && can_create_flags) { 3270 if (session->mbox && strcmp(session->mbox, path) == 0) { 3271 if (IMAP_FOLDER_ITEM(item)->can_create_flags != ITEM_CAN_CREATE_FLAGS_UNKNOWN) 3272 return MAILIMAP_NO_ERROR; 3273 } 3274 } 3275 if (!exists) 3276 exists = &exists_; 3277 if (!recent) 3278 recent = &recent_; 3279 if (!unseen) 3280 unseen = &unseen_; 3281 if (!uid_validity) 3282 uid_validity = &uid_validity_; 3283 if (!can_create_flags) 3284 can_create_flags = &can_create_flags_; 3285 3286 g_free(session->mbox); 3287 session->mbox = NULL; 3288 session->exists = 0; 3289 session->recent = 0; 3290 session->expunge = 0; 3291 3292 real_path = imap_get_real_path(session, folder, path, &ok); 3293 if (is_fatal(ok)) { 3294 g_free(real_path); 3295 return ok; 3296 } 3297 g_slist_free(IMAP_FOLDER_ITEM(item)->ok_flags); 3298 IMAP_FOLDER_ITEM(item)->ok_flags = NULL; 3299 ok = imap_cmd_select(session, real_path, 3300 exists, recent, unseen, uid_validity, can_create_flags, 3301 &(IMAP_FOLDER_ITEM(item)->ok_flags), block); 3302 if (ok != MAILIMAP_NO_ERROR) { 3303 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path); 3304 } else { 3305 session->mbox = g_strdup(path); 3306 session->folder_content_changed = FALSE; 3307 session->exists = *exists; 3308 session->recent = *recent; 3309 session->expunge = 0; 3310 session->unseen = *unseen; 3311 session->uid_validity = *uid_validity; 3312 debug_print("select: exists %d recent %d expunge %d uid_validity %d can_create_flags %d\n", 3313 session->exists, session->recent, session->expunge, 3314 session->uid_validity, *can_create_flags); 3315 } 3316 if (*can_create_flags) { 3317 IMAP_FOLDER_ITEM(item)->can_create_flags = ITEM_CAN_CREATE_FLAGS; 3318 } else { 3319 IMAP_FOLDER_ITEM(item)->can_create_flags = ITEM_CANNOT_CREATE_FLAGS; 3320 } 3321 g_free(real_path); 3322 3323 return ok; 3324 } 3325 3326 static gint imap_status(IMAPSession *session, IMAPFolder *folder, 3327 const gchar *path, IMAPFolderItem *item, 3328 gint *messages, 3329 guint32 *uid_next, guint32 *uid_validity, 3330 gint *unseen, gboolean block) 3331 { 3332 int r = MAILIMAP_NO_ERROR; 3333 clistiter * iter; 3334 struct mailimap_mailbox_data_status * data_status; 3335 int got_values; 3336 gchar *real_path; 3337 guint mask = 0; 3338 3339 real_path = imap_get_real_path(session, folder, path, &r); 3340 if (is_fatal(r)) { 3341 g_free(real_path); 3342 return r; 3343 } 3344 if (messages) { 3345 mask |= 1 << 0; 3346 *messages = 0; 3347 } 3348 if (uid_next) { 3349 mask |= 1 << 2; 3350 *uid_next = 0; 3351 } 3352 if (uid_validity) { 3353 mask |= 1 << 3; 3354 *uid_validity = 0; 3355 } 3356 if (unseen) { 3357 mask |= 1 << 4; 3358 *unseen = 0; 3359 } 3360 3361 if (session->mbox != NULL && 3362 !strcmp(session->mbox, item->item.path)) { 3363 r = imap_cmd_close(session); 3364 if (r != MAILIMAP_NO_ERROR) { 3365 debug_print("close err %d\n", r); 3366 g_free(real_path); 3367 return r; 3368 } 3369 } 3370 3371 r = imap_threaded_status(FOLDER(folder), real_path, 3372 &data_status, mask); 3373 3374 g_free(real_path); 3375 if (r != MAILIMAP_NO_ERROR) { 3376 imap_handle_error(SESSION(session), NULL, r); 3377 debug_print("status err %d\n", r); 3378 return r; 3379 } 3380 3381 if (data_status == NULL || data_status->st_info_list == NULL) { 3382 debug_print("data_status %p\n", data_status); 3383 if (data_status) { 3384 debug_print("data_status->st_info_list %p\n", data_status->st_info_list); 3385 mailimap_mailbox_data_status_free(data_status); 3386 } 3387 return MAILIMAP_ERROR_BAD_STATE; 3388 } 3389 3390 got_values = 0; 3391 if (data_status->st_info_list) { 3392 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ; 3393 iter = clist_next(iter)) { 3394 struct mailimap_status_info * info; 3395 3396 info = clist_content(iter); 3397 switch (info->st_att) { 3398 case MAILIMAP_STATUS_ATT_MESSAGES: 3399 if (messages) { 3400 * messages = info->st_value; 3401 got_values |= 1 << 0; 3402 } 3403 break; 3404 3405 case MAILIMAP_STATUS_ATT_UIDNEXT: 3406 if (uid_next) { 3407 * uid_next = info->st_value; 3408 got_values |= 1 << 2; 3409 } 3410 break; 3411 3412 case MAILIMAP_STATUS_ATT_UIDVALIDITY: 3413 if (uid_validity) { 3414 * uid_validity = info->st_value; 3415 got_values |= 1 << 3; 3416 } 3417 break; 3418 3419 case MAILIMAP_STATUS_ATT_UNSEEN: 3420 if (unseen) { 3421 * unseen = info->st_value; 3422 got_values |= 1 << 4; 3423 } 3424 break; 3425 } 3426 } 3427 } 3428 mailimap_mailbox_data_status_free(data_status); 3429 3430 if (got_values != mask) { 3431 g_warning("status: incomplete values received (%d)", got_values); 3432 } 3433 return MAILIMAP_NO_ERROR; 3434 } 3435 3436 static void imap_free_capabilities(IMAPSession *session) 3437 { 3438 slist_free_strings_full(session->capability); 3439 session->capability = NULL; 3440 } 3441 3442 /* low-level IMAP4rev1 commands */ 3443 3444 static gint imap_cmd_login(IMAPSession *session, 3445 const gchar *user, const gchar *pass, 3446 const gchar *type) 3447 { 3448 int r; 3449 gint ok; 3450 3451 if (!strcmp(type, "plaintext") && imap_has_capability(session, "LOGINDISABLED")) { 3452 ok = MAILIMAP_ERROR_BAD_STATE; 3453 if (imap_has_capability(session, "STARTTLS")) { 3454 log_warning(LOG_PROTOCOL, _("Server requires STARTTLS to log in.\n")); 3455 ok = imap_cmd_starttls(session); 3456 if (ok != MAILIMAP_NO_ERROR) { 3457 log_warning(LOG_PROTOCOL, _("Can't start STARTTLS session.\n")); 3458 return ok; 3459 } else { 3460 /* refresh capas */ 3461 imap_free_capabilities(session); 3462 if ((r = imap_get_capabilities(session)) != MAILIMAP_NO_ERROR) { 3463 imap_handle_error(SESSION(session), NULL, r); 3464 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n")); 3465 return r; 3466 } 3467 } 3468 } else { 3469 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n")); 3470 return MAILIMAP_ERROR_LOGIN; 3471 } 3472 } 3473 3474 log_print(LOG_PROTOCOL, "IMAP> Logging %s to %s using %s\n", 3475 user, 3476 SESSION(session)->server, 3477 type); 3478 r = imap_threaded_login(session->folder, user, pass, type); 3479 if (r != MAILIMAP_NO_ERROR) { 3480 imap_handle_error(SESSION(session), NULL, r); 3481 log_print(LOG_PROTOCOL, "IMAP< Error logging in to %s\n", 3482 SESSION(session)->server); 3483 ok = r; 3484 } else { 3485 log_print(LOG_PROTOCOL, "IMAP< Login to %s successful\n", 3486 SESSION(session)->server); 3487 ok = MAILIMAP_NO_ERROR; 3488 } 3489 return ok; 3490 } 3491 3492 static gint imap_cmd_noop(IMAPSession *session) 3493 { 3494 int r; 3495 unsigned int exists, recent, expunge, unseen, uidnext, uidval; 3496 3497 r = imap_threaded_noop(session->folder, &exists, &recent, &expunge, &unseen, &uidnext, &uidval); 3498 if (r != MAILIMAP_NO_ERROR) { 3499 imap_handle_error(SESSION(session), NULL, r); 3500 debug_print("noop err %d\n", r); 3501 return r; 3502 } 3503 3504 if ((exists && exists != session->exists) 3505 || (recent && recent != session->recent) 3506 || (expunge && expunge != session->expunge) 3507 || (unseen && unseen != session->unseen)) { 3508 session->folder_content_changed = TRUE; 3509 } 3510 if (uidnext != 0 && uidnext != session->uid_next) { 3511 session->uid_next = uidnext; 3512 session->folder_content_changed = TRUE; 3513 } 3514 if (uidval != 0 && uidval != session->uid_validity) { 3515 session->uid_validity = uidval; 3516 session->folder_content_changed = TRUE; 3517 } 3518 3519 session->exists = exists; 3520 session->recent = recent; 3521 session->expunge = expunge; 3522 session->unseen = unseen; 3523 3524 session_set_access_time(SESSION(session)); 3525 3526 return MAILIMAP_NO_ERROR; 3527 } 3528 3529 static gint imap_cmd_starttls(IMAPSession *session) 3530 { 3531 int r; 3532 3533 r = imap_threaded_starttls(session->folder, 3534 SESSION(session)->server, SESSION(session)->port); 3535 if (r != MAILIMAP_NO_ERROR) { 3536 imap_handle_error(SESSION(session), NULL, r); 3537 debug_print("STARTTLS err %d\n", r); 3538 return r; 3539 } 3540 return MAILIMAP_NO_ERROR; 3541 } 3542 3543 static gint imap_cmd_select(IMAPSession *session, const gchar *folder, 3544 gint *exists, gint *recent, gint *unseen, 3545 guint32 *uid_validity, gint *can_create_flags, 3546 GSList **ok_flags, gboolean block) 3547 { 3548 int r; 3549 3550 r = imap_threaded_select(session->folder, folder, 3551 exists, recent, unseen, uid_validity, can_create_flags, ok_flags); 3552 if (r != MAILIMAP_NO_ERROR) { 3553 imap_handle_error(SESSION(session), NULL, r); 3554 debug_print("select err %d\n", r); 3555 return r; 3556 } 3557 return MAILIMAP_NO_ERROR; 3558 } 3559 3560 static gint imap_cmd_close(IMAPSession *session) 3561 { 3562 int r; 3563 3564 r = imap_threaded_close(session->folder); 3565 if (r != MAILIMAP_NO_ERROR) { 3566 imap_handle_error(SESSION(session), NULL, r); 3567 debug_print("close err %d\n", r); 3568 return r; 3569 } 3570 g_free(session->mbox); 3571 session->mbox = NULL; 3572 session->exists = 0; 3573 session->recent = 0; 3574 session->expunge = 0; 3575 return MAILIMAP_NO_ERROR; 3576 } 3577 3578 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder, 3579 gint *exists, gint *recent, gint *unseen, 3580 guint32 *uid_validity, gboolean block) 3581 { 3582 int r; 3583 3584 r = imap_threaded_examine(session->folder, folder, 3585 exists, recent, unseen, uid_validity); 3586 if (r != MAILIMAP_NO_ERROR) { 3587 imap_handle_error(SESSION(session), NULL, r); 3588 debug_print("examine err %d\n", r); 3589 3590 return r; 3591 } 3592 return MAILIMAP_NO_ERROR; 3593 } 3594 3595 static gint imap_cmd_create(IMAPSession *session, const gchar *folder) 3596 { 3597 int r; 3598 3599 r = imap_threaded_create(session->folder, folder); 3600 if (r != MAILIMAP_NO_ERROR) { 3601 imap_handle_error(SESSION(session), NULL, r); 3602 return r; 3603 } 3604 3605 return MAILIMAP_NO_ERROR; 3606 } 3607 3608 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder, 3609 const gchar *new_folder) 3610 { 3611 int r; 3612 3613 r = imap_threaded_rename(session->folder, old_folder, 3614 new_folder); 3615 if (r != MAILIMAP_NO_ERROR) { 3616 imap_handle_error(SESSION(session), NULL, r); 3617 return r; 3618 } 3619 3620 return MAILIMAP_NO_ERROR; 3621 } 3622 3623 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder) 3624 { 3625 int r; 3626 3627 3628 r = imap_threaded_delete(session->folder, folder); 3629 if (r != MAILIMAP_NO_ERROR) { 3630 imap_handle_error(SESSION(session), NULL, r); 3631 return r; 3632 } 3633 3634 return MAILIMAP_NO_ERROR; 3635 } 3636 3637 typedef struct _fetch_data { 3638 IMAPSession *session; 3639 guint32 uid; 3640 const gchar *filename; 3641 gboolean headers; 3642 gboolean body; 3643 gboolean done; 3644 } fetch_data; 3645 3646 static void *imap_cmd_fetch_thread(void *data) 3647 { 3648 fetch_data *stuff = (fetch_data *)data; 3649 IMAPSession *session = stuff->session; 3650 guint32 uid = stuff->uid; 3651 const gchar *filename = stuff->filename; 3652 int r; 3653 3654 if (stuff->body) { 3655 r = imap_threaded_fetch_content(session->folder, 3656 uid, 1, filename); 3657 } 3658 else { 3659 r = imap_threaded_fetch_content(session->folder, 3660 uid, 0, filename); 3661 } 3662 if (r != MAILIMAP_NO_ERROR) { 3663 imap_handle_error(SESSION(session), NULL, r); 3664 debug_print("fetch err %d\n", r); 3665 return GINT_TO_POINTER(r); 3666 } 3667 return GINT_TO_POINTER(MAILIMAP_NO_ERROR); 3668 } 3669 3670 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid, 3671 const gchar *filename, gboolean headers, 3672 gboolean body) 3673 { 3674 fetch_data *data = g_new0(fetch_data, 1); 3675 int result = 0; 3676 data->done = FALSE; 3677 data->session = session; 3678 data->uid = uid; 3679 data->filename = filename; 3680 data->headers = headers; 3681 data->body = body; 3682 3683 if (prefs_common.work_offline && 3684 !inc_offline_should_override(FALSE, 3685 _("Claws Mail needs network access in order " 3686 "to access the IMAP server."))) { 3687 g_free(data); 3688 return -1; 3689 } 3690 statusbar_print_all(_("Fetching message...")); 3691 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data)); 3692 statusbar_pop_all(); 3693 g_free(data); 3694 return result; 3695 } 3696 3697 3698 static gint imap_cmd_append(IMAPSession *session, 3699 IMAPFolderItem *item, 3700 const gchar *destfolder, 3701 const gchar *file, IMAPFlags flags, 3702 guint32 *new_uid) 3703 { 3704 struct mailimap_flag_list * flag_list; 3705 int r; 3706 3707 cm_return_val_if_fail(file != NULL, MAILIMAP_ERROR_BAD_STATE); 3708 3709 flag_list = imap_flag_to_lep(item, flags, NULL); 3710 lock_session(session); 3711 r = imap_threaded_append(session->folder, destfolder, 3712 file, flag_list, (int *)new_uid); 3713 mailimap_flag_list_free(flag_list); 3714 3715 if (r != MAILIMAP_NO_ERROR) { 3716 imap_handle_error(SESSION(session), NULL, r); 3717 debug_print("append err %d\n", r); 3718 return r; 3719 } 3720 3721 unlock_session(session); 3722 3723 return MAILIMAP_NO_ERROR; 3724 } 3725 3726 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set, 3727 const gchar *destfolder, 3728 struct mailimap_set **source, struct mailimap_set **dest) 3729 { 3730 int r; 3731 3732 g_return_val_if_fail(session != NULL, MAILIMAP_ERROR_BAD_STATE); 3733 g_return_val_if_fail(set != NULL, MAILIMAP_ERROR_BAD_STATE); 3734 g_return_val_if_fail(destfolder != NULL, MAILIMAP_ERROR_BAD_STATE); 3735 3736 r = imap_threaded_copy(session->folder, set, destfolder, source, dest); 3737 if (r != MAILIMAP_NO_ERROR) { 3738 imap_handle_error(SESSION(session), NULL, r); 3739 return r; 3740 } 3741 3742 return MAILIMAP_NO_ERROR; 3743 } 3744 3745 static gint imap_cmd_store(IMAPSession *session, 3746 IMAPFolderItem *item, 3747 struct mailimap_set * set, 3748 IMAPFlags flags, GSList *tags, int do_add) 3749 { 3750 int r; 3751 struct mailimap_flag_list * flag_list = NULL; 3752 struct mailimap_store_att_flags * store_att_flags; 3753 3754 flag_list = imap_flag_to_lep(item, flags, tags); 3755 3756 if (do_add) 3757 store_att_flags = 3758 mailimap_store_att_flags_new_add_flags_silent(flag_list); 3759 else 3760 store_att_flags = 3761 mailimap_store_att_flags_new_remove_flags_silent(flag_list); 3762 3763 r = imap_threaded_store(session->folder, set, store_att_flags); 3764 mailimap_store_att_flags_free(store_att_flags); 3765 if (r != MAILIMAP_NO_ERROR) { 3766 imap_handle_error(SESSION(session), NULL, r); 3767 return r; 3768 } 3769 3770 return MAILIMAP_NO_ERROR; 3771 } 3772 3773 static gint imap_cmd_expunge(IMAPSession *session) 3774 { 3775 int r; 3776 3777 if (prefs_common.work_offline && 3778 !inc_offline_should_override(FALSE, 3779 _("Claws Mail needs network access in order " 3780 "to access the IMAP server."))) { 3781 return -1; 3782 } 3783 3784 statusbar_print_all(_("Expunging deleted messages...")); 3785 r = imap_threaded_expunge(session->folder); 3786 statusbar_pop_all(); 3787 if (r != MAILIMAP_NO_ERROR) { 3788 imap_handle_error(SESSION(session), NULL, r); 3789 return r; 3790 } 3791 3792 return MAILIMAP_NO_ERROR; 3793 } 3794 3795 gint imap_expunge(Folder *folder, FolderItem *item) 3796 { 3797 IMAPSession *session = imap_session_get(folder); 3798 if (session == NULL) 3799 return -1; 3800 3801 return imap_cmd_expunge(session); 3802 } 3803 3804 static void imap_path_separator_subst(gchar *str, gchar separator) 3805 { 3806 gchar *p; 3807 gboolean in_escape = FALSE; 3808 3809 if (!separator || separator == '/') return; 3810 3811 for (p = str; *p != '\0'; p++) { 3812 if (*p == '/' && !in_escape) 3813 *p = separator; 3814 else if (*p == '&' && *(p + 1) != '-' && !in_escape) 3815 in_escape = TRUE; 3816 else if (*p == '-' && in_escape) 3817 in_escape = FALSE; 3818 } 3819 } 3820 3821 static gboolean imap_rename_folder_func(GNode *node, gpointer data) 3822 { 3823 FolderItem *item = node->data; 3824 gchar **paths = data; 3825 const gchar *oldpath = paths[0]; 3826 const gchar *newpath = paths[1]; 3827 gchar *real_oldpath, *real_newpath; 3828 gchar *base; 3829 gchar *new_itempath; 3830 gint oldpathlen; 3831 IMAPSession *session = imap_session_get(item->folder); 3832 gint ok = MAILIMAP_NO_ERROR; 3833 oldpathlen = strlen(oldpath); 3834 if (strncmp(oldpath, item->path, oldpathlen) != 0) { 3835 g_warning("path doesn't match: %s, %s", oldpath, item->path); 3836 return TRUE; 3837 } 3838 3839 base = item->path + oldpathlen; 3840 while (*base == '/') base++; 3841 if (*base == '\0') 3842 new_itempath = g_strdup(newpath); 3843 else 3844 new_itempath = g_strconcat(newpath, "/", base, 3845 NULL); 3846 3847 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path, &ok); 3848 g_free(item->path); 3849 item->path = new_itempath; 3850 3851 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path, &ok); 3852 3853 imap_threaded_subscribe(item->folder, real_oldpath, FALSE); 3854 imap_threaded_subscribe(item->folder, real_newpath, TRUE); 3855 3856 g_free(real_oldpath); 3857 g_free(real_newpath); 3858 return FALSE; 3859 } 3860 3861 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list) 3862 { 3863 GSList *uidlist, *elem; 3864 int r = -1; 3865 clist *lep_uidlist = NULL; 3866 gint ok, nummsgs = 0; 3867 3868 if (session == NULL) { 3869 return -1; 3870 } 3871 3872 ok = imap_select(session, IMAP_FOLDER(folder), FOLDER_ITEM(item), 3873 NULL, NULL, NULL, NULL, NULL, TRUE); 3874 if (ok != MAILIMAP_NO_ERROR) { 3875 return -1; 3876 } 3877 3878 g_slist_free(item->uid_list); 3879 item->uid_list = NULL; 3880 3881 uidlist = NULL; 3882 3883 if (r == MAILIMAP_NO_ERROR) { 3884 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL); 3885 mailimap_search_result_free(lep_uidlist); 3886 } else { 3887 carray * lep_uidtab; 3888 if (r != -1) { /* inited */ 3889 imap_handle_error(SESSION(session), NULL, r); 3890 if (is_fatal(r)) 3891 return -1; 3892 } 3893 r = imap_threaded_fetch_uid(folder, 1, 3894 &lep_uidtab); 3895 if (r == MAILIMAP_NO_ERROR) { 3896 uidlist = imap_uid_list_from_lep_tab(lep_uidtab); 3897 imap_fetch_uid_list_free(lep_uidtab); 3898 } 3899 } 3900 3901 if (r != MAILIMAP_NO_ERROR) { 3902 imap_handle_error(SESSION(session), NULL, r); 3903 return -1; 3904 } 3905 3906 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) { 3907 guint msgnum; 3908 3909 msgnum = GPOINTER_TO_INT(elem->data); 3910 3911 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum)); 3912 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum)); 3913 nummsgs++; 3914 } 3915 g_slist_free(uidlist); 3916 3917 return nummsgs; 3918 3919 } 3920 3921 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid) 3922 { 3923 IMAPFolderItem *item = (IMAPFolderItem *)_item; 3924 IMAPSession *session; 3925 gint nummsgs; 3926 GSList *uidlist = NULL; 3927 gchar *dir; 3928 gint known_list_len = 0; 3929 gchar *path; 3930 3931 debug_print("get_num_list\n"); 3932 3933 g_return_val_if_fail(folder != NULL, -1); 3934 g_return_val_if_fail(item != NULL, -1); 3935 g_return_val_if_fail(item->item.path != NULL, -1); 3936 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1); 3937 g_return_val_if_fail(folder->account != NULL, -1); 3938 3939 known_list_len = g_slist_length(item->uid_list); 3940 if (!item->should_update) { 3941 debug_print("get_num_list: nothing to update\n"); 3942 *old_uids_valid = TRUE; 3943 if (known_list_len == item->item.total_msgs 3944 && known_list_len > 0) { 3945 *msgnum_list = g_slist_copy(item->uid_list); 3946 return known_list_len; 3947 } else { 3948 debug_print("don't know the list length...\n"); 3949 } 3950 } 3951 3952 if (prefs_common.work_offline && 3953 !inc_offline_should_override(FALSE, 3954 _("Claws Mail needs network access in order " 3955 "to access the IMAP server."))) { 3956 return -1; 3957 } 3958 3959 path = folder_item_get_path(_item); 3960 if (!is_dir_exist(path)) { 3961 if(is_file_exist(path)) 3962 unlink(path); 3963 make_dir_hier(path); 3964 } 3965 g_free(path); 3966 3967 debug_print("getting session...\n"); 3968 session = imap_session_get(folder); 3969 g_return_val_if_fail(session != NULL, -1); 3970 3971 lock_session(session); 3972 if (FOLDER_ITEM(item)->path) 3973 statusbar_print_all(_("Scanning folder %s/%s..."), 3974 FOLDER_ITEM(item)->folder->name, 3975 FOLDER_ITEM(item)->path); 3976 else 3977 statusbar_print_all(_("Scanning folder %s..."), 3978 FOLDER_ITEM(item)->folder->name); 3979 3980 if (item->should_trash_cache) { 3981 *old_uids_valid = FALSE; 3982 debug_print("get_num_list: trashing num list\n"); 3983 debug_print("Freeing imap uid cache\n"); 3984 item->lastuid = 0; 3985 g_slist_free(item->uid_list); 3986 item->uid_list = NULL; 3987 3988 imap_delete_all_cached_messages((FolderItem *)item); 3989 } else { 3990 debug_print("get_num_list: updating num list\n"); 3991 *old_uids_valid = TRUE; 3992 } 3993 3994 nummsgs = get_list_of_uids(session, folder, item, &uidlist); 3995 3996 unlock_session(session); 3997 3998 /* session could be broken now, in case of fatal error */ 3999 4000 debug_print("get_num_list: got %d msgs\n", nummsgs); 4001 4002 if (nummsgs < 0) { 4003 statusbar_pop_all(); 4004 return -1; 4005 } 4006 4007 *msgnum_list = uidlist; 4008 4009 dir = folder_item_get_path((FolderItem *)item); 4010 debug_print("removing old messages from %s\n", dir); 4011 remove_numbered_files_not_in_list(dir, *msgnum_list); 4012 g_free(dir); 4013 4014 debug_print("get_num_list - ok - %i\n", nummsgs); 4015 statusbar_pop_all(); 4016 item->should_trash_cache = FALSE; 4017 item->should_update = FALSE; 4018 return nummsgs; 4019 } 4020 4021 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item) 4022 { 4023 MsgInfo *msginfo; 4024 MsgFlags flags; 4025 4026 flags.perm_flags = MSG_NEW|MSG_UNREAD; 4027 flags.tmp_flags = 0; 4028 4029 g_return_val_if_fail(item != NULL, NULL); 4030 g_return_val_if_fail(file != NULL, NULL); 4031 4032 if (folder_has_parent_of_type(item, F_QUEUE)) { 4033 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED); 4034 } else if (folder_has_parent_of_type(item, F_DRAFT)) { 4035 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT); 4036 } 4037 4038 msginfo = procheader_parse_file(file, flags, FALSE, FALSE); 4039 if (!msginfo) return NULL; 4040 4041 msginfo->plaintext_file = g_strdup(file); 4042 msginfo->folder = item; 4043 4044 return msginfo; 4045 } 4046 4047 GSList *imap_get_msginfos(Folder *folder, FolderItem *item, 4048 GSList *msgnum_list) 4049 { 4050 IMAPSession *session; 4051 MsgInfoList *ret = NULL; 4052 gint ok; 4053 4054 debug_print("get_msginfos\n"); 4055 4056 g_return_val_if_fail(folder != NULL, NULL); 4057 g_return_val_if_fail(item != NULL, NULL); 4058 g_return_val_if_fail(msgnum_list != NULL, NULL); 4059 4060 debug_print("getting session...\n"); 4061 session = imap_session_get(folder); 4062 g_return_val_if_fail(session != NULL, NULL); 4063 4064 lock_session(session); /* unlocked later in the function */ 4065 4066 debug_print("IMAP getting msginfos\n"); 4067 ok = imap_select(session, IMAP_FOLDER(folder), item, 4068 NULL, NULL, NULL, NULL, NULL, FALSE); 4069 if (ok != MAILIMAP_NO_ERROR) { 4070 return NULL; 4071 } 4072 if (!(folder_has_parent_of_type(item, F_DRAFT) || 4073 folder_has_parent_of_type(item, F_QUEUE))) { 4074 ret = g_slist_concat(ret, 4075 imap_get_uncached_messages(session, item, 4076 msgnum_list, &ok)); 4077 if (ok != MAILIMAP_NO_ERROR) 4078 return NULL; 4079 unlock_session(session); 4080 } else { 4081 MsgNumberList *sorted_list, *elem, *llast = NULL; 4082 gint startnum, lastnum; 4083 4084 unlock_session(session); 4085 4086 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare); 4087 4088 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data); 4089 4090 llast = g_slist_last(ret); 4091 for (elem = sorted_list;; elem = g_slist_next(elem)) { 4092 guint num = 0; 4093 4094 if (elem) 4095 num = GPOINTER_TO_INT(elem->data); 4096 4097 if (num > lastnum + 1 || elem == NULL) { 4098 int i; 4099 for (i = startnum; i <= lastnum; ++i) { 4100 gchar *file; 4101 file = imap_fetch_msg(folder, item, i); 4102 if (file != NULL) { 4103 MsgInfo *msginfo = imap_parse_msg(file, item); 4104 if (msginfo != NULL) { 4105 msginfo->msgnum = i; 4106 if (llast == NULL) 4107 llast = ret = g_slist_append(ret, msginfo); 4108 else { 4109 llast = g_slist_append(llast, msginfo); 4110 llast = llast->next; 4111 } 4112 } 4113 g_free(file); 4114 } 4115 } 4116 4117 if (elem == NULL) 4118 break; 4119 4120 startnum = num; 4121 } 4122 lastnum = num; 4123 } 4124 4125 g_slist_free(sorted_list); 4126 } 4127 return ret; 4128 } 4129 4130 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid) 4131 { 4132 MsgInfo *msginfo = NULL; 4133 MsgInfoList *msginfolist; 4134 MsgNumberList numlist; 4135 4136 numlist.next = NULL; 4137 numlist.data = GINT_TO_POINTER(uid); 4138 4139 msginfolist = imap_get_msginfos(folder, item, &numlist); 4140 if (msginfolist != NULL) { 4141 msginfo = msginfolist->data; 4142 g_slist_free(msginfolist); 4143 } 4144 4145 return msginfo; 4146 } 4147 4148 gboolean imap_scan_required(Folder *folder, FolderItem *_item) 4149 { 4150 IMAPSession *session; 4151 IMAPFolderItem *item = (IMAPFolderItem *)_item; 4152 gint ok, exists = 0, unseen = 0; 4153 guint32 uid_next = 0, uid_val = 0; 4154 gboolean selected_folder; 4155 4156 g_return_val_if_fail(folder != NULL, FALSE); 4157 g_return_val_if_fail(item != NULL, FALSE); 4158 g_return_val_if_fail(item->item.folder != NULL, FALSE); 4159 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE); 4160 4161 if (item->item.path == NULL) 4162 return FALSE; 4163 4164 if (item->should_update) { 4165 debug_print("scan already required\n"); 4166 return TRUE; 4167 } 4168 debug_print("getting session...\n"); 4169 session = imap_session_get(folder); 4170 4171 g_return_val_if_fail(session != NULL, FALSE); 4172 lock_session(session); /* unlocked later in the function */ 4173 4174 selected_folder = (session->mbox != NULL) && 4175 (!strcmp(session->mbox, item->item.path)); 4176 if (selected_folder) { 4177 if (!session->folder_content_changed) { 4178 ok = imap_cmd_noop(session); 4179 if (ok != MAILIMAP_NO_ERROR) { 4180 debug_print("disconnected!\n"); 4181 if (!is_fatal(ok)) 4182 session = imap_reconnect_if_possible(folder, session); 4183 else 4184 session = imap_session_get(folder); 4185 if (session == NULL) 4186 return FALSE; 4187 } 4188 4189 if (session->folder_content_changed) { 4190 debug_print("CHANGED (self-noop)! scan_required\n"); 4191 item->should_update = TRUE; 4192 if (session->uid_validity && session->uid_validity != item->item.mtime) { 4193 item->item.mtime = session->uid_validity; 4194 item->should_trash_cache = TRUE; 4195 } 4196 unlock_session(session); 4197 return TRUE; 4198 } 4199 } else { 4200 debug_print("CHANGED (previous noop)! scan_required\n"); 4201 item->should_update = TRUE; 4202 if (session->uid_validity && session->uid_validity != item->item.mtime) { 4203 item->item.mtime = session->uid_validity; 4204 item->should_trash_cache = TRUE; 4205 } 4206 unlock_session(session); 4207 return TRUE; 4208 } 4209 } else { 4210 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item), 4211 &exists, &uid_next, &uid_val, &unseen, FALSE); 4212 if (ok != MAILIMAP_NO_ERROR) { 4213 return FALSE; 4214 } 4215 4216 debug_print("exists %d, item->item.total_msgs %d\n" 4217 "\tunseen %d, item->item.unread_msgs %d\n" 4218 "\tuid_next %d, item->uid_next %d\n" 4219 "\tuid_val %d, item->item.mtime %ld\n", 4220 exists, item->item.total_msgs, unseen, item->item.unread_msgs, 4221 uid_next, item->uid_next, uid_val, (long)(item->item.mtime)); 4222 if (exists != item->item.total_msgs 4223 || unseen != item->item.unread_msgs 4224 || uid_next != item->uid_next 4225 || uid_val != item->item.mtime) { 4226 debug_print("CHANGED (status)! scan_required\n"); 4227 item->last_change = time(NULL); 4228 item->should_update = TRUE; 4229 item->uid_next = uid_next; 4230 if (uid_val != item->item.mtime) { 4231 item->item.mtime = uid_val; 4232 item->should_trash_cache = TRUE; 4233 } 4234 unlock_session(session); 4235 return TRUE; 4236 } 4237 } 4238 unlock_session(session); 4239 4240 item->should_update = FALSE; 4241 return FALSE; 4242 } 4243 4244 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags) 4245 { 4246 IMAPSession *session; 4247 IMAPFlags flags_set = 0, flags_unset = 0; 4248 gint ok = MAILIMAP_NO_ERROR; 4249 MsgNumberList numlist; 4250 hashtable_data *ht_data = NULL; 4251 4252 g_return_if_fail(folder != NULL); 4253 g_return_if_fail(folder->klass == &imap_class); 4254 g_return_if_fail(item != NULL); 4255 g_return_if_fail(item->folder == folder); 4256 g_return_if_fail(msginfo != NULL); 4257 g_return_if_fail(msginfo->folder == item); 4258 4259 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED)) 4260 flags_set |= IMAP_FLAG_FLAGGED; 4261 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED)) 4262 flags_unset |= IMAP_FLAG_FLAGGED; 4263 4264 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD)) 4265 flags_unset |= IMAP_FLAG_SEEN; 4266 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD)) 4267 flags_set |= IMAP_FLAG_SEEN; 4268 4269 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED)) 4270 flags_set |= IMAP_FLAG_ANSWERED; 4271 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED)) 4272 flags_unset |= IMAP_FLAG_ANSWERED; 4273 4274 if (!MSG_IS_FORWARDED(msginfo->flags) && (newflags & MSG_FORWARDED)) 4275 flags_set |= IMAP_FLAG_FORWARDED; 4276 if ( MSG_IS_FORWARDED(msginfo->flags) && !(newflags & MSG_FORWARDED)) 4277 flags_unset |= IMAP_FLAG_FORWARDED; 4278 4279 if (!MSG_IS_SPAM(msginfo->flags) && (newflags & MSG_SPAM)) { 4280 flags_set |= IMAP_FLAG_SPAM; 4281 flags_unset |= IMAP_FLAG_HAM; 4282 } 4283 if ( MSG_IS_SPAM(msginfo->flags) && !(newflags & MSG_SPAM)) { 4284 flags_set |= IMAP_FLAG_HAM; 4285 flags_unset |= IMAP_FLAG_SPAM; 4286 } 4287 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED)) 4288 flags_set |= IMAP_FLAG_DELETED; 4289 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED)) 4290 flags_unset |= IMAP_FLAG_DELETED; 4291 4292 if (!flags_set && !flags_unset) { 4293 /* the changed flags were not translatable to IMAP-speak. 4294 * like MSG_POSTFILTERED, so just apply. */ 4295 msginfo->flags.perm_flags = newflags; 4296 return; 4297 } 4298 4299 debug_print("getting session...\n"); 4300 session = imap_session_get(folder); 4301 if (!session) { 4302 return; 4303 } 4304 4305 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder, 4306 NULL, NULL, NULL, NULL, NULL, FALSE)) != MAILIMAP_NO_ERROR) { 4307 return; 4308 } 4309 numlist.next = NULL; 4310 numlist.data = GINT_TO_POINTER(msginfo->msgnum); 4311 4312 if (IMAP_FOLDER_ITEM(item)->batching) { 4313 /* instead of performing an UID STORE command for each message change, 4314 * as a lot of them can change "together", we just fill in hashtables 4315 * and defer the treatment so that we're able to send only one 4316 * command. 4317 */ 4318 debug_print("IMAP batch mode on, deferring flags change\n"); 4319 if (flags_set) { 4320 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table, 4321 GINT_TO_POINTER(flags_set)); 4322 if (ht_data == NULL) { 4323 ht_data = g_new0(hashtable_data, 1); 4324 ht_data->item = IMAP_FOLDER_ITEM(item); 4325 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table, 4326 GINT_TO_POINTER(flags_set), ht_data); 4327 } 4328 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum)); 4329 } 4330 if (flags_unset) { 4331 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table, 4332 GINT_TO_POINTER(flags_unset)); 4333 if (ht_data == NULL) { 4334 ht_data = g_new0(hashtable_data, 1); 4335 ht_data->item = IMAP_FOLDER_ITEM(item); 4336 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table, 4337 GINT_TO_POINTER(flags_unset), ht_data); 4338 } 4339 ht_data->msglist = g_slist_prepend(ht_data->msglist, 4340 GINT_TO_POINTER(msginfo->msgnum)); 4341 } 4342 } else { 4343 debug_print("IMAP changing flags\n"); 4344 if (flags_set) { 4345 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item), &numlist, flags_set, NULL, TRUE); 4346 if (ok != MAILIMAP_NO_ERROR) { 4347 return; 4348 } 4349 } 4350 4351 if (flags_unset) { 4352 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item), &numlist, flags_unset, NULL, FALSE); 4353 if (ok != MAILIMAP_NO_ERROR) { 4354 return; 4355 } 4356 } 4357 } 4358 msginfo->flags.perm_flags = newflags; 4359 return; 4360 } 4361 4362 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid) 4363 { 4364 gint ok; 4365 IMAPSession *session; 4366 gchar *dir; 4367 MsgNumberList numlist; 4368 4369 g_return_val_if_fail(folder != NULL, -1); 4370 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1); 4371 g_return_val_if_fail(item != NULL, -1); 4372 4373 debug_print("getting session...\n"); 4374 session = imap_session_get(folder); 4375 if (!session) return -1; 4376 4377 ok = imap_select(session, IMAP_FOLDER(folder), item, 4378 NULL, NULL, NULL, NULL, NULL, FALSE); 4379 if (ok != MAILIMAP_NO_ERROR) { 4380 return ok; 4381 } 4382 numlist.next = NULL; 4383 numlist.data = GINT_TO_POINTER(uid); 4384 4385 ok = imap_set_message_flags 4386 (session, IMAP_FOLDER_ITEM(item), &numlist, IMAP_FLAG_DELETED, NULL, TRUE); 4387 if (ok != MAILIMAP_NO_ERROR) { 4388 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid); 4389 return ok; 4390 } 4391 4392 ok = imap_cmd_expunge(session); 4393 4394 if (ok != MAILIMAP_NO_ERROR) { 4395 log_warning(LOG_PROTOCOL, _("can't expunge\n")); 4396 return ok; 4397 } 4398 4399 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove( 4400 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data); 4401 dir = folder_item_get_path(item); 4402 if (is_dir_exist(dir)) 4403 remove_numbered_files(dir, uid, uid); 4404 g_free(dir); 4405 return MAILIMAP_NO_ERROR; 4406 } 4407 4408 static gint compare_msginfo(gconstpointer a, gconstpointer b) 4409 { 4410 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum; 4411 } 4412 4413 static gboolean flag_ok(IMAPFolderItem *item, guint flag) 4414 { 4415 if (item->ok_flags && g_slist_find(item->ok_flags, GUINT_TO_POINTER(flag))) { 4416 debug_print("flag %d is OK\n", flag); 4417 return TRUE; 4418 } 4419 if (item->can_create_flags == ITEM_CAN_CREATE_FLAGS) { 4420 debug_print("creating flags is OK\n"); 4421 return TRUE; 4422 } 4423 return FALSE; 4424 } 4425 4426 /* 4427 * NEW and DELETED flags are not syncronized 4428 * - The NEW/RECENT flags in IMAP folders can not really be directly 4429 * modified by Claws Mail 4430 * - The DELETE/DELETED flag in IMAP and Claws Mail do not have the same 4431 * meaning, in IMAP it always removes the messages from the FolderItem 4432 * in Claws Mail it can mean to move the message to Trash 4433 */ 4434 4435 typedef struct _get_flags_data { 4436 Folder *folder; 4437 FolderItem *item; 4438 MsgInfoList *msginfo_list; 4439 GHashTable *msgflags; 4440 gboolean full_search; 4441 gboolean done; 4442 } get_flags_data; 4443 4444 static /*gint*/ void *imap_get_flags_thread(void *data) 4445 { 4446 get_flags_data *stuff = (get_flags_data *)data; 4447 Folder *folder = stuff->folder; 4448 FolderItem *fitem = (FolderItem *) stuff->item; 4449 MsgInfoList *msginfo_list = stuff->msginfo_list; 4450 GHashTable *msgflags = stuff->msgflags; 4451 GSList *elem; 4452 carray * lep_uidtab; 4453 IMAPSession *session; 4454 gint ok; 4455 int r = MAILIMAP_NO_ERROR; 4456 GHashTable *flags_hash = NULL; 4457 GHashTable *tags_hash = NULL; 4458 gboolean full_search = stuff->full_search; 4459 GSList *sorted_list = NULL; 4460 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL, *forwarded = NULL, *spam = NULL; 4461 GSList *seq_list; 4462 gboolean selected_folder; 4463 gint exists_cnt, unseen_cnt; 4464 4465 session = imap_session_get(folder); 4466 4467 if (session == NULL) { 4468 stuff->done = TRUE; 4469 return GINT_TO_POINTER(-1); 4470 } 4471 selected_folder = (session->mbox != NULL) && 4472 (!strcmp(session->mbox, fitem->path)); 4473 4474 lock_session(session); 4475 if (!selected_folder) { 4476 ok = imap_select(session, IMAP_FOLDER(folder), fitem, 4477 &exists_cnt, NULL, &unseen_cnt, NULL, NULL, TRUE); 4478 if (ok != MAILIMAP_NO_ERROR) { 4479 stuff->done = TRUE; 4480 return GINT_TO_POINTER(-1); 4481 } 4482 } 4483 4484 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo); 4485 if (!full_search) { 4486 seq_list = imap_get_lep_set_from_msglist(IMAP_FOLDER(folder), msginfo_list); 4487 } else { 4488 struct mailimap_set * set; 4489 set = mailimap_set_new_interval(1, 0); 4490 seq_list = g_slist_append(NULL, set); 4491 } 4492 4493 r = imap_threaded_fetch_uid_flags(folder, 1, &lep_uidtab); 4494 if (r == MAILIMAP_NO_ERROR) { 4495 flags_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); 4496 tags_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL); 4497 imap_flags_hash_from_lep_uid_flags_tab(lep_uidtab, flags_hash, tags_hash); 4498 imap_fetch_uid_flags_list_free(lep_uidtab); 4499 } else { 4500 imap_handle_error(SESSION(session), NULL, r); 4501 goto bail; 4502 } 4503 4504 bail: 4505 if (r == MAILIMAP_NO_ERROR) 4506 unlock_session(session); 4507 4508 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) { 4509 MsgInfo *msginfo; 4510 MsgPermFlags flags, oldflags; 4511 gboolean wasnew; 4512 4513 msginfo = (MsgInfo *) elem->data; 4514 flags = msginfo->flags.perm_flags; 4515 wasnew = (flags & MSG_NEW); 4516 oldflags = flags & ~(MSG_NEW|MSG_UNREAD|MSG_REPLIED|MSG_FORWARDED|MSG_MARKED|MSG_DELETED|MSG_SPAM); 4517 4518 if (flags_hash != NULL) { 4519 flags = GPOINTER_TO_INT(g_hash_table_lookup(flags_hash, 4520 GINT_TO_POINTER(msginfo->msgnum))); 4521 } 4522 4523 if ((flags & MSG_UNREAD) == 0) 4524 flags &= ~MSG_NEW; 4525 else if (wasnew) 4526 flags |= MSG_NEW; 4527 flags |= oldflags; 4528 4529 g_hash_table_insert(msgflags, msginfo, GINT_TO_POINTER(flags)); 4530 } 4531 4532 if (flags_hash) 4533 g_hash_table_destroy(flags_hash); 4534 if (tags_hash) 4535 g_hash_table_destroy(tags_hash); 4536 4537 imap_lep_set_free(seq_list); 4538 g_slist_free(flagged); 4539 g_slist_free(deleted); 4540 g_slist_free(answered); 4541 g_slist_free(forwarded); 4542 g_slist_free(spam); 4543 g_slist_free(unseen); 4544 g_slist_free(sorted_list); 4545 4546 stuff->done = TRUE; 4547 return GINT_TO_POINTER(0); 4548 } 4549 4550 static gint imap_get_flags(Folder *folder, FolderItem *item, 4551 MsgInfoList *msginfo_list, GHashTable *msgflags) 4552 { 4553 gint result; 4554 get_flags_data *data = g_new0(get_flags_data, 1); 4555 data->done = FALSE; 4556 data->folder = folder; 4557 data->item = item; 4558 data->msginfo_list = msginfo_list; 4559 data->msgflags = msgflags; 4560 data->full_search = FALSE; 4561 4562 GSList *tmp = NULL, *cur; 4563 4564 if (prefs_common.work_offline && 4565 !inc_offline_should_override(FALSE, 4566 _("Claws Mail needs network access in order " 4567 "to access the IMAP server."))) { 4568 g_free(data); 4569 return -1; 4570 } 4571 4572 tmp = folder_item_get_msg_list(item); 4573 4574 if (g_slist_length(tmp) <= g_slist_length(msginfo_list)) 4575 data->full_search = TRUE; 4576 4577 for (cur = tmp; cur; cur = cur->next) 4578 procmsg_msginfo_free((MsgInfo **)&(cur->data)); 4579 4580 g_slist_free(tmp); 4581 4582 result = GPOINTER_TO_INT(imap_get_flags_thread(data)); 4583 4584 g_free(data); 4585 return result; 4586 4587 } 4588 4589 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data) 4590 { 4591 gboolean flags_set = GPOINTER_TO_INT(user_data); 4592 gint flags_value = GPOINTER_TO_INT(key); 4593 hashtable_data *data = (hashtable_data *)value; 4594 IMAPFolderItem *_item = data->item; 4595 FolderItem *item = (FolderItem *)_item; 4596 gint ok = MAILIMAP_ERROR_BAD_STATE; 4597 IMAPSession *session = NULL; 4598 4599 debug_print("getting session...\n"); 4600 session = imap_session_get(item->folder); 4601 4602 data->msglist = g_slist_reverse(data->msglist); 4603 4604 debug_print("IMAP %ssetting flags to %d for %d messages\n", 4605 flags_set?"":"un", 4606 flags_value, 4607 g_slist_length(data->msglist)); 4608 4609 lock_session(session); 4610 if (session) { 4611 ok = imap_select(session, IMAP_FOLDER(item->folder), item, 4612 NULL, NULL, NULL, NULL, NULL, FALSE); 4613 } 4614 if (ok == MAILIMAP_NO_ERROR) { 4615 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item), 4616 data->msglist, flags_value, NULL, flags_set); 4617 } else { 4618 g_warning("can't select mailbox %s", item->path); 4619 } 4620 4621 if (!is_fatal(ok)) 4622 unlock_session(session); 4623 4624 g_slist_free(data->msglist); 4625 g_free(data); 4626 return TRUE; 4627 } 4628 4629 static void process_hashtable(IMAPFolderItem *item) 4630 { 4631 if (item->flags_set_table) { 4632 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE)); 4633 g_hash_table_destroy(item->flags_set_table); 4634 item->flags_set_table = NULL; 4635 } 4636 if (item->flags_unset_table) { 4637 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE)); 4638 g_hash_table_destroy(item->flags_unset_table); 4639 item->flags_unset_table = NULL; 4640 } 4641 } 4642 4643 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch) 4644 { 4645 IMAPFolderItem *item = (IMAPFolderItem *)_item; 4646 IMAPSession *session; 4647 4648 g_return_if_fail(item != NULL); 4649 4650 if (item->batching == batch) 4651 return; 4652 4653 if (batch) { 4654 item->batching = TRUE; 4655 debug_print("IMAP switching to batch mode\n"); 4656 if (!item->flags_set_table) { 4657 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal); 4658 } 4659 if (!item->flags_unset_table) { 4660 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal); 4661 } 4662 session = imap_session_get(folder); 4663 if (session) { 4664 imap_refresh_sensitivity(session); 4665 session->sens_update_block = TRUE; 4666 } 4667 } else { 4668 debug_print("IMAP switching away from batch mode\n"); 4669 /* process stuff */ 4670 process_hashtable(item); 4671 item->batching = FALSE; 4672 session = imap_session_get(folder); 4673 if (session) { 4674 session->sens_update_block = FALSE; 4675 imap_refresh_sensitivity(session); 4676 } 4677 } 4678 } 4679 4680 /* data types conversion libetpan <-> claws */ 4681 #define ETPAN_IMAP_MB_MARKED 1 4682 #define ETPAN_IMAP_MB_UNMARKED 2 4683 #define ETPAN_IMAP_MB_NOSELECT 4 4684 #define ETPAN_IMAP_MB_NOINFERIORS 8 4685 4686 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags) 4687 { 4688 int flags; 4689 clistiter * cur; 4690 4691 flags = 0; 4692 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) { 4693 switch (imap_flags->mbf_sflag) { 4694 case MAILIMAP_MBX_LIST_SFLAG_MARKED: 4695 flags |= ETPAN_IMAP_MB_MARKED; 4696 break; 4697 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT: 4698 flags |= ETPAN_IMAP_MB_NOSELECT; 4699 break; 4700 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED: 4701 flags |= ETPAN_IMAP_MB_UNMARKED; 4702 break; 4703 } 4704 } 4705 4706 if (imap_flags->mbf_oflags) { 4707 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ; 4708 cur = clist_next(cur)) { 4709 struct mailimap_mbx_list_oflag * oflag; 4710 4711 oflag = clist_content(cur); 4712 4713 switch (oflag->of_type) { 4714 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS: 4715 flags |= ETPAN_IMAP_MB_NOINFERIORS; 4716 break; 4717 } 4718 } 4719 } 4720 return flags; 4721 } 4722 4723 static GSList * imap_list_from_lep(IMAPFolder * folder, 4724 clist * list, const gchar * real_path, gboolean all) 4725 { 4726 clistiter * iter; 4727 GSList * item_list = NULL, *llast = NULL; 4728 4729 if (list) { 4730 for(iter = clist_begin(list) ; iter != NULL ; 4731 iter = clist_next(iter)) { 4732 struct mailimap_mailbox_list * mb; 4733 int flags; 4734 char delimiter; 4735 char * name; 4736 char * dup_name; 4737 gchar * base; 4738 gchar * loc_name; 4739 gchar * loc_path; 4740 FolderItem *new_item; 4741 4742 mb = clist_content(iter); 4743 4744 if (mb == NULL) 4745 continue; 4746 4747 flags = 0; 4748 if (mb->mb_flag != NULL) 4749 flags = imap_flags_to_flags(mb->mb_flag); 4750 4751 delimiter = mb->mb_delimiter; 4752 name = mb->mb_name; 4753 4754 dup_name = strdup(name); 4755 if (delimiter != '\0') 4756 subst_char(dup_name, delimiter, '/'); 4757 4758 base = g_path_get_basename(dup_name); 4759 if (base[0] == '.') { 4760 g_free(base); 4761 free(dup_name); 4762 continue; 4763 } 4764 if (!all && path_cmp(name, real_path) == 0) { 4765 g_free(base); 4766 free(dup_name); 4767 continue; 4768 } 4769 4770 if (!all && dup_name[strlen(dup_name)-1] == '/') { 4771 g_free(base); 4772 free(dup_name); 4773 continue; 4774 } 4775 4776 loc_name = imap_modified_utf7_to_utf8(base, FALSE); 4777 loc_path = imap_modified_utf7_to_utf8(dup_name, FALSE); 4778 4779 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path); 4780 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0) 4781 new_item->no_sub = TRUE; 4782 if (strcasecmp(dup_name, "INBOX") != 0 && 4783 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0)) 4784 new_item->no_select = TRUE; 4785 4786 if (item_list == NULL) 4787 llast = item_list = g_slist_append(item_list, new_item); 4788 else { 4789 llast = g_slist_append(llast, new_item); 4790 llast = llast->next; 4791 } 4792 debug_print("folder '%s' found.\n", loc_path); 4793 g_free(base); 4794 g_free(loc_path); 4795 g_free(loc_name); 4796 4797 free(dup_name); 4798 } 4799 } 4800 return item_list; 4801 } 4802 4803 static GSList * imap_get_lep_set_from_numlist(IMAPFolder *folder, MsgNumberList *numlist) 4804 { 4805 GSList *sorted_list, *cur; 4806 guint first, last, next; 4807 GSList *ret_list = NULL, *llast = NULL; 4808 struct mailimap_set * current_set; 4809 unsigned int item_count; 4810 4811 if (numlist == NULL) 4812 return NULL; 4813 4814 current_set = mailimap_set_new_empty(); 4815 4816 sorted_list = g_slist_copy(numlist); 4817 sorted_list = g_slist_sort(sorted_list, g_int_compare); 4818 4819 first = GPOINTER_TO_INT(sorted_list->data); 4820 4821 item_count = 0; 4822 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) { 4823 if (GPOINTER_TO_INT(cur->data) == 0) 4824 continue; 4825 4826 item_count ++; 4827 4828 last = GPOINTER_TO_INT(cur->data); 4829 if (cur->next) 4830 next = GPOINTER_TO_INT(cur->next->data); 4831 else 4832 next = 0; 4833 4834 if (last + 1 != next || next == 0 || item_count >= folder->max_set_size) { 4835 4836 struct mailimap_set_item * item; 4837 item = mailimap_set_item_new(first, last); 4838 mailimap_set_add(current_set, item); 4839 4840 first = next; 4841 4842 if (item_count >= folder->max_set_size) { 4843 if (ret_list == NULL) 4844 llast = ret_list = g_slist_append(ret_list, 4845 current_set); 4846 else { 4847 llast = g_slist_append(llast, current_set); 4848 llast = llast->next; 4849 } 4850 4851 current_set = mailimap_set_new_empty(); 4852 item_count = 0; 4853 } 4854 } 4855 } 4856 4857 if (clist_count(current_set->set_list) > 0) { 4858 ret_list = g_slist_append(ret_list, 4859 current_set); 4860 } 4861 4862 g_slist_free(sorted_list); 4863 4864 return ret_list; 4865 } 4866 4867 static GSList * imap_get_lep_set_from_msglist(IMAPFolder *folder, MsgInfoList *msglist) 4868 { 4869 MsgNumberList *numlist = NULL; 4870 GSList *seq_list; 4871 4872 numlist = procmsg_get_number_list_for_msgs(msglist); 4873 4874 seq_list = imap_get_lep_set_from_numlist(folder, numlist); 4875 g_slist_free(numlist); 4876 4877 return seq_list; 4878 } 4879 4880 static GSList * imap_uid_list_from_lep(clist * list, gint* length) 4881 { 4882 clistiter * iter; 4883 GSList * result; 4884 gint len = 0; 4885 4886 result = NULL; 4887 4888 if (list) { 4889 for(iter = clist_begin(list) ; iter != NULL ; 4890 iter = clist_next(iter)) { 4891 uint32_t * puid; 4892 4893 puid = clist_content(iter); 4894 result = g_slist_prepend(result, GINT_TO_POINTER(* puid)); 4895 len++; 4896 } 4897 result = g_slist_reverse(result); 4898 } 4899 if (length) 4900 *length = len; 4901 return result; 4902 } 4903 4904 static GSList * imap_uid_list_from_lep_tab(carray * list) 4905 { 4906 unsigned int i; 4907 GSList * result; 4908 4909 result = NULL; 4910 4911 for(i = 0 ; i < carray_count(list) ; i ++) { 4912 uint32_t * puid; 4913 4914 puid = carray_get(list, i); 4915 result = g_slist_prepend(result, GINT_TO_POINTER(* puid)); 4916 } 4917 result = g_slist_reverse(result); 4918 return result; 4919 } 4920 4921 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list, 4922 GHashTable * hash, 4923 GHashTable * tags_hash) 4924 { 4925 unsigned int i; 4926 4927 for(i = 0 ; i < carray_count(list) ; i += 3) { 4928 uint32_t * puid; 4929 int * pflags; 4930 GSList *tags; 4931 4932 puid = carray_get(list, i); 4933 pflags = carray_get(list, i + 1); 4934 tags = carray_get(list, i + 2); 4935 4936 g_hash_table_insert(hash, GINT_TO_POINTER(*puid), GINT_TO_POINTER(* pflags)); 4937 g_hash_table_insert(tags_hash, GINT_TO_POINTER(*puid), tags); 4938 } 4939 } 4940 4941 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info, 4942 FolderItem *item) 4943 { 4944 MsgInfo *msginfo = NULL; 4945 guint32 uid = 0; 4946 goffset size = 0; 4947 MsgFlags flags = {0, 0}; 4948 4949 if (info->headers == NULL) 4950 return NULL; 4951 4952 MSG_SET_TMP_FLAGS(flags, MSG_IMAP); 4953 if (folder_has_parent_of_type(item, F_QUEUE)) { 4954 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED); 4955 } else if (folder_has_parent_of_type(item, F_DRAFT)) { 4956 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT); 4957 } 4958 flags.perm_flags = info->flags; 4959 4960 uid = info->uid; 4961 size = (goffset) info->size; 4962 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE); 4963 4964 if (msginfo) { 4965 msginfo->msgnum = uid; 4966 msginfo->size = size; 4967 } 4968 4969 return msginfo; 4970 } 4971 4972 static void imap_lep_set_free(GSList *seq_list) 4973 { 4974 GSList * cur; 4975 4976 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) { 4977 struct mailimap_set * imapset; 4978 4979 imapset = cur->data; 4980 mailimap_set_free(imapset); 4981 } 4982 g_slist_free(seq_list); 4983 } 4984 4985 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFolderItem *item, IMAPFlags flags, GSList *tags) 4986 { 4987 struct mailimap_flag_list * flag_list; 4988 GSList *cur = tags; 4989 4990 flag_list = mailimap_flag_list_new_empty(); 4991 4992 if (IMAP_IS_SEEN(flags)) 4993 mailimap_flag_list_add(flag_list, 4994 mailimap_flag_new_seen()); 4995 if (IMAP_IS_ANSWERED(flags)) 4996 mailimap_flag_list_add(flag_list, 4997 mailimap_flag_new_answered()); 4998 if (IMAP_IS_FLAGGED(flags)) 4999 mailimap_flag_list_add(flag_list, 5000 mailimap_flag_new_flagged()); 5001 if (IMAP_IS_DELETED(flags)) 5002 mailimap_flag_list_add(flag_list, 5003 mailimap_flag_new_deleted()); 5004 if (IMAP_IS_DRAFT(flags)) 5005 mailimap_flag_list_add(flag_list, 5006 mailimap_flag_new_draft()); 5007 if (IMAP_IS_FORWARDED(flags) && flag_ok(item, IMAP_FLAG_FORWARDED)) 5008 mailimap_flag_list_add(flag_list, 5009 mailimap_flag_new_flag_keyword(strdup(RTAG_FORWARDED))); 5010 if (IMAP_IS_SPAM(flags) && flag_ok(item, IMAP_FLAG_SPAM)) 5011 mailimap_flag_list_add(flag_list, 5012 mailimap_flag_new_flag_keyword(strdup(RTAG_JUNK))); 5013 else if (IMAP_IS_HAM(flags) && flag_ok(item, IMAP_FLAG_HAM)) 5014 mailimap_flag_list_add(flag_list, 5015 mailimap_flag_new_flag_keyword(strdup(RTAG_NON_JUNK))); 5016 5017 for (; cur; cur = cur->next) { 5018 gchar *enc_str = 5019 imap_utf8_to_modified_utf7(cur->data, TRUE); 5020 g_strstrip(enc_str); 5021 5022 mailimap_flag_list_add(flag_list, 5023 mailimap_flag_new_flag_keyword(enc_str)); 5024 } 5025 5026 return flag_list; 5027 } 5028 5029 guint imap_folder_get_refcnt(Folder *folder) 5030 { 5031 return ((IMAPFolder *)folder)->refcnt; 5032 } 5033 5034 void imap_folder_ref(Folder *folder) 5035 { 5036 ((IMAPFolder *)folder)->refcnt++; 5037 } 5038 5039 void imap_disconnect_all(gboolean have_connectivity) 5040 { 5041 GList *list; 5042 5043 imap_main_set_timeout(1); 5044 5045 for (list = account_get_list(); list != NULL; list = list->next) { 5046 PrefsAccount *account = list->data; 5047 if (account->protocol == A_IMAP4) { 5048 RemoteFolder *folder = (RemoteFolder *)account->folder; 5049 if (folder && folder->session) { 5050 if (imap_is_busy(FOLDER(folder))) 5051 imap_threaded_cancel(FOLDER(folder)); 5052 5053 IMAPSession *session = (IMAPSession *)folder->session; 5054 if (have_connectivity) 5055 imap_threaded_disconnect(FOLDER(folder)); 5056 SESSION(session)->state = SESSION_DISCONNECTED; 5057 SESSION(session)->sock = NULL; 5058 imap_safe_destroy(session); 5059 folder->session = NULL; 5060 } 5061 } 5062 } 5063 imap_main_set_timeout(prefs_common.io_timeout_secs); 5064 } 5065 5066 void imap_folder_unref(Folder *folder) 5067 { 5068 if (((IMAPFolder *)folder)->refcnt > 0) 5069 ((IMAPFolder *)folder)->refcnt--; 5070 } 5071 5072 void imap_cancel_all(void) 5073 { 5074 GList *folderlist; 5075 GList *cur; 5076 5077 folderlist = folder_get_list(); 5078 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) { 5079 Folder *folder = (Folder *) cur->data; 5080 5081 if (folder->klass == &imap_class) { 5082 if (imap_is_busy(folder)) { 5083 IMAPSession *imap_session; 5084 RemoteFolder *rfolder; 5085 5086 g_printerr("cancelled\n"); 5087 imap_threaded_cancel(folder); 5088 rfolder = (RemoteFolder *) folder; 5089 imap_session = (IMAPSession *) rfolder->session; 5090 if (imap_session) 5091 imap_session->cancelled = 1; 5092 } 5093 } 5094 } 5095 } 5096 5097 gboolean imap_cancel_all_enabled(void) 5098 { 5099 GList *folderlist; 5100 GList *cur; 5101 5102 folderlist = folder_get_list(); 5103 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) { 5104 Folder *folder = (Folder *) cur->data; 5105 5106 if (folder->klass == &imap_class) { 5107 if (imap_is_busy(folder)) { 5108 return TRUE; 5109 } 5110 } 5111 } 5112 5113 return FALSE; 5114 } 5115 5116 static gboolean imap_is_busy(Folder *folder) 5117 { 5118 IMAPSession *imap_session; 5119 RemoteFolder *rfolder; 5120 5121 rfolder = (RemoteFolder *) folder; 5122 imap_session = (IMAPSession *) rfolder->session; 5123 if (imap_session == NULL) 5124 return FALSE; 5125 5126 return imap_session->busy; 5127 } 5128 5129 static void imap_synchronise(FolderItem *item, gint days) 5130 { 5131 if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) { 5132 debug_print("%s already synced\n", item->path?item->path:item->name); 5133 return; 5134 } 5135 debug_print("syncing %s\n", item->path?item->path:item->name); 5136 imap_gtk_synchronise(item, days); 5137 IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change; 5138 } 5139 5140 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag) 5141 { 5142 GList *cur; 5143 folder_item_set_xml(folder, item, tag); 5144 5145 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) { 5146 XMLAttr *attr = (XMLAttr *) cur->data; 5147 5148 if (!attr || !attr->name || !attr->value) continue; 5149 if (!strcmp(attr->name, "uidnext")) 5150 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value); 5151 if (!strcmp(attr->name, "last_sync")) 5152 IMAP_FOLDER_ITEM(item)->last_sync = (time_t)atol(attr->value); 5153 if (!strcmp(attr->name, "last_change")) 5154 IMAP_FOLDER_ITEM(item)->last_change = (time_t)atol(attr->value); 5155 } 5156 if (IMAP_FOLDER_ITEM(item)->last_change == 0) 5157 IMAP_FOLDER_ITEM(item)->last_change = time(NULL); 5158 } 5159 5160 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item) 5161 { 5162 XMLTag *tag; 5163 5164 tag = folder_item_get_xml(folder, item); 5165 5166 xml_tag_add_attr(tag, xml_attr_new_int("uidnext", 5167 IMAP_FOLDER_ITEM(item)->uid_next)); 5168 xml_tag_add_attr(tag, xml_attr_new_time_t("last_sync", 5169 IMAP_FOLDER_ITEM(item)->last_sync)); 5170 xml_tag_add_attr(tag, xml_attr_new_time_t("last_change", 5171 IMAP_FOLDER_ITEM(item)->last_change)); 5172 return tag; 5173 } 5174 5175 /* =================================================================== 5176 * UTF-7 conversion routines as in RFC 2192 5177 * =================================================================== 5178 * These two functions from: 5179 * libimap library. 5180 * Copyright (C) 2003-2004 Pawel Salek. */ 5181 5182 /* UTF7 modified base64 alphabet */ 5183 static char base64chars[] = 5184 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; 5185 #define UNDEFINED 64 5186 5187 /* UTF16 definitions */ 5188 #define UTF16MASK 0x03FFUL 5189 #define UTF16SHIFT 10 5190 #define UTF16BASE 0x10000UL 5191 #define UTF16HIGHSTART 0xD800UL 5192 #define UTF16HIGHEND 0xDBFFUL 5193 #define UTF16LOSTART 0xDC00UL 5194 #define UTF16LOEND 0xDFFFUL 5195 5196 5197 /* Convert an IMAP mailbox to a UTF-8 string. 5198 * dst needs to have roughly 4 times the storage space of src 5199 * Hex encoding can triple the size of the input 5200 * UTF-7 can be slightly denser than UTF-8 5201 * (worst case: 8 octets UTF-7 becomes 9 octets UTF-8) 5202 */ 5203 char* imap_modified_utf7_to_utf8(const char *mbox, gboolean change_spaces) 5204 { 5205 unsigned c, i, bitcount; 5206 unsigned long ucs4, utf16, bitbuf; 5207 unsigned char base64[256]; 5208 const char *src; 5209 char *dst, *res = g_malloc(2*strlen(mbox)+1); 5210 5211 dst = res; 5212 src = mbox; 5213 if(!dst) return NULL; 5214 /* initialize modified base64 decoding table */ 5215 memset(base64, UNDEFINED, sizeof (base64)); 5216 for (i = 0; i < sizeof (base64chars); ++i) { 5217 base64[(unsigned)base64chars[i]] = i; 5218 } 5219 5220 /* loop until end of string */ 5221 while (*src != '\0') { 5222 c = *src++; 5223 /* deal with literal characters and &- */ 5224 if (c != '&' || *src == '-') { 5225 /* encode literally */ 5226 if (change_spaces && c == '_') 5227 *dst++ = ' '; 5228 else 5229 *dst++ = c; 5230 /* skip over the '-' if this is an &- sequence */ 5231 if (c == '&') ++src; 5232 } else { 5233 /* convert modified UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> HEX */ 5234 bitbuf = 0; 5235 bitcount = 0; 5236 ucs4 = 0; 5237 while ((c = base64[(unsigned char) *src]) != UNDEFINED) { 5238 ++src; 5239 bitbuf = (bitbuf << 6) | c; 5240 bitcount += 6; 5241 /* enough bits for a UTF-16 character? */ 5242 if (bitcount >= 16) { 5243 bitcount -= 16; 5244 utf16 = (bitcount ? bitbuf >> bitcount 5245 : bitbuf) & 0xffff; 5246 /* convert UTF16 to UCS4 */ 5247 if 5248 (utf16 >= UTF16HIGHSTART && utf16 <= UTF16HIGHEND) { 5249 ucs4 = (utf16 - UTF16HIGHSTART) << UTF16SHIFT; 5250 continue; 5251 } else if 5252 (utf16 >= UTF16LOSTART && utf16 <= UTF16LOEND) { 5253 ucs4 += utf16 - UTF16LOSTART + UTF16BASE; 5254 } else { 5255 ucs4 = utf16; 5256 } 5257 5258 /* convert UTF-16 range of UCS4 to UTF-8 */ 5259 if (ucs4 <= 0x7fUL) { 5260 dst[0] = ucs4; 5261 dst += 1; 5262 } else if (ucs4 <= 0x7ffUL) { 5263 dst[0] = 0xc0 | (ucs4 >> 6); 5264 dst[1] = 0x80 | (ucs4 & 0x3f); 5265 dst += 2; 5266 } else if (ucs4 <= 0xffffUL) { 5267 dst[0] = 0xe0 | (ucs4 >> 12); 5268 dst[1] = 0x80 | ((ucs4 >> 6) & 0x3f); 5269 dst[2] = 0x80 | (ucs4 & 0x3f); 5270 dst += 3; 5271 } else { 5272 dst[0] = 0xf0 | (ucs4 >> 18); 5273 dst[1] = 0x80 | ((ucs4 >> 12) & 0x3f); 5274 dst[2] = 0x80 | ((ucs4 >> 6) & 0x3f); 5275 dst[3] = 0x80 | (ucs4 & 0x3f); 5276 dst += 4; 5277 } 5278 } 5279 } 5280 /* skip over trailing '-' in modified UTF-7 encoding */ 5281 if (*src == '-') ++src; 5282 } 5283 } 5284 /* terminate destination string */ 5285 *dst = '\0'; 5286 return res; 5287 } 5288 5289 /* Convert hex coded UTF-8 string to modified UTF-7 IMAP mailbox 5290 * dst should be about twice the length of src to deal with non-hex 5291 * coded URLs 5292 */ 5293 char* imap_utf8_to_modified_utf7(const char *src, gboolean change_spaces) 5294 { 5295 unsigned int utf8pos, utf8total, c, utf7mode, bitstogo, utf16flag; 5296 unsigned long ucs4 = 0, bitbuf = 0; 5297 5298 /* initialize hex lookup table */ 5299 char *dst, *res; 5300 5301 if (!src) return NULL; 5302 5303 res = malloc(2*strlen(src)+1); 5304 dst = res; 5305 if(!dst) return NULL; 5306 5307 utf7mode = 0; 5308 utf8total = 0; 5309 bitstogo = 0; 5310 utf8pos = 0; 5311 while ((c = (unsigned char)*src) != '\0') { 5312 ++src; 5313 /* normal character? */ 5314 if (c >= ' ' && c <= '~' && (c != '_' || !change_spaces)) { 5315 /* switch out of UTF-7 mode */ 5316 if (utf7mode) { 5317 if (bitstogo) { 5318 *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F]; 5319 } 5320 *dst++ = '-'; 5321 utf7mode = 0; 5322 utf8pos = 0; 5323 bitstogo = 0; 5324 utf8total= 0; 5325 } 5326 if (change_spaces && c == ' ') 5327 *dst++ = '_'; 5328 else 5329 *dst++ = c; 5330 /* encode '&' as '&-' */ 5331 if (c == '&') { 5332 *dst++ = '-'; 5333 } 5334 continue; 5335 } 5336 /* switch to UTF-7 mode */ 5337 if (!utf7mode) { 5338 *dst++ = '&'; 5339 utf7mode = 1; 5340 } 5341 /* Encode US-ASCII characters as themselves */ 5342 if (c < 0x80) { 5343 ucs4 = c; 5344 } else if (utf8total) { 5345 /* save UTF8 bits into UCS4 */ 5346 ucs4 = (ucs4 << 6) | (c & 0x3FUL); 5347 if (++utf8pos < utf8total) { 5348 continue; 5349 } 5350 } else { 5351 utf8pos = 1; 5352 if (c < 0xE0) { 5353 utf8total = 2; 5354 ucs4 = c & 0x1F; 5355 } else if (c < 0xF0) { 5356 utf8total = 3; 5357 ucs4 = c & 0x0F; 5358 } else { 5359 /* NOTE: can't convert UTF8 sequences longer than 4 */ 5360 utf8total = 4; 5361 ucs4 = c & 0x03; 5362 } 5363 continue; 5364 } 5365 /* loop to split ucs4 into two utf16 chars if necessary */ 5366 utf8total = 0; 5367 do { 5368 if (ucs4 >= UTF16BASE) { 5369 ucs4 -= UTF16BASE; 5370 bitbuf = (bitbuf << 16) | ((ucs4 >> UTF16SHIFT) 5371 + UTF16HIGHSTART); 5372 ucs4 = (ucs4 & UTF16MASK) + UTF16LOSTART; 5373 utf16flag = 1; 5374 } else { 5375 bitbuf = (bitbuf << 16) | ucs4; 5376 utf16flag = 0; 5377 } 5378 bitstogo += 16; 5379 /* spew out base64 */ 5380 while (bitstogo >= 6) { 5381 bitstogo -= 6; 5382 *dst++ = base64chars[(bitstogo ? (bitbuf >> bitstogo) 5383 : bitbuf) 5384 & 0x3F]; 5385 } 5386 } while (utf16flag); 5387 } 5388 /* if in UTF-7 mode, finish in ASCII */ 5389 if (utf7mode) { 5390 if (bitstogo) { 5391 *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F]; 5392 } 5393 *dst++ = '-'; 5394 } 5395 /* tie off string */ 5396 *dst = '\0'; 5397 return res; 5398 }