Fix error creation and warning
[claws.git] / src / imap.c
blobff418908acd3d5c384debbc84776f84e807bba3a
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2022 the Claws Mail team and Hiroyuki Yamamoto
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.
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.
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/>.
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #include "claws-features.h"
22 #endif
24 #include "defs.h"
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include "imap.h"
31 #include "imap_gtk.h"
32 #include "inc.h"
33 #include "xml.h"
34 #include "alertpanel.h"
36 #ifdef HAVE_LIBETPAN
38 #include <stdlib.h>
39 #include <dirent.h>
40 #include <unistd.h>
41 #include <ctype.h>
42 #include <time.h>
43 #include <errno.h>
44 #if HAVE_ICONV
45 # include <iconv.h>
46 #endif
48 #ifdef USE_GNUTLS
49 # include "ssl.h"
50 #endif
52 #include "folder.h"
53 #include "session.h"
54 #include "procmsg.h"
55 #include "socket.h"
56 #include "recv.h"
57 #include "procheader.h"
58 #include "prefs_account.h"
59 #include "codeconv.h"
60 #include "md5.h"
61 #include "utils.h"
62 #include "prefs_common.h"
63 #include "inputdialog.h"
64 #include "log.h"
65 #include "remotefolder.h"
66 #include "claws.h"
67 #include "statusbar.h"
68 #include "msgcache.h"
69 #include "imap-thread.h"
70 #include "account.h"
71 #include "tags.h"
72 #include "main.h"
73 #include "passwordstore.h"
74 #include "file-utils.h"
75 #ifdef USE_OAUTH2
76 #include "oauth2.h"
77 #endif
79 typedef struct _IMAPFolder IMAPFolder;
80 typedef struct _IMAPSession IMAPSession;
81 typedef struct _IMAPNameSpace IMAPNameSpace;
82 typedef struct _IMAPFolderItem IMAPFolderItem;
84 #define IMAP_FOLDER(obj) ((IMAPFolder *)obj)
85 #define IMAP_FOLDER_ITEM(obj) ((IMAPFolderItem *)obj)
86 #define IMAP_SESSION(obj) ((IMAPSession *)obj)
88 struct _IMAPFolder
90 RemoteFolder rfolder;
92 /* list of IMAPNameSpace */
93 GList *ns_personal;
94 GList *ns_others;
95 GList *ns_shared;
96 gchar last_seen_separator;
97 guint refcnt;
98 guint max_set_size;
99 gchar *search_charset;
100 gboolean search_charset_supported;
103 struct _IMAPSession
105 Session session;
107 gboolean authenticated;
109 GSList *capability;
110 gboolean uidplus;
112 gchar *mbox;
113 guint cmd_count;
115 /* CLAWS */
116 gboolean folder_content_changed;
117 guint exists;
118 guint recent;
119 guint expunge;
120 guint unseen;
121 guint uid_validity;
122 guint uid_next;
124 Folder * folder;
125 gboolean busy;
126 gboolean cancelled;
127 gboolean sens_update_block;
128 gboolean do_destroy;
130 gint scan_tree_recurs_depth;
133 struct _IMAPNameSpace
135 gchar *name;
136 gchar separator;
139 #define IMAPBUFSIZE 8192
141 #define IMAP_IS_SEEN(flags) ((flags & IMAP_FLAG_SEEN) != 0)
142 #define IMAP_IS_ANSWERED(flags) ((flags & IMAP_FLAG_ANSWERED) != 0)
143 #define IMAP_IS_FLAGGED(flags) ((flags & IMAP_FLAG_FLAGGED) != 0)
144 #define IMAP_IS_DELETED(flags) ((flags & IMAP_FLAG_DELETED) != 0)
145 #define IMAP_IS_DRAFT(flags) ((flags & IMAP_FLAG_DRAFT) != 0)
146 #define IMAP_IS_FORWARDED(flags) ((flags & IMAP_FLAG_FORWARDED) != 0)
147 #define IMAP_IS_SPAM(flags) ((flags & IMAP_FLAG_SPAM) != 0)
148 #define IMAP_IS_HAM(flags) ((flags & IMAP_FLAG_HAM) != 0)
151 #define IMAP4_PORT 143
152 #ifdef USE_GNUTLS
153 #define IMAPS_PORT 993
154 #endif
156 #define IMAP_CMD_LIMIT 1000
158 enum {
159 ITEM_CAN_CREATE_FLAGS_UNKNOWN = 0,
160 ITEM_CAN_CREATE_FLAGS,
161 ITEM_CANNOT_CREATE_FLAGS
164 struct _IMAPFolderItem
166 FolderItem item;
168 guint lastuid;
169 guint uid_next;
170 GSList *uid_list;
171 gboolean batching;
173 GHashTable *flags_set_table;
174 GHashTable *flags_unset_table;
175 guint32 last_change;
176 guint32 last_sync;
177 gboolean should_update;
178 gboolean should_trash_cache;
179 gint can_create_flags;
181 GHashTable *tags_set_table;
182 GHashTable *tags_unset_table;
183 GSList *ok_flags;
187 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
188 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
190 static void imap_folder_init (Folder *folder,
191 const gchar *name,
192 const gchar *path);
194 static Folder *imap_folder_new (const gchar *name,
195 const gchar *path);
196 static void imap_folder_destroy (Folder *folder);
198 static IMAPSession *imap_session_new (Folder *folder,
199 const PrefsAccount *account);
200 static gint imap_session_authenticate(IMAPSession *session,
201 PrefsAccount *account);
202 static void imap_session_destroy (Session *session);
204 static gchar *imap_fetch_msg (Folder *folder,
205 FolderItem *item,
206 gint uid);
207 static gchar *imap_fetch_msg_full (Folder *folder,
208 FolderItem *item,
209 gint uid,
210 gboolean headers,
211 gboolean body);
212 static void imap_remove_cached_msg (Folder *folder,
213 FolderItem *item,
214 MsgInfo *msginfo);
215 static gint imap_add_msg (Folder *folder,
216 FolderItem *dest,
217 const gchar *file,
218 MsgFlags *flags);
219 static gint imap_add_msgs (Folder *folder,
220 FolderItem *dest,
221 GSList *file_list,
222 GHashTable *relation);
224 static gint imap_copy_msg (Folder *folder,
225 FolderItem *dest,
226 MsgInfo *msginfo);
227 static gint imap_copy_msgs (Folder *folder,
228 FolderItem *dest,
229 MsgInfoList *msglist,
230 GHashTable *relation);
232 static gint search_msgs (Folder *folder,
233 FolderItem *container,
234 MsgNumberList **msgs,
235 gboolean *on_server,
236 MatcherList *predicate,
237 SearchProgressNotify progress_cb,
238 gpointer progress_data);
240 static gint imap_remove_msg (Folder *folder,
241 FolderItem *item,
242 gint uid);
243 static gint imap_remove_msgs (Folder *folder,
244 FolderItem *dest,
245 MsgInfoList *msglist,
246 GHashTable *relation);
247 static gint imap_expunge (Folder *folder,
248 FolderItem *dest);
249 static gint imap_remove_all_msg (Folder *folder,
250 FolderItem *item);
252 static gboolean imap_is_msg_changed (Folder *folder,
253 FolderItem *item,
254 MsgInfo *msginfo);
256 static gint imap_close (Folder *folder,
257 FolderItem *item);
259 static gint imap_scan_tree (Folder *folder);
261 static gint imap_create_tree (Folder *folder);
263 static FolderItem *imap_create_folder (Folder *folder,
264 FolderItem *parent,
265 const gchar *name);
266 static gint imap_rename_folder (Folder *folder,
267 FolderItem *item,
268 const gchar *name);
269 static gint imap_remove_folder (Folder *folder,
270 FolderItem *item);
272 static FolderItem *imap_folder_item_new (Folder *folder);
273 static void imap_folder_item_destroy (Folder *folder,
274 FolderItem *item);
276 static IMAPSession *imap_session_get (Folder *folder);
278 static gint imap_auth (IMAPSession *session,
279 const gchar *user,
280 const gchar *pass,
281 IMAPAuthType type);
283 static gint imap_scan_tree_recursive (IMAPSession *session,
284 FolderItem *item,
285 gboolean subs_only);
286 static gint imap_scan_tree_recursive_dive (IMAPSession *session,
287 FolderItem *item,
288 gboolean subs_only);
290 static void imap_create_missing_folders (Folder *folder);
291 static FolderItem *imap_create_special_folder
292 (Folder *folder,
293 SpecialFolderItemType stype,
294 const gchar *name);
296 static gint imap_do_copy_msgs (Folder *folder,
297 FolderItem *dest,
298 MsgInfoList *msglist,
299 GHashTable *relation,
300 gboolean same_dest_ok);
302 static gint imap_do_remove_msgs (Folder *folder,
303 FolderItem *dest,
304 MsgInfoList *msglist,
305 GHashTable *relation);
307 static void imap_delete_all_cached_messages (FolderItem *item);
308 static void imap_set_batch (Folder *folder,
309 FolderItem *item,
310 gboolean batch);
311 static gint imap_set_message_flags (IMAPSession *session,
312 IMAPFolderItem *item,
313 MsgNumberList *numlist,
314 IMAPFlags flags,
315 GSList *tags,
316 gboolean is_set);
317 static gint imap_select (IMAPSession *session,
318 IMAPFolder *folder,
319 FolderItem *item,
320 gint *exists,
321 gint *recent,
322 gint *unseen,
323 guint32 *uid_validity,
324 gint *can_create_flags,
325 gboolean block);
326 static gint imap_status (IMAPSession *session,
327 IMAPFolder *folder,
328 const gchar *path,
329 IMAPFolderItem *item,
330 gint *messages,
331 guint32 *uid_next,
332 guint32 *uid_validity,
333 gint *unseen,
334 gboolean block);
335 static void imap_commit_tags (FolderItem *item,
336 MsgInfo *msginfo,
337 GSList *set_tags,
338 GSList *unset_tags);
340 static gchar imap_get_path_separator (IMAPSession *session,
341 IMAPFolder *folder,
342 const gchar *path,
343 gint *ok);
344 static gchar *imap_get_real_path (IMAPSession *session,
345 IMAPFolder *folder,
346 const gchar *path,
347 gint *ok);
348 #ifdef HAVE_LIBETPAN
349 static void imap_synchronise (FolderItem *item, gint days);
350 #endif
351 static gboolean imap_is_busy (Folder *folder);
353 static void imap_free_capabilities (IMAPSession *session);
355 /* low-level IMAP4rev1 commands */
356 static gint imap_cmd_login (IMAPSession *session,
357 const gchar *user,
358 const gchar *pass,
359 const gchar *type);
360 static gint imap_cmd_noop (IMAPSession *session);
361 #ifdef USE_GNUTLS
362 static gint imap_cmd_starttls (IMAPSession *session);
363 #endif
364 static gint imap_cmd_select (IMAPSession *session,
365 const gchar *folder,
366 gint *exists,
367 gint *recent,
368 gint *unseen,
369 guint32 *uid_validity,
370 gint *can_create_flags,
371 GSList **ok_flags,
372 gboolean block);
373 static gint imap_cmd_close (IMAPSession *session);
374 static gint imap_cmd_examine (IMAPSession *session,
375 const gchar *folder,
376 gint *exists,
377 gint *recent,
378 gint *unseen,
379 guint32 *uid_validity,
380 gboolean block);
381 static gint imap_cmd_create (IMAPSession *sock,
382 const gchar *folder);
383 static gint imap_cmd_rename (IMAPSession *sock,
384 const gchar *oldfolder,
385 const gchar *newfolder);
386 static gint imap_cmd_delete (IMAPSession *session,
387 const gchar *folder);
388 static gint imap_cmd_fetch (IMAPSession *sock,
389 guint32 uid,
390 const gchar *filename,
391 gboolean headers,
392 gboolean body);
393 static gint imap_cmd_append (IMAPSession *session,
394 IMAPFolderItem *item,
395 const gchar *destfolder,
396 const gchar *file,
397 IMAPFlags flags,
398 guint32 *new_uid);
399 static gint imap_cmd_copy (IMAPSession *session,
400 struct mailimap_set * set,
401 const gchar *destfolder,
402 struct mailimap_set ** source,
403 struct mailimap_set ** dest);
404 static gint imap_cmd_store (IMAPSession *session,
405 IMAPFolderItem *item,
406 struct mailimap_set * set,
407 IMAPFlags flags,
408 GSList *tags,
409 int do_add);
410 static gint imap_cmd_expunge (IMAPSession *session);
412 static void imap_path_separator_subst (gchar *str,
413 gchar separator);
415 static gboolean imap_rename_folder_func (GNode *node,
416 gpointer data);
417 static gint imap_get_num_list (Folder *folder,
418 FolderItem *item,
419 GSList **list,
420 gboolean *old_uids_valid);
421 static GSList *imap_get_msginfos (Folder *folder,
422 FolderItem *item,
423 GSList *msgnum_list);
424 static MsgInfo *imap_get_msginfo (Folder *folder,
425 FolderItem *item,
426 gint num);
427 static gboolean imap_scan_required (Folder *folder,
428 FolderItem *item);
429 static void imap_change_flags (Folder *folder,
430 FolderItem *item,
431 MsgInfo *msginfo,
432 MsgPermFlags newflags);
433 static gint imap_get_flags (Folder *folder,
434 FolderItem *item,
435 MsgInfoList *msglist,
436 GHashTable *msgflags);
437 static gchar *imap_item_get_path (Folder *folder,
438 FolderItem *item);
439 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item);
442 /* data types conversion libetpan <-> claws */
443 static GSList * imap_list_from_lep(IMAPFolder * folder,
444 clist * list, const gchar * real_path, gboolean all);
445 static GSList * imap_get_lep_set_from_numlist(IMAPFolder *folder, MsgNumberList *numlist);
446 static GSList * imap_get_lep_set_from_msglist(IMAPFolder *folder, MsgInfoList *msglist);
447 static GSList * imap_uid_list_from_lep(clist * list, gint* length);
448 static GSList * imap_uid_list_from_lep_tab(carray * list);
449 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
450 GHashTable * hash,
451 GHashTable *tags_hash);
452 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
453 FolderItem *item);
454 static void imap_lep_set_free(GSList *seq_list);
455 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFolderItem *item, IMAPFlags flags, GSList *tags);
457 typedef struct _hashtable_data {
458 GSList *msglist;
459 IMAPFolderItem *item;
460 } hashtable_data;
462 static FolderClass imap_class;
464 FolderClass *imap_get_class(void)
466 if (imap_class.idstr == NULL) {
467 imap_class.type = F_IMAP;
468 imap_class.idstr = "imap";
469 imap_class.uistr = "IMAP";
470 imap_class.supports_server_search = TRUE;
472 /* Folder functions */
473 imap_class.new_folder = imap_folder_new;
474 imap_class.destroy_folder = imap_folder_destroy;
475 imap_class.scan_tree = imap_scan_tree;
476 imap_class.create_tree = imap_create_tree;
478 /* FolderItem functions */
479 imap_class.item_new = imap_folder_item_new;
480 imap_class.item_destroy = imap_folder_item_destroy;
481 imap_class.item_get_path = imap_item_get_path;
482 imap_class.create_folder = imap_create_folder;
483 imap_class.rename_folder = imap_rename_folder;
484 imap_class.remove_folder = imap_remove_folder;
485 imap_class.close = imap_close;
486 imap_class.get_num_list = imap_get_num_list;
487 imap_class.scan_required = imap_scan_required;
488 imap_class.set_xml = folder_set_xml;
489 imap_class.get_xml = folder_get_xml;
490 imap_class.item_set_xml = imap_item_set_xml;
491 imap_class.item_get_xml = imap_item_get_xml;
493 /* Message functions */
494 imap_class.get_msginfo = imap_get_msginfo;
495 imap_class.get_msginfos = imap_get_msginfos;
496 imap_class.fetch_msg = imap_fetch_msg;
497 imap_class.fetch_msg_full = imap_fetch_msg_full;
498 imap_class.add_msg = imap_add_msg;
499 imap_class.add_msgs = imap_add_msgs;
500 imap_class.copy_msg = imap_copy_msg;
501 imap_class.copy_msgs = imap_copy_msgs;
502 imap_class.search_msgs = search_msgs;
503 imap_class.remove_msg = imap_remove_msg;
504 imap_class.remove_msgs = imap_remove_msgs;
505 imap_class.expunge = imap_expunge;
506 imap_class.remove_all_msg = imap_remove_all_msg;
507 imap_class.is_msg_changed = imap_is_msg_changed;
508 imap_class.change_flags = imap_change_flags;
509 imap_class.get_flags = imap_get_flags;
510 imap_class.set_batch = imap_set_batch;
511 imap_class.synchronise = imap_synchronise;
512 imap_class.remove_cached_msg = imap_remove_cached_msg;
513 imap_class.commit_tags = imap_commit_tags;
516 return &imap_class;
519 static void imap_refresh_sensitivity (IMAPSession *session)
521 MainWindow *mainwin;
523 if (session->sens_update_block)
524 return;
525 mainwin = mainwindow_get_mainwindow();
526 if (mainwin) {
527 toolbar_main_set_sensitive(mainwin);
528 main_window_set_menu_sensitive(mainwin);
532 static void lock_session(IMAPSession *session)
534 if (session) {
535 debug_print("locking session %p (%d)\n", session, session->busy);
536 if (session->busy)
537 debug_print(" SESSION WAS LOCKED !! \n");
538 session->busy = TRUE;
539 imap_refresh_sensitivity(session);
540 } else {
541 debug_print("can't lock null session\n");
545 static void unlock_session(IMAPSession *session)
547 if (session) {
548 debug_print("unlocking session %p\n", session);
549 session->busy = FALSE;
550 imap_refresh_sensitivity(session);
551 } else {
552 debug_print("can't unlock null session\n");
556 static gboolean imap_ping(gpointer data)
558 Session *session = (Session *)data;
559 IMAPSession *imap_session = IMAP_SESSION(session);
560 int r;
562 if (session->state != SESSION_READY)
563 return FALSE;
564 if (imap_session->busy || !imap_session->authenticated)
565 return TRUE;
567 lock_session(imap_session);
568 r = imap_cmd_noop(imap_session);
569 unlock_session(imap_session);
571 return r == MAILIMAP_NO_ERROR;
574 static void imap_disc_session_destroy(IMAPSession *session)
576 RemoteFolder *rfolder = NULL;
578 if (session == NULL)
579 return;
581 rfolder = REMOTE_FOLDER(IMAP_SESSION(session)->folder);
583 if (rfolder == NULL)
584 return;
585 log_warning(LOG_PROTOCOL, _("IMAP connection broken\n"));
586 SESSION(session)->state = SESSION_DISCONNECTED;
587 SESSION(session)->sock = NULL;
590 static void imap_safe_destroy(IMAPSession *session)
592 if (!session->busy)
593 session_destroy(SESSION(session));
594 else
595 session->do_destroy = TRUE;
598 static gboolean is_fatal(int libetpan_errcode)
600 switch(libetpan_errcode) {
601 case MAILIMAP_ERROR_STREAM:
602 case MAILIMAP_ERROR_PROTOCOL:
603 case MAILIMAP_ERROR_PARSE:
604 case MAILIMAP_ERROR_BAD_STATE:
605 return TRUE;
606 default:
607 return FALSE;
611 #define MY_LOG_WARNING(concat_cmd, ...) \
612 msg = concat_cmd; \
613 log_warning(LOG_PROTOCOL, msg, __VA_ARGS__); \
614 g_free(msg);
616 static void imap_handle_error(Session *session, const gchar *server, int libetpan_errcode)
618 const gchar *session_server = (session ? session->server : NULL);
619 gchar *msg;
621 if (session_server == NULL)
622 session_server = server;
623 if (session_server == NULL)
624 session_server = "(null)";
626 switch(libetpan_errcode) {
627 case MAILIMAP_NO_ERROR:
628 return;
629 case MAILIMAP_NO_ERROR_AUTHENTICATED:
630 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("authenticated"), "\n", NULL), session_server)
631 break;
632 case MAILIMAP_NO_ERROR_NON_AUTHENTICATED:
633 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("not authenticated"), "\n", NULL), session_server)
634 break;
635 case MAILIMAP_ERROR_BAD_STATE:
636 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("bad state"), "\n", NULL), session_server)
637 break;
638 case MAILIMAP_ERROR_STREAM:
639 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("stream error"), "\n", NULL), session_server)
640 break;
641 case MAILIMAP_ERROR_PARSE:
642 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("parse error "
643 "(very probably non-RFC compliance from the server)"), "\n", NULL), session_server)
644 break;
645 case MAILIMAP_ERROR_CONNECTION_REFUSED:
646 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("connection refused"), "\n", NULL), session_server)
647 break;
648 case MAILIMAP_ERROR_MEMORY:
649 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("memory error"), "\n", NULL), session_server)
650 break;
651 case MAILIMAP_ERROR_FATAL:
652 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("fatal error"), "\n", NULL), session_server)
653 break;
654 case MAILIMAP_ERROR_PROTOCOL:
655 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("protocol error "
656 "(very probably non-RFC compliance from the server)"), "\n", NULL), session_server)
657 break;
658 case MAILIMAP_ERROR_DONT_ACCEPT_CONNECTION:
659 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("connection not accepted"), "\n", NULL), session_server)
660 break;
661 case MAILIMAP_ERROR_APPEND:
662 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("APPEND error"), "\n", NULL), session_server)
663 break;
664 case MAILIMAP_ERROR_NOOP:
665 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("NOOP error"), "\n", NULL), session_server)
666 break;
667 case MAILIMAP_ERROR_LOGOUT:
668 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("LOGOUT error"), "\n", NULL), session_server)
669 break;
670 case MAILIMAP_ERROR_CAPABILITY:
671 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("CAPABILITY error"), "\n", NULL), session_server)
672 break;
673 case MAILIMAP_ERROR_CHECK:
674 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("CHECK error"), "\n", NULL), session_server)
675 break;
676 case MAILIMAP_ERROR_CLOSE:
677 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("CLOSE error"), "\n", NULL), session_server)
678 break;
679 case MAILIMAP_ERROR_EXPUNGE:
680 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("EXPUNGE error"), "\n", NULL), session_server)
681 break;
682 case MAILIMAP_ERROR_COPY:
683 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("COPY error"), "\n", NULL), session_server)
684 break;
685 case MAILIMAP_ERROR_UID_COPY:
686 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("UID COPY error"), "\n", NULL), session_server)
687 break;
688 case MAILIMAP_ERROR_CREATE:
689 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("CREATE error"), "\n", NULL), session_server)
690 break;
691 case MAILIMAP_ERROR_DELETE:
692 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("DELETE error"), "\n", NULL), session_server)
693 break;
694 case MAILIMAP_ERROR_EXAMINE:
695 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("EXAMINE error"), "\n", NULL), session_server)
696 break;
697 case MAILIMAP_ERROR_FETCH:
698 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("FETCH error"), "\n", NULL), session_server)
699 break;
700 case MAILIMAP_ERROR_UID_FETCH:
701 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("UID FETCH error"), "\n", NULL), session_server)
702 break;
703 case MAILIMAP_ERROR_LIST:
704 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("LIST error"), "\n", NULL), session_server)
705 break;
706 case MAILIMAP_ERROR_LOGIN:
707 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("LOGIN error"), "\n", NULL), session_server)
708 break;
709 case MAILIMAP_ERROR_LSUB:
710 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("LSUB error"), "\n", NULL), session_server)
711 break;
712 case MAILIMAP_ERROR_RENAME:
713 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("RENAME error"), "\n", NULL), session_server)
714 break;
715 case MAILIMAP_ERROR_SEARCH:
716 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("SEARCH error"), "\n", NULL), session_server)
717 break;
718 case MAILIMAP_ERROR_UID_SEARCH:
719 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("UID SEARCH error"), "\n", NULL), session_server)
720 break;
721 case MAILIMAP_ERROR_SELECT:
722 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("SELECT error"), "\n", NULL), session_server)
723 break;
724 case MAILIMAP_ERROR_STATUS:
725 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("STATUS error"), "\n", NULL), session_server)
726 break;
727 case MAILIMAP_ERROR_STORE:
728 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("STORE error"), "\n", NULL), session_server)
729 break;
730 case MAILIMAP_ERROR_UID_STORE:
731 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("UID STORE error"), "\n", NULL), session_server)
732 break;
733 case MAILIMAP_ERROR_SUBSCRIBE:
734 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("SUBSCRIBE error"), "\n", NULL), session_server)
735 break;
736 case MAILIMAP_ERROR_UNSUBSCRIBE:
737 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("UNSUBSCRIBE error"), "\n", NULL), session_server)
738 break;
739 case MAILIMAP_ERROR_STARTTLS:
740 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("STARTTLS error"), "\n", NULL), session_server)
741 break;
742 case MAILIMAP_ERROR_INVAL:
743 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("INVAL error"), "\n", NULL), session_server)
744 break;
745 case MAILIMAP_ERROR_EXTENSION:
746 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("EXTENSION error"), "\n", NULL), session_server)
747 break;
748 case MAILIMAP_ERROR_SASL:
749 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("SASL error"), "\n", NULL), session_server)
750 break;
751 #ifdef USE_GNUTLS
752 case MAILIMAP_ERROR_SSL:
753 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("TLS error"), "\n", NULL), session_server)
754 break;
755 #endif
756 default:
757 MY_LOG_WARNING(g_strconcat(_("IMAP error on %s:"), " ", _("Unknown error [%d]"), "\n", NULL),
758 session_server, libetpan_errcode)
759 break;
762 if (session && is_fatal(libetpan_errcode)) {
763 imap_disc_session_destroy(IMAP_SESSION(session));
764 } else if (session && !is_fatal(libetpan_errcode)) {
765 if (IMAP_SESSION(session)->busy)
766 unlock_session(IMAP_SESSION(session));
770 #undef MY_LOG_WARNING
772 static Folder *imap_folder_new(const gchar *name, const gchar *path)
774 Folder *folder;
776 folder = (Folder *)g_new0(IMAPFolder, 1);
777 folder->klass = &imap_class;
778 imap_folder_init(folder, name, path);
780 return folder;
783 static void imap_folder_destroy(Folder *folder)
785 while (imap_folder_get_refcnt(folder) > 0)
786 gtk_main_iteration();
788 g_free(IMAP_FOLDER(folder)->search_charset);
790 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
791 imap_done(folder);
794 static void imap_folder_init(Folder *folder, const gchar *name,
795 const gchar *path)
797 folder_remote_folder_init((Folder *)folder, name, path);
798 IMAP_FOLDER(folder)->max_set_size = IMAP_SET_MAX_COUNT;
799 IMAP_FOLDER(folder)->search_charset_supported = TRUE;
800 IMAP_FOLDER(folder)->search_charset = g_strdup(conv_get_locale_charset_str_no_utf8());
803 static FolderItem *imap_folder_item_new(Folder *folder)
805 IMAPFolderItem *item;
807 item = g_new0(IMAPFolderItem, 1);
808 item->lastuid = 0;
809 item->uid_next = 0;
810 item->uid_list = NULL;
812 return (FolderItem *)item;
815 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
817 IMAPFolderItem *item = (IMAPFolderItem *)_item;
819 g_return_if_fail(item != NULL);
820 g_slist_free(item->uid_list);
822 g_free(_item);
825 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
827 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
829 item->lastuid = 0;
830 g_slist_free(item->uid_list);
831 item->uid_list = NULL;
833 return FALSE;
836 static void imap_reset_uid_lists(Folder *folder)
838 if(folder->node == NULL)
839 return;
841 /* Destroy all uid lists and rest last uid */
842 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
845 static int imap_get_capabilities(IMAPSession *session)
847 struct mailimap_capability_data *capabilities = NULL;
848 clistiter *cur;
849 int result;
851 if (session->capability != NULL)
852 return MAILIMAP_NO_ERROR;
854 result = imap_threaded_capability(session->folder, &capabilities);
856 if (result != MAILIMAP_NO_ERROR) {
857 return result;
860 if (capabilities == NULL || capabilities->cap_list == NULL) {
861 return MAILIMAP_NO_ERROR;
864 for(cur = clist_begin(capabilities->cap_list) ; cur != NULL ;
865 cur = clist_next(cur)) {
866 struct mailimap_capability * cap =
867 clist_content(cur);
868 if (!cap || cap->cap_data.cap_name == NULL)
869 continue;
870 session->capability = g_slist_append
871 (session->capability,
872 g_strdup(cap->cap_data.cap_name));
873 debug_print("got capa %s\n", cap->cap_data.cap_name);
875 mailimap_capability_data_free(capabilities);
876 return MAILIMAP_NO_ERROR;
879 static gboolean imap_has_capability(IMAPSession *session, const gchar *cap)
881 GSList *cur;
882 for (cur = session->capability; cur; cur = cur->next) {
883 if (!g_ascii_strcasecmp(cur->data, cap))
884 return TRUE;
886 return FALSE;
889 static gint imap_auth(IMAPSession *session, const gchar *user, const gchar *pass,
890 IMAPAuthType type)
892 gint ok = MAILIMAP_ERROR_LOGIN;
893 static time_t last_login_err = 0;
894 gchar *ext_info = "";
895 int r;
896 gchar *server = NULL;
897 if ((r = imap_get_capabilities(session)) != MAILIMAP_NO_ERROR) {
898 imap_handle_error(SESSION(session), NULL, r);
899 return r;
901 server = g_strdup(SESSION(session)->server);
902 switch(type) {
903 case IMAP_AUTH_ANON:
904 ok = imap_cmd_login(session, user, pass, "ANONYMOUS");
905 break;
906 case IMAP_AUTH_CRAM_MD5:
907 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
908 break;
909 case IMAP_AUTH_DIGEST_MD5:
910 ok = imap_cmd_login(session, user, pass, "DIGEST-MD5");
911 break;
912 case IMAP_AUTH_SCRAM_SHA1:
913 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-1");
914 break;
915 case IMAP_AUTH_PLAIN:
916 ok = imap_cmd_login(session, user, pass, "PLAIN");
917 break;
918 #ifdef USE_GNUTLS
919 case IMAP_AUTH_OAUTH2:
920 ok = imap_cmd_login(session, user, pass, "XOAUTH2");
921 break;
922 #endif
923 case IMAP_AUTH_LOGIN:
924 ok = imap_cmd_login(session, user, pass, "LOGIN");
925 break;
926 case IMAP_AUTH_PLAINTEXT:
927 ok = imap_cmd_login(session, user, pass, "plaintext");
928 break;
929 case IMAP_AUTH_GSSAPI:
930 ok = imap_cmd_login(session, user, pass, "GSSAPI");
931 break;
932 default:
933 debug_print("capabilities:\n"
934 "\t ANONYMOUS %d\n"
935 "\t CRAM-MD5 %d\n"
936 "\t DIGEST-MD5 %d\n"
937 "\t SCRAM-SHA-1 %d\n"
938 "\t PLAIN %d\n"
939 #ifdef USE_GNUTLS
940 "\t OAUTH2 %d\n"
941 #endif
942 "\t LOGIN %d\n"
943 "\t GSSAPI %d\n",
944 imap_has_capability(session, "ANONYMOUS"),
945 imap_has_capability(session, "CRAM-MD5"),
946 imap_has_capability(session, "DIGEST-MD5"),
947 imap_has_capability(session, "SCRAM-SHA-1"),
948 imap_has_capability(session, "PLAIN"),
949 #ifdef USE_GNUTLS
950 imap_has_capability(session, "XOAUTH2"),
951 #endif
952 imap_has_capability(session, "LOGIN"),
953 imap_has_capability(session, "GSSAPI"));
954 if (imap_has_capability(session, "CRAM-MD5"))
955 ok = imap_cmd_login(session, user, pass, "CRAM-MD5");
956 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "DIGEST-MD5"))
957 ok = imap_cmd_login(session, user, pass, "DIGEST-MD5");
958 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "SCRAM-SHA-1"))
959 ok = imap_cmd_login(session, user, pass, "SCRAM-SHA-1");
960 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "PLAIN"))
961 ok = imap_cmd_login(session, user, pass, "PLAIN");
962 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "LOGIN"))
963 ok = imap_cmd_login(session, user, pass, "LOGIN");
964 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "GSSAPI"))
965 ok = imap_cmd_login(session, user, pass, "GSSAPI");
966 if (ok == MAILIMAP_ERROR_LOGIN) /* we always try plaintext login before giving up */
967 ok = imap_cmd_login(session, user, pass, "plaintext");
968 #ifdef USE_GNUTLS
969 if (ok == MAILIMAP_ERROR_LOGIN && imap_has_capability(session, "XOAUTH2"))
970 ok = imap_cmd_login(session, user, pass, "XOAUTH2");
971 #endif
974 if (ok == MAILIMAP_NO_ERROR)
975 session->authenticated = TRUE;
976 else {
977 if (type == IMAP_AUTH_CRAM_MD5) {
978 ext_info = _("\n\nCRAM-MD5 logins only work if libetpan has been "
979 "compiled with SASL support and the "
980 "CRAM-MD5 SASL plugin is installed.");
983 if (type == IMAP_AUTH_DIGEST_MD5) {
984 ext_info = _("\n\nDIGEST-MD5 logins only work if libetpan has been "
985 "compiled with SASL support and the "
986 "DIGEST-MD5 SASL plugin is installed.");
989 if (type == IMAP_AUTH_SCRAM_SHA1) {
990 ext_info = _("\n\nSCRAM-SHA-1 logins only work if libetpan has been "
991 "compiled with SASL support and the "
992 "SCRAM SASL plugin is installed.");
995 if (type == IMAP_AUTH_PLAIN) {
996 ext_info = _("\n\nPLAIN logins only work if libetpan has been "
997 "compiled with SASL support and the "
998 "PLAIN SASL plugin is installed.");
1001 if (type == IMAP_AUTH_LOGIN) {
1002 ext_info = _("\n\nLOGIN logins only work if libetpan has been "
1003 "compiled with SASL support and the "
1004 "LOGIN SASL plugin is installed.");
1006 #ifdef USE_GNUTLS
1007 if (type == IMAP_AUTH_OAUTH2) {
1008 ext_info = _("\n\nOAuth2 error. Check and correct your OAuth2 "
1009 "account preferences.");
1011 #endif
1012 if (time(NULL) - last_login_err > 10) {
1013 if (!prefs_common.no_recv_err_panel) {
1014 alertpanel_error_log(_("Connection to %s failed: "
1015 "login refused.%s"),
1016 server, ext_info);
1017 } else {
1018 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
1019 "login refused.%s\n"),
1020 server, ext_info);
1023 last_login_err = time(NULL);
1025 g_free(server);
1026 return ok;
1029 static IMAPSession *imap_reconnect_if_possible(Folder *folder, IMAPSession *session)
1031 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
1032 /* Check if this is the first try to establish a
1033 connection, if yes we don't try to reconnect */
1034 debug_print("reconnecting\n");
1035 if (rfolder->session == NULL) {
1036 log_warning(LOG_PROTOCOL, _("Connecting to %s failed"),
1037 folder->account->recv_server);
1038 SESSION(session)->sock = NULL;
1039 imap_safe_destroy(session);
1040 session = NULL;
1041 } else {
1042 rfolder->session = NULL;
1043 log_warning(LOG_PROTOCOL, _("IMAP connection to %s has been"
1044 " disconnected. Reconnecting...\n"),
1045 folder->account->recv_server);
1046 statusbar_print_all(_("IMAP connection to %s has been"
1047 " disconnected. Reconnecting...\n"),
1048 folder->account->recv_server);
1049 SESSION(session)->state = SESSION_DISCONNECTED;
1050 SESSION(session)->sock = NULL;
1051 imap_safe_destroy(session);
1052 /* Clear folders session to make imap_session_get create
1053 a new session, because of rfolder->session == NULL
1054 it will not try to reconnect again and so avoid an
1055 endless loop */
1056 debug_print("getting session...\n");
1057 session = imap_session_get(folder);
1058 rfolder->session = SESSION(session);
1059 statusbar_pop_all();
1061 return session;
1064 static IMAPSession *imap_session_get(Folder *folder)
1066 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
1067 IMAPSession *session = NULL;
1068 gint r = MAILIMAP_NO_ERROR;
1070 g_return_val_if_fail(folder != NULL, NULL);
1071 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, NULL);
1072 g_return_val_if_fail(folder->account != NULL, NULL);
1074 if (prefs_common.work_offline &&
1075 !inc_offline_should_override(FALSE,
1076 _("Claws Mail needs network access in order "
1077 "to access the IMAP server."))) {
1078 return NULL;
1081 /* check for deferred destroy */
1082 if (rfolder->session != NULL) {
1083 session = IMAP_SESSION(rfolder->session);
1084 if (!session->busy && session->do_destroy) {
1085 rfolder->session = NULL;
1086 imap_safe_destroy(session);
1087 session = NULL;
1091 /* Make sure we have a session */
1092 if (rfolder->session != NULL && rfolder->session->state != SESSION_DISCONNECTED) {
1093 session = IMAP_SESSION(rfolder->session);
1094 } else if (rfolder->session != NULL && rfolder->session->state == SESSION_DISCONNECTED) {
1095 imap_safe_destroy(IMAP_SESSION(rfolder->session));
1096 rfolder->session = NULL;
1097 goto new_conn;
1098 } else if (rfolder->connecting) {
1099 debug_print("already connecting\n");
1100 return NULL;
1101 } else {
1102 new_conn:
1103 imap_reset_uid_lists(folder);
1104 if (time(NULL) - rfolder->last_failure <= 2)
1105 return NULL;
1106 rfolder->connecting = TRUE;
1107 session = imap_session_new(folder, folder->account);
1109 if(session == NULL) {
1110 rfolder->last_failure = time(NULL);
1111 rfolder->connecting = FALSE;
1112 return NULL;
1115 /* Make sure session is authenticated */
1116 if (!IMAP_SESSION(session)->authenticated)
1117 r = imap_session_authenticate(IMAP_SESSION(session), folder->account);
1119 if (r != MAILIMAP_NO_ERROR || (!is_fatal(r) && !IMAP_SESSION(session)->authenticated)) {
1120 rfolder->session = NULL;
1121 if (!is_fatal(r)) {
1122 imap_threaded_disconnect(session->folder);
1124 SESSION(session)->state = SESSION_DISCONNECTED;
1125 SESSION(session)->sock = NULL;
1126 imap_safe_destroy(session);
1127 rfolder->last_failure = time(NULL);
1128 rfolder->connecting = FALSE;
1129 return NULL;
1132 /* I think the point of this code is to avoid sending a
1133 * keepalive if we've used the session recently and therefore
1134 * think it's still alive. Unfortunately, most of the code
1135 * does not yet check for errors on the socket, and so if the
1136 * connection drops we don't notice until the timeout expires.
1137 * A better solution than sending a NOOP every time would be
1138 * for every command to be prepared to retry until it is
1139 * successfully sent. -- mbp */
1140 if ((time(NULL) - SESSION(session)->last_access_time > SESSION_TIMEOUT_INTERVAL) || session->cancelled) {
1141 /* verify that the session is still alive */
1142 r = imap_cmd_noop(session);
1144 if (r != MAILIMAP_NO_ERROR) {
1145 debug_print("disconnected!\n");
1146 if (!is_fatal(r))
1147 session = imap_reconnect_if_possible(folder, session);
1148 else {
1149 rfolder->session = NULL;
1150 rfolder->connecting = FALSE;
1151 SESSION(session)->state = SESSION_DISCONNECTED;
1152 SESSION(session)->sock = NULL;
1153 imap_safe_destroy(session);
1154 session = imap_session_get(folder);
1157 if (session)
1158 session->cancelled = FALSE;
1161 rfolder->session = SESSION(session);
1162 rfolder->connecting = FALSE;
1164 return IMAP_SESSION(session);
1167 static IMAPSession *imap_session_new(Folder * folder,
1168 const PrefsAccount *account)
1170 IMAPSession *session;
1171 ProxyInfo *proxy_info = NULL;
1172 gushort port;
1173 int r;
1174 int authenticated = FALSE;
1175 gchar *buf;
1177 #ifdef USE_GNUTLS
1178 /* FIXME: IMAP over SSL only... */
1179 SSLType ssl_type;
1181 port = account->set_imapport ? account->imapport
1182 : account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
1183 ssl_type = account->ssl_imap;
1184 #else
1185 if (account->ssl_imap != SSL_NONE) {
1186 if (alertpanel_full(_("Insecure connection"),
1187 _("This connection is configured to be secured "
1188 "using TLS, but TLS is not available "
1189 "in this build of Claws Mail. \n\n"
1190 "Do you want to continue connecting to this "
1191 "server? The communication would not be "
1192 "secure."),
1193 NULL, _("_Cancel"), NULL, _("Con_tinue connecting"), NULL, NULL,
1194 ALERTFOCUS_FIRST, FALSE, NULL, ALERT_WARNING) != G_ALERTALTERNATE)
1195 return NULL;
1197 port = account->set_imapport ? account->imapport
1198 : IMAP4_PORT;
1199 #endif
1201 imap_init(folder);
1202 buf = g_strdup_printf(_("Account '%s': Connecting to IMAP server: %s:%d..."),
1203 folder->account->account_name, folder->account->recv_server,
1204 port);
1205 statusbar_print_all("%s", buf);
1206 log_message(LOG_PROTOCOL, "%s\n", buf);
1207 g_free(buf);
1209 if (account->use_proxy) {
1210 if (account->use_default_proxy) {
1211 proxy_info = (ProxyInfo *)&(prefs_common.proxy_info);
1212 if (proxy_info->use_proxy_auth)
1213 proxy_info->proxy_pass = passwd_store_get(PWS_CORE, PWS_CORE_PROXY,
1214 PWS_CORE_PROXY_PASS);
1215 } else {
1216 proxy_info = (ProxyInfo *)&(account->proxy_info);
1217 if (proxy_info->use_proxy_auth)
1218 proxy_info->proxy_pass = passwd_store_get_account(account->account_id,
1219 PWS_ACCOUNT_PROXY_PASS);
1223 #ifndef G_OS_WIN32
1224 if (account->set_tunnelcmd) {
1225 r = imap_threaded_connect_cmd(folder,
1226 account->tunnelcmd,
1227 account->recv_server,
1228 port);
1230 else
1231 #endif
1233 #ifdef USE_GNUTLS
1235 if (ssl_type == SSL_TUNNEL) {
1236 r = imap_threaded_connect_ssl(folder,
1237 account->recv_server,
1238 port,
1239 proxy_info);
1241 else
1242 #endif
1244 r = imap_threaded_connect(folder,
1245 account->recv_server,
1246 port,
1247 proxy_info);
1251 statusbar_pop_all();
1252 if (r == MAILIMAP_NO_ERROR_AUTHENTICATED) {
1253 authenticated = TRUE;
1255 else if (r == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) {
1256 authenticated = FALSE;
1258 else {
1259 #ifdef USE_GNUTLS
1260 if (r == MAILIMAP_ERROR_SSL)
1261 log_error(LOG_PROTOCOL, _("TLS handshake failed\n"));
1262 else
1263 #endif
1264 imap_handle_error(NULL, account->recv_server, r);
1266 if(!prefs_common.no_recv_err_panel) {
1267 alertpanel_error_log(_("Can't connect to IMAP server: %s:%d"),
1268 account->recv_server, port);
1269 } else {
1270 log_error(LOG_PROTOCOL, _("Can't connect to IMAP server: %s:%d\n"),
1271 account->recv_server, port);
1274 return NULL;
1277 session = g_new0(IMAPSession, 1);
1278 session_init(SESSION(session), account, FALSE);
1279 SESSION(session)->type = SESSION_IMAP;
1280 SESSION(session)->server = g_strdup(account->recv_server);
1281 SESSION(session)->port = port;
1282 SESSION(session)->sock = NULL;
1283 SESSION(session)->proxy_info = proxy_info;
1284 SESSION(session)->destroy = imap_session_destroy;
1286 session->capability = NULL;
1287 session->authenticated = authenticated;
1288 session->mbox = NULL;
1289 session->exists = 0;
1290 session->recent = 0;
1291 session->expunge = 0;
1292 session->cmd_count = 0;
1293 session->folder = folder;
1294 IMAP_FOLDER(session->folder)->last_seen_separator = 0;
1296 #ifdef USE_GNUTLS
1297 if (account->ssl_imap == SSL_STARTTLS) {
1298 gint ok;
1300 ok = imap_cmd_starttls(session);
1301 if (ok != MAILIMAP_NO_ERROR) {
1302 log_warning(LOG_PROTOCOL, _("Can't start STARTTLS session.\n"));
1303 if (!is_fatal(ok)) {
1304 SESSION(session)->sock = NULL;
1305 imap_safe_destroy(session);
1307 return NULL;
1310 imap_free_capabilities(session);
1311 session->authenticated = FALSE;
1312 session->uidplus = FALSE;
1313 session->cmd_count = 1;
1315 SESSION(session)->use_tls_sni = account->use_tls_sni;
1316 #endif
1318 log_message(LOG_PROTOCOL, "IMAP connection is %s-authenticated\n",
1319 (session->authenticated) ? "pre" : "un");
1321 session_register_ping(SESSION(session), imap_ping);
1323 return session;
1326 static gint imap_session_authenticate(IMAPSession *session,
1327 PrefsAccount *account)
1329 gchar *pass, *acc_pass = NULL;
1330 gboolean failed = FALSE;
1331 gint ok = MAILIMAP_NO_ERROR;
1332 g_return_val_if_fail(account->userid != NULL, MAILIMAP_ERROR_BAD_STATE);
1333 #ifdef USE_OAUTH2
1334 if(account->imap_auth_type == IMAP_AUTH_OAUTH2)
1335 oauth2_check_passwds (account);
1336 #endif
1337 if (!password_get(account->userid, account->recv_server, "imap",
1338 SESSION(session)->port, &acc_pass)) {
1339 acc_pass = passwd_store_get_account(account->account_id,
1340 PWS_ACCOUNT_RECV);
1343 try_again:
1344 pass = acc_pass;
1345 if (!pass && account->imap_auth_type != IMAP_AUTH_ANON && account->imap_auth_type != IMAP_AUTH_GSSAPI) {
1346 gchar *tmp_pass;
1347 tmp_pass = input_dialog_query_password_keep(account->recv_server,
1348 account->userid,
1349 &(account->session_passwd));
1350 if (!tmp_pass) {
1351 return MAILIMAP_NO_ERROR;
1353 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return MAILIMAP_NO_ERROR;});
1354 g_free(tmp_pass);
1355 } else if (account->imap_auth_type == IMAP_AUTH_ANON || account->imap_auth_type == IMAP_AUTH_GSSAPI) {
1356 pass = "";
1358 if ((ok = imap_auth(session, account->userid, pass, account->imap_auth_type)) != MAILIMAP_NO_ERROR) {
1360 if (!failed && !is_fatal(ok)) {
1361 if (acc_pass != NULL) {
1362 memset(acc_pass, 0, strlen(acc_pass));
1363 g_free(acc_pass);
1364 acc_pass = NULL;
1366 failed = TRUE;
1367 if (account->session_passwd != NULL) {
1368 g_free(account->session_passwd);
1369 account->session_passwd = NULL;
1371 goto try_again;
1372 } else {
1373 if (prefs_common.no_recv_err_panel) {
1374 log_error(LOG_PROTOCOL, _("Couldn't login to IMAP server %s.\n"), account->recv_server);
1375 mainwindow_show_error();
1376 } else
1377 alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server);
1380 if (acc_pass != NULL) {
1381 memset(acc_pass, 0, strlen(acc_pass));
1382 g_free(acc_pass);
1383 acc_pass = NULL;
1386 return ok;
1389 if (acc_pass) {
1390 memset(acc_pass, 0, strlen(acc_pass));
1391 g_free(acc_pass);
1393 statusbar_pop_all();
1394 session->authenticated = TRUE;
1395 return MAILIMAP_NO_ERROR;
1398 static void imap_session_destroy(Session *session)
1400 if (session->state != SESSION_DISCONNECTED)
1401 imap_threaded_disconnect(IMAP_SESSION(session)->folder);
1403 imap_free_capabilities(IMAP_SESSION(session));
1404 g_free(IMAP_SESSION(session)->mbox);
1407 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
1409 return imap_fetch_msg_full(folder, item, uid, TRUE, TRUE);
1412 static guint get_file_size_with_crs(const gchar *filename)
1414 FILE *fp = NULL;
1415 guint cnt = 0;
1416 gchar buf[4096];
1418 if (filename == NULL)
1419 return -1;
1421 fp = claws_fopen(filename, "rb");
1422 if (!fp)
1423 return -1;
1425 while (claws_fgets(buf, sizeof (buf), fp) != NULL) {
1426 cnt += strlen(buf);
1427 if (!strstr(buf, "\r\n") && strstr(buf, "\n"))
1428 cnt++;
1431 claws_fclose(fp);
1432 return cnt;
1435 static gchar *imap_get_cached_filename(FolderItem *item, guint msgnum)
1437 gchar *path, *filename;
1439 cm_return_val_if_fail(item != NULL, NULL);
1441 path = folder_item_get_path(item);
1443 if (!is_dir_exist(path)) {
1444 g_free(path);
1445 return NULL;
1448 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(msgnum), NULL);
1450 if (is_dir_exist(filename)) {
1451 g_free(filename);
1452 filename = g_strconcat(path, G_DIR_SEPARATOR_S, ".", itos(msgnum), NULL);
1454 g_free(path);
1456 return filename;
1459 static void imap_remove_cached_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1461 gchar *filename;
1463 filename = imap_get_cached_filename(item, msginfo->msgnum);
1465 cm_return_if_fail(filename != NULL);
1467 if (is_file_exist(filename)) {
1468 claws_unlink(filename);
1470 g_free(filename);
1473 typedef struct _TagsData {
1474 gchar *str;
1475 GSList *msglist;
1476 IMAPFolderItem *item;
1477 } TagsData;
1479 static void imap_commit_tags(FolderItem *item, MsgInfo *msginfo, GSList *tags_set, GSList *tags_unset)
1481 IMAPSession *session;
1482 gint ok, can_create_tags;
1483 Folder *folder = NULL;
1484 TagsData *ht_data = NULL;
1485 GSList *cur;
1487 g_return_if_fail(item != NULL);
1488 g_return_if_fail(msginfo != NULL);
1490 folder = item->folder;
1491 debug_print("getting session...\n");
1492 session = imap_session_get(folder);
1494 if (!session) {
1495 debug_print("can't get session\n");
1496 return;
1499 ok = imap_select(session, IMAP_FOLDER(folder), item,
1500 NULL, NULL, NULL, NULL, &can_create_tags, FALSE);
1502 if (ok != MAILIMAP_NO_ERROR) {
1503 return;
1507 if (IMAP_FOLDER_ITEM(item)->can_create_flags != ITEM_CAN_CREATE_FLAGS)
1508 return;
1510 if (IMAP_FOLDER_ITEM(item)->batching) {
1511 /* instead of performing an UID STORE command for each message change,
1512 * as a lot of them can change "together", we just fill in hashtables
1513 * and defer the treatment so that we're able to send only one
1514 * command.
1516 debug_print("IMAP batch mode on, deferring tags change\n");
1517 for (cur = tags_set; cur; cur = cur->next) {
1518 gint cur_tag = GPOINTER_TO_INT(cur->data);
1519 if (cur_tag) {
1520 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->tags_set_table,
1521 GINT_TO_POINTER(cur_tag));
1522 if (ht_data == NULL) {
1523 ht_data = g_new0(TagsData, 1);
1524 ht_data->str = g_strdup(tags_get_tag(cur_tag));
1525 ht_data->item = IMAP_FOLDER_ITEM(item);
1526 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->tags_set_table,
1527 GINT_TO_POINTER(cur_tag), ht_data);
1529 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
1532 for (cur = tags_unset; cur; cur = cur->next) {
1533 gint cur_tag = GPOINTER_TO_INT(cur->data);
1534 if (cur_tag) {
1535 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->tags_unset_table,
1536 GINT_TO_POINTER(cur_tag));
1537 if (ht_data == NULL) {
1538 ht_data = g_new0(TagsData, 1);
1539 ht_data->str = g_strdup(tags_get_tag(cur_tag));
1540 ht_data->item = IMAP_FOLDER_ITEM(item);
1541 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->tags_unset_table,
1542 GINT_TO_POINTER(cur_tag), ht_data);
1544 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
1547 } else {
1548 GSList *list_set = NULL;
1549 GSList *list_unset = NULL;
1550 GSList numlist;
1552 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
1553 numlist.next = NULL;
1555 debug_print("IMAP changing tags NOW\n");
1556 for (cur = tags_set; cur; cur = cur->next) {
1557 gint cur_tag = GPOINTER_TO_INT(cur->data);
1558 const gchar *str = tags_get_tag(cur_tag);
1559 if (IS_NOT_RESERVED_TAG(str))
1560 list_set = g_slist_prepend(list_set, g_strdup(str));
1562 if (list_set) {
1563 ok = imap_set_message_flags(session,
1564 IMAP_FOLDER_ITEM(item), &numlist, 0, list_set, TRUE);
1565 slist_free_strings_full(list_set);
1566 if (ok != MAILIMAP_NO_ERROR) {
1567 return;
1571 for (cur = tags_unset; cur; cur = cur->next) {
1572 gint cur_tag = GPOINTER_TO_INT(cur->data);
1573 const gchar *str = tags_get_tag(cur_tag);
1574 if (IS_NOT_RESERVED_TAG(str))
1575 list_unset = g_slist_prepend(list_unset, g_strdup(str));
1577 if (list_unset) {
1578 ok = imap_set_message_flags(session,
1579 IMAP_FOLDER_ITEM(item), &numlist, 0, list_unset, FALSE);
1580 slist_free_strings_full(list_unset);
1581 if (ok != MAILIMAP_NO_ERROR) {
1582 return;
1588 static gchar *imap_fetch_msg_full(Folder *folder, FolderItem *item, gint uid,
1589 gboolean headers, gboolean body)
1591 gchar *path, *filename;
1592 IMAPSession *session;
1593 gint ok;
1595 g_return_val_if_fail(folder != NULL, NULL);
1596 g_return_val_if_fail(item != NULL, NULL);
1598 if (uid == 0)
1599 return NULL;
1601 path = folder_item_get_path(item);
1602 if (!is_dir_exist(path)) {
1603 if(is_file_exist(path))
1604 claws_unlink(path);
1605 make_dir_hier(path);
1607 g_free(path);
1609 filename = imap_get_cached_filename(item, uid);
1610 debug_print("trying to fetch cached %s\n", filename);
1612 if (is_file_exist(filename)) {
1613 /* see whether the local file represents the whole message
1614 * or not. As the IMAP server reports size with \r chars,
1615 * we have to update the local file (UNIX \n only) size */
1616 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1617 guint have_size = -1;
1619 if (cached)
1620 debug_print("message %d has been already %scached.\n", uid,
1621 MSG_IS_FULLY_CACHED(cached->flags) ? "fully ":"");
1623 if (!cached || !MSG_IS_FULLY_CACHED(cached->flags)) {
1624 have_size = get_file_size_with_crs(filename);
1625 if (cached && (cached->size <= have_size || !body)) {
1626 ok = file_strip_crs(filename);
1627 if (ok == 0 && cached && cached->size <= have_size) {
1628 /* we have it all and stripped */
1629 debug_print("...fully cached in fact (%u/%"G_GOFFSET_FORMAT"); setting flag.\n",
1630 have_size, cached->size);
1631 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1633 procmsg_msginfo_free(&cached);
1634 return filename;
1635 } else if (!cached && time(NULL) - get_file_mtime(filename) < 60) {
1636 debug_print("message not cached and file recent, considering file complete\n");
1637 ok = file_strip_crs(filename);
1638 if (ok == 0)
1639 return filename;
1640 } else {
1641 procmsg_msginfo_free(&cached);
1644 if (cached && MSG_IS_FULLY_CACHED(cached->flags)) {
1645 procmsg_msginfo_free(&cached);
1646 return filename;
1648 } else {
1649 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1650 if (cached) {
1651 procmsg_msginfo_unset_flags(cached, MSG_FULLY_CACHED, 0);
1652 procmsg_msginfo_free(&cached);
1656 debug_print("getting session...\n");
1657 session = imap_session_get(folder);
1659 if (!session) {
1660 g_free(filename);
1661 return NULL;
1663 session_set_access_time(SESSION(session));
1664 lock_session(session); /* unlocked later in the function */
1666 debug_print("IMAP fetching messages\n");
1667 ok = imap_select(session, IMAP_FOLDER(folder), item,
1668 NULL, NULL, NULL, NULL, NULL, FALSE);
1669 if (ok != MAILIMAP_NO_ERROR) {
1670 g_warning("can't select mailbox %s", item->path);
1671 g_free(filename);
1672 return NULL;
1675 session_set_access_time(SESSION(session));
1677 debug_print("getting message %d...\n", uid);
1678 ok = imap_cmd_fetch(session, (guint32)uid, filename, headers, body);
1680 if (ok != MAILIMAP_NO_ERROR) {
1681 g_warning("can't fetch message %d", uid);
1682 g_free(filename);
1683 return NULL;
1686 session_set_access_time(SESSION(session));
1687 unlock_session(session);
1689 ok = file_strip_crs(filename);
1691 if (ok == 0 && headers && body) {
1692 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1693 if (cached) {
1694 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1695 procmsg_msginfo_free(&cached);
1697 } else if (ok == -1) {
1698 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1699 if (cached) {
1700 procmsg_msginfo_unset_flags(cached, MSG_FULLY_CACHED, 0);
1701 procmsg_msginfo_free(&cached);
1704 return filename;
1707 static gboolean imap_is_msg_fully_cached(Folder *folder, FolderItem *item, gint uid)
1709 gchar *filename;
1710 guint size = 0;
1711 MsgInfo *cached = msgcache_get_msg(item->cache,uid);
1713 if (!cached)
1714 return FALSE;
1716 if (MSG_IS_FULLY_CACHED(cached->flags)) {
1717 procmsg_msginfo_free(&cached);
1718 return TRUE;
1721 filename = imap_get_cached_filename(item, uid);
1723 if (is_file_exist(filename)) {
1724 if (cached && cached->total_size == cached->size) {
1725 /* fast path */
1726 g_free(filename);
1727 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1728 return TRUE;
1730 size = get_file_size_with_crs(filename);
1732 g_free(filename);
1733 debug_print("msg %d cached, has size %d, full should be %"G_GOFFSET_FORMAT".\n", uid, size, cached->size);
1734 if (cached && size >= cached->size) {
1735 cached->total_size = cached->size;
1736 procmsg_msginfo_set_flags(cached, MSG_FULLY_CACHED, 0);
1737 procmsg_msginfo_free(&cached);
1738 return TRUE;
1740 if (cached)
1741 procmsg_msginfo_free(&cached);
1742 return FALSE;
1745 void imap_cache_msg(FolderItem *item, gint msgnum)
1747 Folder *folder = NULL;
1749 if (!item)
1750 return;
1751 folder = item->folder;
1753 if (!imap_is_msg_fully_cached(folder, item, msgnum)) {
1754 gchar *tmp = imap_fetch_msg_full(folder, item, msgnum, TRUE, TRUE);
1755 debug_print("fetched %s\n", tmp);
1756 g_free(tmp);
1760 static gint imap_add_msg(Folder *folder, FolderItem *dest,
1761 const gchar *file, MsgFlags *flags)
1763 gint ret;
1764 GSList file_list;
1765 MsgFileInfo fileinfo;
1767 g_return_val_if_fail(file != NULL, -1);
1769 fileinfo.msginfo = NULL;
1770 fileinfo.file = (gchar *)file;
1771 fileinfo.flags = flags;
1772 file_list.data = &fileinfo;
1773 file_list.next = NULL;
1775 ret = imap_add_msgs(folder, dest, &file_list, NULL);
1776 return ret;
1779 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1780 GHashTable *relation)
1782 gchar *destdir;
1783 IMAPSession *session;
1784 guint32 last_uid = 0;
1785 GSList *cur;
1786 MsgFileInfo *fileinfo;
1787 gint ok = MAILIMAP_NO_ERROR;
1788 gint curnum = 0, total = 0;
1789 gboolean missing_uids = FALSE;
1791 g_return_val_if_fail(folder != NULL, -1);
1792 g_return_val_if_fail(dest != NULL, -1);
1793 g_return_val_if_fail(file_list != NULL, -1);
1795 debug_print("getting session...\n");
1796 session = imap_session_get(folder);
1797 if (!session) {
1798 return -1;
1800 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
1801 if (is_fatal(ok)) {
1802 g_free(destdir);
1803 return -1;
1805 statusbar_print_all(_("Adding messages..."));
1806 total = g_slist_length(file_list);
1807 for (cur = file_list; cur != NULL; cur = cur->next) {
1808 IMAPFlags iflags = 0;
1809 guint32 new_uid = 0;
1810 gchar *real_file = NULL;
1811 fileinfo = (MsgFileInfo *)cur->data;
1813 statusbar_progress_all(curnum, total, total < 10 ? 1:10);
1814 curnum++;
1816 if (fileinfo->flags) {
1817 if (MSG_IS_MARKED(*fileinfo->flags))
1818 iflags |= IMAP_FLAG_FLAGGED;
1819 if (MSG_IS_REPLIED(*fileinfo->flags))
1820 iflags |= IMAP_FLAG_ANSWERED;
1821 if (MSG_IS_FORWARDED(*fileinfo->flags))
1822 iflags |= IMAP_FLAG_FORWARDED;
1823 if (MSG_IS_SPAM(*fileinfo->flags))
1824 iflags |= IMAP_FLAG_SPAM;
1825 else
1826 iflags |= IMAP_FLAG_HAM;
1827 if (!MSG_IS_UNREAD(*fileinfo->flags))
1828 iflags |= IMAP_FLAG_SEEN;
1832 if (real_file == NULL)
1833 real_file = g_strdup(fileinfo->file);
1835 if (folder_has_parent_of_type(dest, F_QUEUE) ||
1836 folder_has_parent_of_type(dest, F_OUTBOX) ||
1837 folder_has_parent_of_type(dest, F_DRAFT) ||
1838 folder_has_parent_of_type(dest, F_TRASH))
1839 iflags |= IMAP_FLAG_SEEN;
1841 ok = imap_cmd_append(session, IMAP_FOLDER_ITEM(dest), destdir, real_file, iflags,
1842 &new_uid);
1844 if (ok != MAILIMAP_NO_ERROR) {
1845 g_warning("can't append message %s", real_file);
1846 g_free(real_file);
1847 g_free(destdir);
1848 statusbar_progress_all(0,0,0);
1849 statusbar_pop_all();
1850 return -1;
1851 } else {
1852 debug_print("appended new message as %d\n", new_uid);
1853 /* put the local file in the imapcache, so that we don't
1854 * have to fetch it back later. */
1856 if (new_uid == 0) {
1857 missing_uids = TRUE;
1858 debug_print("Missing UID (0)\n");
1860 if (new_uid > 0) {
1861 gchar *cache_path = folder_item_get_path(dest);
1862 if (!is_dir_exist(cache_path))
1863 make_dir_hier(cache_path);
1864 if (is_dir_exist(cache_path)) {
1865 gchar *cache_file = g_strconcat(
1866 cache_path, G_DIR_SEPARATOR_S,
1867 itos(new_uid), NULL);
1868 copy_file(real_file, cache_file, TRUE);
1869 debug_print("got UID %d, copied to cache: %s\n", new_uid, cache_file);
1870 g_free(cache_file);
1872 g_free(cache_path);
1876 if (relation != NULL)
1877 g_hash_table_insert(relation, fileinfo->msginfo != NULL ?
1878 (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
1879 GINT_TO_POINTER(new_uid));
1880 if (last_uid < new_uid) {
1881 last_uid = new_uid;
1884 g_free(real_file);
1887 statusbar_progress_all(0,0,0);
1888 statusbar_pop_all();
1891 g_free(destdir);
1893 imap_scan_required(folder, dest);
1895 session = imap_session_get(folder);
1896 if (!session) {
1897 return -1;
1899 if (missing_uids) {
1900 gint a;
1901 ok = imap_select(session, IMAP_FOLDER(folder), dest,
1902 &a, NULL, NULL, NULL, NULL, FALSE);
1904 return last_uid;
1907 static GSList *flatten_mailimap_set(struct mailimap_set * set)
1909 GSList *result = NULL;
1910 clistiter *list;
1911 guint32 start, end, t;
1912 GSList *cur;
1914 if (!set || !set->set_list)
1915 return NULL;
1917 for (list = clist_begin(set->set_list); list; list = clist_next(list)) {
1918 struct mailimap_set_item *item = (struct mailimap_set_item *)clist_content(list);
1919 start = item->set_first;
1920 end = item->set_last;
1921 if (start <= end) {
1922 for (t = start; t <= end; t++) {
1923 result = g_slist_prepend(result, GINT_TO_POINTER(t));
1927 result = g_slist_reverse(result);
1928 if (debug_get_mode()) {
1929 debug_print("flat imap set: ");
1930 for (cur = result; cur; cur = cur->next) {
1931 debug_print("%d ", GPOINTER_TO_INT(cur->data));
1933 debug_print("\n");
1936 return result;
1938 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest,
1939 MsgInfoList *msglist, GHashTable *relation,
1940 gboolean same_dest_ok)
1942 FolderItem *src;
1943 gchar *destdir;
1944 GSList *seq_list, *cur;
1945 MsgInfo *msginfo;
1946 IMAPSession *session;
1947 gint ok = MAILIMAP_NO_ERROR;
1948 GHashTable *uid_hash;
1949 gint last_num = 0;
1951 g_return_val_if_fail(folder != NULL, -1);
1952 g_return_val_if_fail(dest != NULL, -1);
1953 g_return_val_if_fail(msglist != NULL, -1);
1955 debug_print("getting session...\n");
1956 session = imap_session_get(folder);
1958 if (!session) {
1959 return -1;
1962 msginfo = (MsgInfo *)msglist->data;
1963 src = msginfo->folder;
1964 if (!same_dest_ok && src == dest) {
1965 g_warning("the src folder is identical to the dest");
1966 return -1;
1969 if (src->folder != dest->folder) {
1970 GSList *infolist = NULL, *cur;
1971 int res = -1;
1972 for (cur = msglist; cur; cur = cur->next) {
1973 msginfo = (MsgInfo *)cur->data;
1974 MsgFileInfo *fileinfo = g_new0(MsgFileInfo, 1);
1975 fileinfo->file = procmsg_get_message_file(msginfo);
1976 fileinfo->flags = &(msginfo->flags);
1977 infolist = g_slist_prepend(infolist, fileinfo);
1979 infolist = g_slist_reverse(infolist);
1980 res = folder_item_add_msgs(dest, infolist, FALSE);
1981 for (cur = infolist; cur; cur = cur->next) {
1982 MsgFileInfo *info = (MsgFileInfo *)cur->data;
1983 g_free(info->file);
1984 g_free(info);
1986 g_slist_free(infolist);
1987 return res;
1990 lock_session(session); /* unlocked later in the function */
1992 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
1993 NULL, NULL, NULL, NULL, NULL, FALSE);
1994 if (ok != MAILIMAP_NO_ERROR) {
1995 return ok;
1998 unlock_session(session);
2000 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
2002 if (is_fatal(ok)) {
2003 g_free(destdir);
2004 return ok;
2007 seq_list = imap_get_lep_set_from_msglist(IMAP_FOLDER(folder), msglist);
2008 uid_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
2010 statusbar_print_all(_("Copying messages..."));
2011 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
2012 struct mailimap_set * seq_set;
2013 struct mailimap_set * source = NULL;
2014 struct mailimap_set * dest = NULL;
2015 seq_set = cur->data;
2017 debug_print("Copying messages from %s to %s ...\n",
2018 src->path, destdir);
2020 lock_session(session); /* unlocked later in the function */
2021 ok = imap_cmd_copy(session, seq_set, destdir,
2022 &source, &dest);
2024 if (is_fatal(ok)) {
2025 session = NULL;
2028 if (ok == MAILIMAP_NO_ERROR) {
2029 unlock_session(session);
2030 if (relation && source && dest) {
2031 GSList *s_list = flatten_mailimap_set(source);
2032 GSList *d_list = flatten_mailimap_set(dest);
2033 GSList *s_cur, *d_cur;
2034 if (g_slist_length(s_list) == g_slist_length(d_list)) {
2036 for (s_cur = s_list, d_cur = d_list;
2037 s_cur && d_cur;
2038 s_cur = s_cur->next, d_cur = d_cur->next) {
2039 g_hash_table_insert(uid_hash, s_cur->data, d_cur->data);
2042 } else {
2043 debug_print("hhhmm, source list length != dest list length.\n");
2045 g_slist_free(s_list);
2046 g_slist_free(d_list);
2051 if (source)
2052 mailimap_set_free(source);
2053 if (dest)
2054 mailimap_set_free(dest);
2056 if (ok != MAILIMAP_NO_ERROR) {
2057 g_hash_table_destroy(uid_hash);
2058 imap_lep_set_free(seq_list);
2059 statusbar_pop_all();
2060 return -1;
2064 for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
2065 MsgInfo *msginfo = (MsgInfo *)cur->data;
2066 gpointer hashval;
2068 hashval = g_hash_table_lookup(uid_hash, GINT_TO_POINTER(msginfo->msgnum));
2070 if (hashval != NULL) {
2071 gint num = GPOINTER_TO_INT(hashval);
2072 g_hash_table_insert(relation, msginfo,
2073 GINT_TO_POINTER(num));
2074 if (num > last_num)
2075 last_num = num;
2076 debug_print("copied message %d as %d\n", msginfo->msgnum, num);
2077 /* put the local file in the imapcache, so that we don't
2078 * have to fetch it back later. */
2079 if (num > 0) {
2080 gchar *cache_path = folder_item_get_path(msginfo->folder);
2081 gchar *real_file = g_strconcat(
2082 cache_path, G_DIR_SEPARATOR_S,
2083 itos(msginfo->msgnum), NULL);
2084 gchar *cache_file = NULL;
2085 g_free(cache_path);
2086 cache_path = folder_item_get_path(dest);
2087 cache_file = g_strconcat(
2088 cache_path, G_DIR_SEPARATOR_S,
2089 itos(num), NULL);
2090 if (!is_dir_exist(cache_path))
2091 make_dir_hier(cache_path);
2092 if (is_file_exist(real_file) && is_dir_exist(cache_path)) {
2093 if (copy_file(real_file, cache_file, TRUE) < 0)
2094 debug_print("couldn't cache to %s: %s\n", cache_file,
2095 strerror(errno));
2096 else
2097 debug_print("copied to cache: %s\n", cache_file);
2099 g_free(real_file);
2100 g_free(cache_file);
2101 g_free(cache_path);
2103 } else
2104 g_hash_table_insert(relation, msginfo,
2105 GINT_TO_POINTER(0));
2107 statusbar_pop_all();
2109 g_hash_table_destroy(uid_hash);
2110 imap_lep_set_free(seq_list);
2112 g_free(destdir);
2114 IMAP_FOLDER_ITEM(dest)->lastuid = 0;
2115 IMAP_FOLDER_ITEM(dest)->uid_next = 0;
2116 g_slist_free(IMAP_FOLDER_ITEM(dest)->uid_list);
2117 IMAP_FOLDER_ITEM(dest)->uid_list = NULL;
2119 imap_scan_required(folder, dest);
2120 if (ok == MAILIMAP_NO_ERROR)
2121 return last_num;
2122 else
2123 return -1;
2126 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
2128 GSList msglist;
2130 g_return_val_if_fail(msginfo != NULL, -1);
2132 msglist.data = msginfo;
2133 msglist.next = NULL;
2135 return imap_copy_msgs(folder, dest, &msglist, NULL);
2138 static gint imap_copy_msgs(Folder *folder, FolderItem *dest,
2139 MsgInfoList *msglist, GHashTable *relation)
2141 MsgInfo *msginfo;
2142 gint ret;
2144 g_return_val_if_fail(folder != NULL, -1);
2145 g_return_val_if_fail(dest != NULL, -1);
2146 g_return_val_if_fail(msglist != NULL, -1);
2148 msginfo = (MsgInfo *)msglist->data;
2149 g_return_val_if_fail(msginfo->folder != NULL, -1);
2151 ret = imap_do_copy_msgs(folder, dest, msglist, relation, FALSE);
2152 return ret;
2155 static gboolean imap_matcher_type_is_local(gint matchertype)
2157 switch (matchertype) {
2158 case MATCHCRITERIA_FROM:
2159 case MATCHCRITERIA_TO:
2160 case MATCHCRITERIA_CC:
2161 case MATCHCRITERIA_TO_OR_CC:
2162 case MATCHCRITERIA_SUBJECT:
2163 case MATCHCRITERIA_REFERENCES:
2164 case MATCHCRITERIA_MESSAGEID:
2165 case MATCHCRITERIA_INREPLYTO:
2166 case MATCHCRITERIA_AGE_GREATER:
2167 case MATCHCRITERIA_AGE_LOWER:
2168 case MATCHCRITERIA_FORWARDED:
2169 case MATCHCRITERIA_SPAM:
2170 case MATCHCRITERIA_UNREAD:
2171 case MATCHCRITERIA_NEW:
2172 case MATCHCRITERIA_MARKED:
2173 case MATCHCRITERIA_REPLIED:
2174 case MATCHCRITERIA_DELETED:
2175 case MATCHCRITERIA_SIZE_GREATER:
2176 case MATCHCRITERIA_SIZE_SMALLER:
2177 case MATCHCRITERIA_SIZE_EQUAL:
2178 return TRUE;
2180 return FALSE;
2183 static IMAPSearchKey* search_make_key(MatcherProp* match, gboolean* is_all)
2185 if (match->matchtype == MATCHTYPE_MATCHCASE || match->matchtype == MATCHTYPE_MATCH) {
2186 IMAPSearchKey* result = NULL;
2187 gboolean invert = FALSE;
2188 gint matchertype = match->criteria;
2190 if (is_all) {
2191 *is_all = FALSE;
2194 switch (matchertype) {
2195 case MATCHCRITERIA_NOT_NEW: invert = TRUE; matchertype = MATCHCRITERIA_NEW; break;
2196 case MATCHCRITERIA_NOT_MARKED: invert = TRUE; matchertype = MATCHCRITERIA_MARKED; break;
2197 case MATCHCRITERIA_NOT_FORWARDED: invert = TRUE; matchertype = MATCHCRITERIA_FORWARDED; break;
2198 case MATCHCRITERIA_NOT_SPAM: invert = TRUE; matchertype = MATCHCRITERIA_SPAM; break;
2199 case MATCHCRITERIA_NOT_SUBJECT: invert = TRUE; matchertype = MATCHCRITERIA_SUBJECT; break;
2200 case MATCHCRITERIA_NOT_FROM: invert = TRUE; matchertype = MATCHCRITERIA_FROM; break;
2201 case MATCHCRITERIA_NOT_TO: invert = TRUE; matchertype = MATCHCRITERIA_TO; break;
2202 case MATCHCRITERIA_NOT_CC: invert = TRUE; matchertype = MATCHCRITERIA_CC; break;
2203 case MATCHCRITERIA_NOT_REFERENCES: invert = TRUE; matchertype = MATCHCRITERIA_REFERENCES; break;
2204 case MATCHCRITERIA_NOT_HEADER: invert = TRUE; matchertype = MATCHCRITERIA_HEADER; break;
2205 case MATCHCRITERIA_NOT_TAG: invert = TRUE; matchertype = MATCHCRITERIA_TAG; break;
2206 case MATCHCRITERIA_NOT_HEADERS_PART: invert = TRUE; matchertype = MATCHCRITERIA_HEADERS_PART; break;
2207 case MATCHCRITERIA_NOT_HEADERS_CONT: invert = TRUE; matchertype = MATCHCRITERIA_HEADERS_CONT; break;
2208 case MATCHCRITERIA_NOT_MESSAGE: invert = TRUE; matchertype = MATCHCRITERIA_MESSAGE; break;
2209 case MATCHCRITERIA_NOT_BODY_PART: invert = TRUE; matchertype = MATCHCRITERIA_BODY_PART; break;
2210 case MATCHCRITERIA_NOT_TO_AND_NOT_CC: invert = TRUE; matchertype = MATCHCRITERIA_TO_OR_CC; break;
2211 case MATCHCRITERIA_NOT_MESSAGEID: invert = TRUE; matchertype = MATCHCRITERIA_MESSAGEID; break;
2212 case MATCHCRITERIA_NOT_INREPLYTO: invert = TRUE; matchertype = MATCHCRITERIA_INREPLYTO; break;
2216 * this aborts conversion even for predicates understood by the following code.
2217 * while that might seem wasteful, claws local search for information listed below
2218 * has proven faster than IMAP search plus network roundtrips. once this changes,
2219 * consider removing these exceptions.
2221 if (imap_matcher_type_is_local(matchertype))
2222 return NULL;
2224 /* the Message-ID header is also cached */
2225 if (matchertype == MATCHCRITERIA_HEADER && g_strcmp0("Message-ID", match->header) == 0) {
2226 return NULL;
2229 switch (matchertype) {
2230 case MATCHCRITERIA_FORWARDED:
2231 result = imap_search_new(IMAP_SEARCH_CRITERIA_TAG, NULL, RTAG_FORWARDED, 0);
2232 break;
2234 case MATCHCRITERIA_SPAM:
2235 result = imap_search_new(IMAP_SEARCH_CRITERIA_TAG, NULL, RTAG_JUNK, 0);
2236 break;
2238 case MATCHCRITERIA_MESSAGEID:
2239 result = imap_search_new(IMAP_SEARCH_CRITERIA_HEADER, "Message-ID", match->expr, 0);
2240 break;
2242 case MATCHCRITERIA_INREPLYTO:
2243 result = imap_search_new(IMAP_SEARCH_CRITERIA_HEADER, "In-Reply-To", match->expr, 0);
2244 break;
2246 case MATCHCRITERIA_REFERENCES:
2247 result = imap_search_new(IMAP_SEARCH_CRITERIA_HEADER, "References", match->expr, 0);
2248 break;
2250 case MATCHCRITERIA_TO_OR_CC:
2251 result = imap_search_or(
2252 imap_search_new(IMAP_SEARCH_CRITERIA_TO, NULL, match->expr, 0),
2253 imap_search_new(IMAP_SEARCH_CRITERIA_CC, NULL, match->expr, 0)
2255 break;
2257 case MATCHCRITERIA_HEADERS_PART:
2258 case MATCHCRITERIA_HEADERS_CONT:
2259 result = imap_search_and(
2260 imap_search_not(imap_search_new(IMAP_SEARCH_CRITERIA_BODY, NULL, match->expr, 0)),
2261 imap_search_new(IMAP_SEARCH_CRITERIA_MESSAGE, NULL, match->expr, 0)
2263 break;
2265 case MATCHCRITERIA_SIZE_EQUAL:
2266 result = imap_search_and(
2267 imap_search_not(imap_search_new(IMAP_SEARCH_CRITERIA_SIZE_SMALLER, NULL, NULL, match->value)),
2268 imap_search_not(imap_search_new(IMAP_SEARCH_CRITERIA_SIZE_GREATER, NULL, NULL, match->value))
2270 break;
2272 case MATCHCRITERIA_NOT_UNREAD:
2273 result = imap_search_new(IMAP_SEARCH_CRITERIA_READ, NULL, NULL, 0);
2274 break;
2276 case MATCHCRITERIA_UNREAD:
2277 result = imap_search_new(IMAP_SEARCH_CRITERIA_UNREAD, NULL, NULL, 0);
2278 break;
2280 case MATCHCRITERIA_NEW:
2281 result = imap_search_new(IMAP_SEARCH_CRITERIA_NEW, NULL, NULL, 0);
2282 break;
2284 case MATCHCRITERIA_MARKED:
2285 result = imap_search_new(IMAP_SEARCH_CRITERIA_MARKED, NULL, NULL, 0);
2286 break;
2288 case MATCHCRITERIA_DELETED:
2289 result = imap_search_new(IMAP_SEARCH_CRITERIA_DELETED, NULL, NULL, 0);
2290 break;
2292 case MATCHCRITERIA_REPLIED:
2293 result = imap_search_new(IMAP_SEARCH_CRITERIA_REPLIED, NULL, NULL, 0);
2294 break;
2296 case MATCHCRITERIA_TAG:
2298 gchar *tmp = imap_utf8_to_modified_utf7(match->expr, TRUE);
2299 result = imap_search_new(IMAP_SEARCH_CRITERIA_TAG, NULL, tmp, 0);
2300 g_free(tmp);
2302 break;
2304 case MATCHCRITERIA_SUBJECT:
2305 result = imap_search_new(IMAP_SEARCH_CRITERIA_SUBJECT, NULL, match->expr, 0);
2306 break;
2308 case MATCHCRITERIA_FROM:
2309 result = imap_search_new(IMAP_SEARCH_CRITERIA_FROM, NULL, match->expr, 0);
2310 break;
2312 case MATCHCRITERIA_TO:
2313 result = imap_search_new(IMAP_SEARCH_CRITERIA_TO, NULL, match->expr, 0);
2314 break;
2316 case MATCHCRITERIA_CC:
2317 result = imap_search_new(IMAP_SEARCH_CRITERIA_CC, NULL, match->expr, 0);
2318 break;
2320 case MATCHCRITERIA_AGE_GREATER:
2321 result = imap_search_new(IMAP_SEARCH_CRITERIA_AGE_GREATER, NULL, NULL, match->value);
2322 break;
2324 case MATCHCRITERIA_AGE_LOWER:
2325 result = imap_search_new(IMAP_SEARCH_CRITERIA_AGE_LOWER, NULL, NULL, match->value);
2326 break;
2328 case MATCHCRITERIA_BODY_PART:
2329 result = imap_search_new(IMAP_SEARCH_CRITERIA_BODY, NULL, match->expr, 0);
2330 break;
2332 case MATCHCRITERIA_MESSAGE:
2333 result = imap_search_new(IMAP_SEARCH_CRITERIA_MESSAGE, NULL, match->expr, 0);
2334 break;
2336 case MATCHCRITERIA_HEADER:
2337 result = imap_search_new(IMAP_SEARCH_CRITERIA_HEADER, match->header, match->expr, 0);
2338 break;
2340 case MATCHCRITERIA_SIZE_GREATER:
2341 result = imap_search_new(IMAP_SEARCH_CRITERIA_SIZE_GREATER, NULL, NULL, match->value);
2342 break;
2344 case MATCHCRITERIA_SIZE_SMALLER:
2345 result = imap_search_new(IMAP_SEARCH_CRITERIA_SIZE_SMALLER, NULL, NULL, match->value);
2346 break;
2348 default:
2349 result = imap_search_new(IMAP_SEARCH_CRITERIA_ALL, NULL, NULL, 0);
2350 if (is_all) {
2351 *is_all = TRUE;
2353 break;
2356 if (invert) {
2357 result = imap_search_not(result);
2358 if (is_all && *is_all) {
2359 *is_all = FALSE;
2363 return result;
2366 return NULL;
2369 static void imap_change_search_charset(IMAPFolder *folder)
2371 /* If server supports charset in searches, but the last used one failed,
2372 * changed to the next preferred charset. If none are still available,
2373 * disable charset searches.
2374 * Charsets are tried in the following order:
2375 * UTF-8, locale's charset, UTF-7.
2378 if (folder->search_charset_supported) {
2379 if (folder->search_charset && !strcmp(folder->search_charset, conv_get_locale_charset_str_no_utf8()))
2380 folder->search_charset = "UTF-8";
2381 else if (folder->search_charset && !strcmp(folder->search_charset, "UTF-8"))
2382 folder->search_charset = "UTF-7";
2383 else {
2384 folder->search_charset = NULL;
2385 folder->search_charset_supported = FALSE;
2390 static MatcherProp *imap_matcher_prop_set_charset(IMAPFolder *folder,
2391 MatcherProp *utf8_prop,
2392 gchar **charset)
2394 /* If the match is going to be done locally, or the criteria is on
2395 * tag (special-cased to modified-UTF-7), or the expression searched
2396 * is ASCII, don't bother converting.
2398 if (imap_matcher_type_is_local(utf8_prop->criteria)
2399 || utf8_prop->criteria == MATCHCRITERIA_TAG
2400 || utf8_prop->criteria == MATCHCRITERIA_NOT_TAG
2401 || utf8_prop->expr == NULL
2402 || is_ascii_str(utf8_prop->expr))
2403 return matcherprop_new(utf8_prop->criteria,
2404 utf8_prop->header,
2405 utf8_prop->matchtype,
2406 utf8_prop->expr,
2407 utf8_prop->value);
2408 else {
2409 gchar *conv_expr = NULL;
2411 /* If the search is server-side and the server doesn't support
2412 * searching with the charsets we handle, bail out.
2414 if (folder->search_charset_supported == FALSE)
2415 return NULL;
2417 /* Else, convert. */
2418 if (*charset == NULL)
2419 *charset = g_strdup(folder->search_charset);
2421 conv_expr = conv_codeset_strdup(utf8_prop->expr, CS_UTF_8, *charset);
2423 if (conv_expr == NULL)
2424 conv_expr = g_strdup(utf8_prop->expr);
2426 return matcherprop_new(utf8_prop->criteria,
2427 utf8_prop->header,
2428 utf8_prop->matchtype,
2429 conv_expr,
2430 utf8_prop->value);
2434 static gint search_msgs (Folder *folder,
2435 FolderItem *container,
2436 MsgNumberList **msgs,
2437 gboolean *on_server,
2438 MatcherList *predicate,
2439 SearchProgressNotify progress_cb,
2440 gpointer progress_data)
2442 IMAPSearchKey* key = NULL;
2443 GSList* cur;
2444 int result = -1;
2445 clist* uidlist = NULL;
2446 gboolean server_filtering_useless = FALSE;
2447 IMAPSession *session;
2448 gchar *charset_to_use = NULL;
2450 if (on_server == NULL || !*on_server) {
2451 return folder_item_search_msgs_local(folder, container, msgs, on_server,
2452 predicate, progress_cb, progress_data);
2455 for (cur = predicate->matchers; cur != NULL; cur = cur->next) {
2456 IMAPSearchKey* matcherPart = NULL;
2457 MatcherProp* prop = (MatcherProp*) cur->data;
2458 gboolean is_all;
2459 MatcherProp *imap_prop = imap_matcher_prop_set_charset(IMAP_FOLDER(folder), prop, &charset_to_use);
2461 if (imap_prop == NULL) {
2462 /* Couldn't convert matcherprop to IMAP - probably not ascii
2463 * and server doesn't support the charsets we do. */
2464 return -1;
2467 matcherPart = search_make_key(imap_prop, &is_all);
2469 matcherprop_free(imap_prop);
2471 if (on_server) {
2472 *on_server &= matcherPart != NULL && prop->matchtype == MATCHTYPE_MATCHCASE;
2475 if (matcherPart) {
2476 if (key == NULL) {
2477 key = matcherPart;
2478 server_filtering_useless = is_all;
2479 } else if (predicate->bool_and) {
2480 key = imap_search_and(key, matcherPart);
2481 server_filtering_useless &= is_all;
2482 } else {
2483 key = imap_search_or(key, matcherPart);
2484 server_filtering_useless |= is_all;
2489 if (server_filtering_useless) {
2490 imap_search_free(key);
2491 key = NULL;
2494 if (key == NULL && progress_cb != NULL) {
2495 GSList* cur;
2496 GSList* list;
2497 int count = 0;
2499 progress_cb(progress_data, TRUE, 0, 0, container->total_msgs);
2500 progress_cb(progress_data, TRUE, container->total_msgs, 0, container->total_msgs);
2502 list = folder_item_get_msg_list(container);
2503 for (cur = list; cur != NULL; cur = cur->next) {
2504 *msgs = g_slist_prepend(*msgs, GUINT_TO_POINTER(((MsgInfo*) cur->data)->msgnum));
2505 count++;
2507 procmsg_msg_list_free(list);
2509 *msgs = g_slist_reverse(*msgs);
2511 return count;
2514 session = imap_session_get(folder);
2515 if (!session) {
2516 return -1;
2518 result = imap_select(session, IMAP_FOLDER(folder), FOLDER_ITEM(container),
2519 NULL, NULL, NULL, NULL, NULL, TRUE);
2520 if (result != MAILIMAP_NO_ERROR)
2521 return -1;
2523 if (progress_cb)
2524 progress_cb(progress_data, TRUE, 0, 0, container->total_msgs);
2525 result = imap_threaded_search(folder, IMAP_SEARCH_TYPE_KEYED, key, charset_to_use, NULL, &uidlist);
2526 if (progress_cb)
2527 progress_cb(progress_data, TRUE, container->total_msgs, 0, container->total_msgs);
2529 if (result == MAILIMAP_ERROR_PROTOCOL) {
2530 debug_print("Server side search unavailable, using local search\n");
2531 imap_handle_error(SESSION(session), NULL, result);
2532 result = folder_item_search_msgs_local(folder, container, msgs, NULL,
2533 predicate, progress_cb, progress_data);
2534 if (result < 0) {
2535 debug_print("search_msgs - got protocol error, aborting\n");
2536 alertpanel_error_log(_("Search failed due to server error."));
2537 return -1;
2540 return result;
2543 if (result == MAILIMAP_NO_ERROR) {
2544 gint result = 0;
2546 *msgs = imap_uid_list_from_lep(uidlist, &result);
2548 mailimap_search_result_free(uidlist);
2550 if (charset_to_use != NULL)
2551 g_free(charset_to_use);
2553 return result;
2554 } else if (charset_to_use != NULL) {
2555 /* If search failed and was on an 8-bit string, try the next
2556 * available charset to search if there still are some.
2558 g_free(charset_to_use);
2560 imap_change_search_charset(IMAP_FOLDER(folder));
2561 if (IMAP_FOLDER(folder)->search_charset_supported)
2562 return search_msgs(folder, container, msgs, on_server, predicate,
2563 progress_cb, progress_data);
2564 else {
2565 imap_handle_error(SESSION(session), NULL, result);
2566 return -1;
2568 } else {
2569 imap_handle_error(SESSION(session), NULL, result);
2570 return -1;
2575 static gint imap_do_remove_msgs(Folder *folder, FolderItem *dest,
2576 MsgInfoList *msglist, GHashTable *relation)
2578 gchar *destdir, *dir;
2579 GSList *numlist = NULL, *cur;
2580 MsgInfo *msginfo;
2581 IMAPSession *session;
2582 gint ok = MAILIMAP_NO_ERROR;
2584 g_return_val_if_fail(folder != NULL, -1);
2585 g_return_val_if_fail(dest != NULL, -1);
2586 g_return_val_if_fail(msglist != NULL, -1);
2588 debug_print("getting session...\n");
2589 session = imap_session_get(folder);
2590 if (!session) {
2591 return -1;
2594 lock_session(session); /* unlocked later in the function */
2596 msginfo = (MsgInfo *)msglist->data;
2598 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
2599 NULL, NULL, NULL, NULL, NULL, FALSE);
2600 if (ok != MAILIMAP_NO_ERROR) {
2601 return ok;
2604 destdir = imap_get_real_path(session, IMAP_FOLDER(folder), dest->path, &ok);
2605 if (is_fatal(ok)) {
2606 g_free(destdir);
2607 return ok;
2609 for (cur = msglist; cur; cur = cur->next) {
2610 msginfo = (MsgInfo *)cur->data;
2611 if (!MSG_IS_DELETED(msginfo->flags))
2612 numlist = g_slist_prepend(numlist, GINT_TO_POINTER(msginfo->msgnum));
2614 numlist = g_slist_reverse(numlist);
2616 if (numlist != NULL) {
2617 ok = imap_set_message_flags
2618 (session, IMAP_FOLDER_ITEM(msginfo->folder), numlist, IMAP_FLAG_DELETED, NULL, TRUE);
2619 if (ok != MAILIMAP_NO_ERROR) {
2620 log_warning(LOG_PROTOCOL, _("can't set deleted flags\n"));
2621 g_free(destdir);
2622 return ok;
2624 } /* else we just need to expunge */
2625 ok = imap_cmd_expunge(session);
2626 if (ok != MAILIMAP_NO_ERROR) {
2627 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
2628 g_free(destdir);
2629 return ok;
2632 session->folder_content_changed = TRUE;
2633 unlock_session(session);
2635 dir = folder_item_get_path(msginfo->folder);
2636 if (is_dir_exist(dir)) {
2637 for (cur = msglist; cur; cur = cur->next) {
2638 msginfo = (MsgInfo *)cur->data;
2639 remove_numbered_files(dir, msginfo->msgnum, msginfo->msgnum);
2642 g_free(dir);
2644 g_slist_free(numlist);
2646 imap_scan_required(folder, dest);
2648 g_free(destdir);
2650 return 0;
2653 static gint imap_remove_msgs(Folder *folder, FolderItem *dest,
2654 MsgInfoList *msglist, GHashTable *relation)
2656 MsgInfo *msginfo;
2658 g_return_val_if_fail(folder != NULL, -1);
2659 g_return_val_if_fail(dest != NULL, -1);
2660 if (msglist == NULL)
2661 return 0;
2663 msginfo = (MsgInfo *)msglist->data;
2664 g_return_val_if_fail(msginfo->folder != NULL, -1);
2666 return imap_do_remove_msgs(folder, dest, msglist, relation);
2669 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
2671 GSList *list = folder_item_get_msg_list(item);
2672 gint res = imap_remove_msgs(folder, item, list, NULL);
2673 procmsg_msg_list_free(list);
2674 return res;
2677 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
2678 MsgInfo *msginfo)
2680 /* TODO: properly implement this method */
2681 return FALSE;
2684 static gint imap_close(Folder *folder, FolderItem *item)
2686 return 0;
2689 static gint imap_scan_tree_real(Folder *folder, gboolean subs_only)
2691 FolderItem *item = NULL;
2692 IMAPSession *session;
2693 gchar *root_folder = NULL;
2695 g_return_val_if_fail(folder != NULL, -1);
2696 g_return_val_if_fail(folder->account != NULL, -1);
2698 debug_print("getting session...\n");
2699 session = imap_session_get(folder);
2700 if (!session) {
2701 if (!folder->node) {
2702 folder_tree_destroy(folder);
2703 item = folder_item_new(folder, folder->name, NULL);
2704 item->folder = folder;
2705 folder->node = item->node = g_node_new(item);
2707 return -1;
2710 if (folder->account->imap_dir && *folder->account->imap_dir) {
2711 gchar *real_path;
2712 int r = MAILIMAP_NO_ERROR;
2713 clist * lep_list;
2715 Xstrdup_a(root_folder, folder->account->imap_dir, {return -1;});
2716 extract_quote(root_folder, '"');
2717 subst_char(root_folder,
2718 imap_get_path_separator(session, IMAP_FOLDER(folder),
2719 root_folder, &r),
2720 '/');
2721 if (is_fatal(r))
2722 return -1;
2723 strtailchomp(root_folder, '/');
2724 real_path = imap_get_real_path
2725 (session, IMAP_FOLDER(folder), root_folder, &r);
2726 if (is_fatal(r)) {
2727 g_free(real_path);
2728 return -1;
2730 debug_print("IMAP root directory: %s\n", real_path);
2732 /* check if root directory exist */
2734 r = imap_threaded_list(session->folder, "", real_path,
2735 &lep_list);
2737 if (r != MAILIMAP_NO_ERROR)
2738 imap_handle_error(SESSION(session), NULL, r);
2740 if ((r != MAILIMAP_NO_ERROR) || (clist_count(lep_list) == 0)) {
2741 if (!folder->node) {
2742 item = folder_item_new(folder, folder->name, NULL);
2743 item->folder = folder;
2744 folder->node = item->node = g_node_new(item);
2746 return -1;
2748 mailimap_list_result_free(lep_list);
2750 g_free(real_path);
2753 if (folder->node)
2754 item = FOLDER_ITEM(folder->node->data);
2756 if (item && !item->path && root_folder) {
2757 item->path = g_strdup(root_folder);
2760 if (!item || ((item->path || root_folder) &&
2761 g_strcmp0(item->path, root_folder) != 0)) {
2762 folder_tree_destroy(folder);
2763 item = folder_item_new(folder, folder->name, root_folder);
2764 item->folder = folder;
2765 folder->node = item->node = g_node_new(item);
2768 imap_scan_tree_recursive(session, FOLDER_ITEM(folder->node->data), subs_only);
2769 imap_create_missing_folders(folder);
2771 return 0;
2774 static gint imap_scan_tree(Folder *folder)
2776 gboolean subs_only = FALSE;
2777 if (folder->account) {
2778 debug_print(" scanning only subs %d\n", folder->account->imap_subsonly);
2779 subs_only = folder->account->imap_subsonly;
2781 return imap_scan_tree_real(folder, subs_only);
2784 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item, gboolean subs_only)
2786 /* reset recursion depth counter */
2787 session->scan_tree_recurs_depth = 0;
2789 return imap_scan_tree_recursive_dive(session, item, subs_only);
2792 static gint imap_scan_tree_recursive_dive(IMAPSession *session, FolderItem *item, gboolean subs_only)
2794 Folder *folder;
2795 IMAPFolder *imapfolder;
2796 FolderItem *new_item;
2797 GSList *item_list, *cur;
2798 GNode *node;
2799 gchar *real_path;
2800 gchar *wildcard_path;
2801 gchar separator;
2802 gchar wildcard[3];
2803 clist * lep_list;
2804 int r = MAILIMAP_NO_ERROR;
2806 g_return_val_if_fail(item != NULL, -1);
2807 g_return_val_if_fail(item->folder != NULL, -1);
2808 g_return_val_if_fail(item->no_sub == FALSE, -1);
2810 /* recursion depth limiter */
2811 if(session->scan_tree_recurs_depth >= prefs_common.imap_scan_tree_recurs_limit) {
2812 g_warning("IMAP scan tree recursion limit reached (%d, folder '%s')",
2813 prefs_common.imap_scan_tree_recurs_limit, item->folder->name);
2814 return -1;
2816 /* entering recursion func: increase depth counter */
2817 session->scan_tree_recurs_depth++;
2819 folder = item->folder;
2820 imapfolder = IMAP_FOLDER(folder);
2822 separator = imap_get_path_separator(session, imapfolder, item->path, &r);
2823 if (is_fatal(r))
2824 return r;
2826 if (folder->ui_func)
2827 folder->ui_func(folder, item, folder->ui_func_data);
2829 if (item->path) {
2830 wildcard[0] = separator;
2831 wildcard[1] = '%';
2832 wildcard[2] = '\0';
2833 real_path = imap_get_real_path(session, imapfolder, item->path, &r);
2834 if (is_fatal(r)) {
2835 g_free(real_path);
2836 return r;
2838 } else {
2839 wildcard[0] = '%';
2840 wildcard[1] = '\0';
2841 real_path = g_strdup("");
2844 Xstrcat_a(wildcard_path, real_path, wildcard,
2845 {g_free(real_path); return MAILIMAP_ERROR_BAD_STATE;});
2846 lep_list = NULL;
2848 if (subs_only)
2849 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
2850 else
2851 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
2853 if (r != MAILIMAP_NO_ERROR) {
2854 imap_handle_error(SESSION(session), NULL, r);
2855 item_list = NULL;
2856 g_free(real_path);
2857 return r;
2859 else {
2860 item_list = imap_list_from_lep(imapfolder,
2861 lep_list, real_path, FALSE);
2862 mailimap_list_result_free(lep_list);
2865 g_free(real_path);
2867 node = item->node->children;
2868 while (node != NULL) {
2869 FolderItem *old_item = FOLDER_ITEM(node->data);
2870 GNode *next = node->next;
2872 new_item = NULL;
2873 for (cur = item_list; cur != NULL; cur = cur->next) {
2874 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2875 if (!g_strcmp0(old_item->path, cur_item->path)) {
2876 new_item = cur_item;
2877 break;
2880 if (!new_item) {
2881 if (old_item && old_item->path && !strcasecmp(old_item->path, "INBOX")) {
2882 debug_print("not removing INBOX\n");
2883 } else {
2884 debug_print("folder '%s' not found. removing...\n",
2885 old_item->path);
2886 folder_item_remove(old_item);
2888 } else {
2889 old_item->no_sub = new_item->no_sub;
2890 old_item->no_select = new_item->no_select;
2891 if (old_item->no_sub == TRUE && node->children) {
2892 debug_print("folder '%s' doesn't have "
2893 "subfolders. removing...\n",
2894 old_item->path);
2895 folder_item_remove_children(old_item);
2899 node = next;
2902 for (cur = item_list; cur != NULL; cur = cur->next) {
2903 FolderItem *cur_item = FOLDER_ITEM(cur->data);
2904 new_item = NULL;
2906 for (node = item->node->children; node != NULL;
2907 node = node->next) {
2908 if (!g_strcmp0(FOLDER_ITEM(node->data)->path,
2909 cur_item->path)) {
2910 new_item = FOLDER_ITEM(node->data);
2911 folder_item_destroy(cur_item);
2912 cur_item = NULL;
2913 break;
2916 if (!new_item) {
2917 new_item = cur_item;
2918 debug_print("new folder '%s' found.\n", new_item->path);
2919 folder_item_append(item, new_item);
2922 if (!strcasecmp(new_item->path, "INBOX")) {
2923 new_item->stype = F_INBOX;
2924 folder->inbox = new_item;
2925 } else if (!folder_item_parent(item) || item->stype == F_INBOX) {
2926 gchar *base;
2928 base = g_path_get_basename(new_item->path);
2930 if (!folder->outbox && !g_ascii_strcasecmp(base, "Sent")) {
2931 new_item->stype = F_OUTBOX;
2932 folder->outbox = new_item;
2933 } else if (!folder->draft && (!g_ascii_strcasecmp(base, "Drafts") || !g_ascii_strcasecmp(base, "Draft"))) {
2934 new_item->stype = F_DRAFT;
2935 folder->draft = new_item;
2936 } else if (!folder->queue && !g_ascii_strcasecmp(base, "Queue")) {
2937 new_item->stype = F_QUEUE;
2938 folder->queue = new_item;
2939 } else if (!folder->trash && !g_ascii_strcasecmp(base, "Trash")) {
2940 new_item->stype = F_TRASH;
2941 folder->trash = new_item;
2943 g_free(base);
2946 if (new_item->no_sub == FALSE) {
2947 imap_scan_tree_recursive_dive(session, new_item, subs_only);
2949 /* entering recursion func: increase depth counter */
2950 session->scan_tree_recurs_depth--;
2951 if (session->scan_tree_recurs_depth < 0)
2952 g_error("IMAP scan tree recursion underflow (%d)",
2953 session->scan_tree_recurs_depth);
2957 g_slist_free(item_list);
2959 return MAILIMAP_NO_ERROR;
2962 GList *imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
2964 IMAPSession *session = imap_session_get(folder);
2965 gchar *real_path;
2966 gchar *wildcard_path;
2967 gchar separator;
2968 gchar wildcard[3];
2969 clist * lep_list;
2970 GSList *item_list = NULL, *cur;
2971 GList *child_list = NULL, *tmplist = NULL;
2972 GSList *sub_list = NULL;
2973 int r = MAILIMAP_NO_ERROR;
2975 if (!session)
2976 return NULL;
2978 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &r);
2979 if (is_fatal(r))
2980 return NULL;
2982 if (item->path) {
2983 wildcard[0] = separator;
2984 wildcard[1] = '%';
2985 wildcard[2] = '\0';
2986 real_path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &r);
2987 if (is_fatal(r)) {
2988 g_free(real_path);
2989 return NULL;
2991 } else {
2992 wildcard[0] = '%';
2993 wildcard[1] = '\0';
2994 real_path = g_strdup("");
2997 Xstrcat_a(wildcard_path, real_path, wildcard,
2998 {g_free(real_path); return NULL;});
2999 lep_list = NULL;
3001 if (unsubs_only)
3002 statusbar_print_all(_("Looking for unsubscribed folders in %s..."),
3003 item->path?item->path:item->name);
3004 else
3005 statusbar_print_all(_("Looking for subfolders of %s..."),
3006 item->path?item->path:item->name);
3008 r = imap_threaded_list(folder, "", wildcard_path, &lep_list);
3009 if (r) {
3010 g_free(real_path);
3011 statusbar_pop_all();
3012 return NULL;
3014 item_list = imap_list_from_lep(IMAP_FOLDER(folder),
3015 lep_list, real_path, FALSE);
3016 mailimap_list_result_free(lep_list);
3018 for (cur = item_list; cur != NULL; cur = cur->next) {
3019 FolderItem *cur_item = FOLDER_ITEM(cur->data);
3020 if (recursive) {
3021 tmplist = imap_scan_subtree(folder, cur_item,
3022 unsubs_only, recursive);
3023 if (tmplist)
3024 child_list = g_list_concat(child_list, tmplist);
3026 child_list = g_list_prepend(child_list,
3027 imap_get_real_path(session,
3028 IMAP_FOLDER(folder), cur_item->path, &r));
3029 if (is_fatal(r)) {
3030 g_free(real_path);
3031 statusbar_pop_all();
3032 return NULL;
3034 folder_item_destroy(cur_item);
3036 child_list = g_list_reverse(child_list);
3037 g_slist_free(item_list);
3039 if (unsubs_only) {
3040 r = imap_threaded_lsub(folder, "", wildcard_path, &lep_list);
3041 if (r) {
3042 g_free(real_path);
3043 statusbar_pop_all();
3044 return NULL;
3046 sub_list = imap_list_from_lep(IMAP_FOLDER(folder),
3047 lep_list, real_path, FALSE);
3048 mailimap_list_result_free(lep_list);
3050 for (cur = sub_list; cur != NULL; cur = cur->next) {
3051 FolderItem *cur_item = FOLDER_ITEM(cur->data);
3052 GList *oldlitem = NULL;
3053 gchar *tmp = imap_get_real_path(session,
3054 IMAP_FOLDER(folder), cur_item->path, &r);
3055 if (r) {
3056 g_free(real_path);
3057 statusbar_pop_all();
3058 g_free(tmp);
3059 return NULL;
3061 folder_item_destroy(cur_item);
3062 oldlitem = g_list_find_custom(
3063 child_list, tmp, (GCompareFunc)g_strcmp0);
3064 if (oldlitem) {
3065 child_list = g_list_remove_link(child_list, oldlitem);
3066 g_free(oldlitem->data);
3067 g_list_free(oldlitem);
3069 g_free(tmp);
3073 g_free(real_path);
3074 statusbar_pop_all();
3076 return child_list;
3079 static gint imap_create_tree(Folder *folder)
3081 g_return_val_if_fail(folder != NULL, -1);
3082 g_return_val_if_fail(folder->node != NULL, -1);
3083 g_return_val_if_fail(folder->node->data != NULL, -1);
3084 g_return_val_if_fail(folder->account != NULL, -1);
3086 imap_scan_tree(folder);
3087 imap_create_missing_folders(folder);
3089 return 0;
3092 static void imap_create_missing_folders(Folder *folder)
3094 g_return_if_fail(folder != NULL);
3096 if (!folder->inbox)
3097 folder->inbox = imap_create_special_folder
3098 (folder, F_INBOX, "INBOX");
3099 if (!folder->trash)
3100 folder->trash = imap_create_special_folder
3101 (folder, F_TRASH, "Trash");
3102 if (!folder->queue)
3103 folder->queue = imap_create_special_folder
3104 (folder, F_QUEUE, "Queue");
3105 if (!folder->outbox)
3106 folder->outbox = imap_create_special_folder
3107 (folder, F_OUTBOX, "Sent");
3108 if (!folder->draft)
3109 folder->draft = imap_create_special_folder
3110 (folder, F_DRAFT, "Drafts");
3113 static FolderItem *imap_create_special_folder(Folder *folder,
3114 SpecialFolderItemType stype,
3115 const gchar *name)
3117 FolderItem *item;
3118 FolderItem *new_item;
3120 g_return_val_if_fail(folder != NULL, NULL);
3121 g_return_val_if_fail(folder->node != NULL, NULL);
3122 g_return_val_if_fail(folder->node->data != NULL, NULL);
3123 g_return_val_if_fail(folder->account != NULL, NULL);
3124 g_return_val_if_fail(name != NULL, NULL);
3126 item = FOLDER_ITEM(folder->node->data);
3127 new_item = imap_create_folder(folder, item, name);
3129 if (!new_item) {
3130 g_warning("can't create '%s'", name);
3131 if (!folder->inbox) return NULL;
3133 new_item = imap_create_folder(folder, folder->inbox, name);
3134 if (!new_item)
3135 g_warning("can't create '%s' under INBOX", name);
3136 else
3137 new_item->stype = stype;
3138 } else
3139 new_item->stype = stype;
3141 return new_item;
3144 #ifdef G_OS_WIN32
3145 static gchar *imap_encode_unsafe_chars(const gchar *str)
3147 gchar *ret = NULL, *o_ret;
3148 gchar *i;
3149 if (!str)
3150 return NULL;
3151 ret = g_malloc(3*strlen(str)+1);
3152 o_ret = ret;
3153 for (i = (gchar *)str; *i; i++) {
3154 switch(*i) {
3155 case ':':
3156 case '|':
3157 case '<':
3158 case '>':
3159 case '*':
3160 case '?':
3161 case '#':
3162 *ret++ = '%';
3163 *ret++ = '0'+(*i/10);
3164 *ret++ = '0'+(*i%10);
3165 break;
3166 default:
3167 *ret++ = *i;
3170 *ret++ = '\0';
3171 return o_ret;
3173 #endif
3174 static gchar *imap_item_get_path(Folder *folder, FolderItem *item)
3176 gchar *folder_path, *path;
3177 gchar *item_path = NULL;
3179 g_return_val_if_fail(folder != NULL, NULL);
3180 g_return_val_if_fail(folder->account != NULL, NULL);
3181 g_return_val_if_fail(item != NULL, NULL);
3182 folder_path = prefs_account_cache_dir(folder->account, FALSE);
3184 g_return_val_if_fail(folder_path != NULL, NULL);
3186 #ifdef G_OS_UNIX
3187 item_path = g_strdup(item->path);
3188 #else
3189 item_path = imap_encode_unsafe_chars(item->path);
3190 #endif
3192 if (g_path_is_absolute(folder_path)) {
3193 if (item_path)
3194 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
3195 item_path, NULL);
3196 else
3197 path = g_strdup(folder_path);
3198 } else {
3199 if (item_path)
3200 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
3201 folder_path, G_DIR_SEPARATOR_S,
3202 item_path, NULL);
3203 else
3204 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
3205 folder_path, NULL);
3207 g_free(folder_path);
3208 g_free(item_path);
3209 #ifdef G_OS_WIN32
3210 while (strchr(path, '/'))
3211 *strchr(path, '/') = '\\';
3212 #endif
3214 return path;
3217 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
3218 const gchar *name)
3220 gchar *dirpath, *imap_path;
3221 IMAPSession *session;
3222 FolderItem *new_item;
3223 gchar separator;
3224 gchar *new_name;
3225 const gchar *p;
3226 gint ok = MAILIMAP_NO_ERROR;
3227 gboolean no_select = FALSE, no_sub = FALSE;
3228 gboolean exist = FALSE;
3230 g_return_val_if_fail(folder != NULL, NULL);
3231 g_return_val_if_fail(folder->account != NULL, NULL);
3232 g_return_val_if_fail(parent != NULL, NULL);
3233 g_return_val_if_fail(name != NULL, NULL);
3235 if (to_number(name) > 0) {
3236 gchar *cached_msg = imap_get_cached_filename(parent, to_number(name));
3237 if (is_file_exist(cached_msg)) {
3238 if (claws_unlink(cached_msg) != 0) {
3239 g_free(cached_msg);
3240 return NULL;
3243 g_free(cached_msg);
3246 debug_print("getting session...\n");
3247 session = imap_session_get(folder);
3248 if (!session) {
3249 return NULL;
3252 if (!folder_item_parent(parent) && strcasecmp(name, "INBOX") == 0) {
3253 dirpath = g_strdup(name);
3254 }else if (parent->path)
3255 dirpath = g_strconcat(parent->path, "/", name, NULL);
3256 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
3257 dirpath = g_strdup(name);
3258 else if (folder->account->imap_dir && *folder->account->imap_dir) {
3259 gchar *imap_dir;
3261 Xstrdup_a(imap_dir, folder->account->imap_dir, {return NULL;});
3262 strtailchomp(imap_dir, '/');
3263 dirpath = g_strconcat(imap_dir, "/", name, NULL);
3264 } else
3265 dirpath = g_strdup(name);
3269 /* keep trailing directory separator to create a folder that contains
3270 sub folder */
3271 imap_path = imap_utf8_to_modified_utf7(dirpath, FALSE);
3273 strtailchomp(dirpath, '/');
3274 Xstrdup_a(new_name, name, {
3275 g_free(dirpath);
3276 g_free(imap_path);
3277 return NULL;});
3279 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), imap_path, &ok);
3280 if (is_fatal(ok)) {
3281 g_free(dirpath);
3282 g_free(imap_path);
3283 return NULL;
3285 imap_path_separator_subst(imap_path, separator);
3286 /* remove trailing / for display */
3287 strtailchomp(new_name, '/');
3289 if (strcasecmp(dirpath, "INBOX") != 0) {
3290 int r;
3291 clist * lep_list;
3293 r = imap_threaded_list(folder, "", imap_path, &lep_list);
3294 if (r != MAILIMAP_NO_ERROR) {
3295 imap_handle_error(SESSION(session), NULL, r);
3296 log_warning(LOG_PROTOCOL, _("can't create mailbox: LIST failed\n"));
3297 g_free(imap_path);
3298 g_free(dirpath);
3299 return NULL;
3302 if (clist_count(lep_list) > 0)
3303 exist = TRUE;
3304 mailimap_list_result_free(lep_list);
3305 lep_list = NULL;
3306 if (!exist) {
3307 ok = imap_cmd_create(session, imap_path);
3308 if (ok != MAILIMAP_NO_ERROR) {
3309 log_warning(LOG_PROTOCOL, _("can't create mailbox\n"));
3310 g_free(imap_path);
3311 g_free(dirpath);
3312 return NULL;
3314 r = imap_threaded_list(folder, "", imap_path, &lep_list);
3315 if (r == MAILIMAP_NO_ERROR) {
3316 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
3317 lep_list, dirpath, TRUE);
3318 if (item_list) {
3319 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
3320 no_select = cur_item->no_select;
3321 no_sub = cur_item->no_sub;
3322 g_slist_free(item_list);
3324 mailimap_list_result_free(lep_list);
3325 } else {
3326 imap_handle_error(SESSION(session), NULL, r);
3329 imap_threaded_subscribe(folder, imap_path, TRUE);
3330 } else {
3331 clist *lep_list;
3332 int r;
3333 /* just get flags */
3334 r = imap_threaded_list(folder, "", "INBOX", &lep_list);
3335 if (r == MAILIMAP_NO_ERROR) {
3336 GSList *item_list = imap_list_from_lep(IMAP_FOLDER(folder),
3337 lep_list, dirpath, TRUE);
3338 if (item_list) {
3339 FolderItem *cur_item = FOLDER_ITEM(item_list->data);
3340 no_select = cur_item->no_select;
3341 no_sub = cur_item->no_sub;
3342 g_slist_free(item_list);
3344 mailimap_list_result_free(lep_list);
3345 } else {
3346 imap_handle_error(SESSION(session), NULL, r);
3350 new_item = folder_item_new(folder, new_name, dirpath);
3351 new_item->no_select = no_select;
3352 new_item->no_sub = no_sub;
3353 folder_item_append(parent, new_item);
3354 g_free(imap_path);
3355 g_free(dirpath);
3357 dirpath = folder_item_get_path(new_item);
3358 if (!is_dir_exist(dirpath))
3359 make_dir_hier(dirpath);
3360 g_free(dirpath);
3362 if (exist) {
3363 /* folder existed, scan it */
3364 imap_scan_required(folder, new_item);
3365 folder_item_scan_full(new_item, FALSE);
3368 return new_item;
3371 static gint imap_rename_folder(Folder *folder, FolderItem *item,
3372 const gchar *name)
3374 gchar *dirpath;
3375 gchar *newpath;
3376 gchar *real_oldpath;
3377 gchar *real_newpath;
3378 gchar *paths[2];
3379 gchar *old_cache_dir;
3380 gchar *new_cache_dir;
3381 IMAPSession *session;
3382 gchar separator;
3383 gint ok = MAILIMAP_NO_ERROR;
3384 gint exists, recent, unseen;
3385 guint32 uid_validity;
3387 g_return_val_if_fail(folder != NULL, -1);
3388 g_return_val_if_fail(item != NULL, -1);
3389 g_return_val_if_fail(item->path != NULL, -1);
3390 g_return_val_if_fail(name != NULL, -1);
3392 debug_print("getting session...\n");
3393 session = imap_session_get(folder);
3394 if (!session) {
3395 return -1;
3398 if (strchr(name, imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &ok)) != NULL ||
3399 is_fatal(ok)) {
3400 g_warning("new folder name must not contain the namespace path separator");
3401 return -1;
3404 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &ok);
3405 if (is_fatal(ok)) {
3406 g_free(real_oldpath);
3407 return -1;
3410 g_free(session->mbox);
3411 session->mbox = NULL;
3412 session->exists = 0;
3413 session->recent = 0;
3414 session->expunge = 0;
3415 ok = imap_cmd_examine(session, "INBOX",
3416 &exists, &recent, &unseen, &uid_validity, FALSE);
3417 if (ok != MAILIMAP_NO_ERROR) {
3418 g_free(real_oldpath);
3419 return -1;
3422 separator = imap_get_path_separator(session, IMAP_FOLDER(folder), item->path, &ok);
3423 if (is_fatal(ok)) {
3424 g_free(real_oldpath);
3425 return -1;
3427 if (strchr(item->path, '/')) {
3428 dirpath = g_path_get_dirname(item->path);
3429 newpath = g_strconcat(dirpath, "/", name, NULL);
3430 g_free(dirpath);
3431 } else
3432 newpath = g_strdup(name);
3434 real_newpath = imap_utf8_to_modified_utf7(newpath, FALSE);
3435 imap_path_separator_subst(real_newpath, separator);
3437 ok = imap_cmd_rename(session, real_oldpath, real_newpath);
3438 if (ok != MAILIMAP_NO_ERROR) {
3439 log_warning(LOG_PROTOCOL, _("can't rename mailbox: %s to %s\n"),
3440 real_oldpath, real_newpath);
3441 g_free(real_oldpath);
3442 g_free(newpath);
3443 g_free(real_newpath);
3444 return -1;
3446 g_free(item->name);
3447 item->name = g_strdup(name);
3449 old_cache_dir = folder_item_get_path(item);
3451 paths[0] = g_strdup(item->path);
3452 paths[1] = newpath;
3453 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
3454 imap_rename_folder_func, paths);
3456 if (is_dir_exist(old_cache_dir)) {
3457 new_cache_dir = folder_item_get_path(item);
3458 if (g_rename(old_cache_dir, new_cache_dir) < 0) {
3459 FILE_OP_ERROR(old_cache_dir, "rename");
3461 g_free(new_cache_dir);
3464 g_free(old_cache_dir);
3465 g_free(paths[0]);
3466 g_free(newpath);
3467 g_free(real_oldpath);
3468 g_free(real_newpath);
3469 return 0;
3472 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
3474 gchar *path;
3475 gint r = MAILIMAP_NO_ERROR;
3476 IMAPSession *session;
3477 debug_print("getting session...\n");
3479 session = imap_session_get(folder);
3480 if (!session) {
3481 return -1;
3483 if (item && item->path) {
3484 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &r);
3485 if (!path)
3486 return -1;
3487 if (is_fatal(r)) {
3488 g_free(path);
3489 return -1;
3491 if (!strcasecmp(path, "INBOX") && sub == FALSE) {
3492 g_free(path);
3493 return -1;
3495 debug_print("%ssubscribing %s\n", sub?"":"un", path);
3496 r = imap_threaded_subscribe(folder, path, sub);
3497 g_free(path);
3498 } else if (rpath) {
3499 r = imap_threaded_subscribe(folder, rpath, sub);
3500 } else
3501 return -1;
3502 return r;
3505 static gint imap_remove_folder_real(Folder *folder, FolderItem *item)
3507 gint ok = MAILIMAP_NO_ERROR;
3508 IMAPSession *session;
3509 gchar *path;
3510 gchar *cache_dir;
3511 gboolean selected_folder;
3513 g_return_val_if_fail(folder != NULL, -1);
3514 g_return_val_if_fail(item != NULL, -1);
3515 g_return_val_if_fail(item->path != NULL, -1);
3517 debug_print("getting session...\n");
3518 session = imap_session_get(folder);
3519 if (!session) {
3520 return -1;
3522 path = imap_get_real_path(session, IMAP_FOLDER(folder), item->path, &ok);
3523 if (is_fatal(ok)) {
3524 g_free(path);
3525 return -1;
3527 imap_threaded_subscribe(folder, path, FALSE);
3529 selected_folder = (session->mbox != NULL) &&
3530 (!strcmp(session->mbox, item->path));
3531 if (selected_folder) {
3532 ok = imap_cmd_close(session);
3533 if (ok != MAILIMAP_NO_ERROR) {
3534 debug_print("close err %d\n", ok);
3535 return ok;
3538 ok = imap_cmd_delete(session, path);
3539 if (ok != MAILIMAP_NO_ERROR && !is_fatal(ok)) {
3540 gchar *tmp = NULL;
3542 ok = MAILIMAP_NO_ERROR;
3543 tmp = g_strdup_printf("%s%c", path,
3544 imap_get_path_separator(session, IMAP_FOLDER(folder), path, &ok));
3545 g_free(path);
3546 path = tmp;
3547 if (!is_fatal(ok))
3548 ok = imap_cmd_delete(session, path);
3551 if (ok != MAILIMAP_NO_ERROR) {
3552 log_warning(LOG_PROTOCOL, _("can't delete mailbox\n"));
3553 g_free(path);
3554 return -1;
3557 g_free(path);
3558 cache_dir = folder_item_get_path(item);
3559 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
3560 g_warning("can't remove directory '%s'", cache_dir);
3561 g_free(cache_dir);
3562 folder_item_remove(item);
3563 return 0;
3566 static gint imap_remove_folder(Folder *folder, FolderItem *item)
3568 GNode *node, *next;
3570 g_return_val_if_fail(item != NULL, -1);
3571 g_return_val_if_fail(item->folder != NULL, -1);
3572 g_return_val_if_fail(item->node != NULL, -1);
3574 node = item->node->children;
3575 while (node != NULL) {
3576 next = node->next;
3577 if (imap_remove_folder(folder, FOLDER_ITEM(node->data)) < 0)
3578 return -1;
3579 node = next;
3581 debug_print("IMAP removing %s\n", item->path);
3583 if (imap_remove_all_msg(folder, item) < 0)
3584 return -1;
3585 return imap_remove_folder_real(folder, item);
3588 typedef struct _uncached_data {
3589 IMAPSession *session;
3590 FolderItem *item;
3591 MsgNumberList *numlist;
3592 guint cur;
3593 guint total;
3594 gboolean done;
3595 int ok;
3596 } uncached_data;
3598 static void *imap_get_uncached_messages_thread(void *data)
3600 uncached_data *stuff = (uncached_data *)data;
3601 IMAPSession *session = stuff->session;
3602 FolderItem *item = stuff->item;
3603 MsgNumberList *numlist = stuff->numlist;
3604 GSList *newlist = NULL;
3605 GSList *llast = NULL;
3606 GSList *seq_list, *cur;
3607 gboolean got_alien_tags = FALSE;
3609 debug_print("uncached_messages\n");
3611 if (session == NULL || item == NULL || item->folder == NULL
3612 || FOLDER_CLASS(item->folder) != &imap_class) {
3613 stuff->done = TRUE;
3614 return NULL;
3617 seq_list = imap_get_lep_set_from_numlist(IMAP_FOLDER(item->folder), numlist);
3618 debug_print("get msgs info\n");
3619 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
3620 struct mailimap_set * imapset;
3621 unsigned int i;
3622 int r;
3623 carray * env_list;
3624 int count;
3626 if (session->cancelled)
3627 break;
3629 imapset = cur->data;
3631 r = imap_threaded_fetch_env(session->folder,
3632 imapset, &env_list);
3633 if (r != MAILIMAP_NO_ERROR) {
3634 imap_handle_error(SESSION(session), NULL, r);
3635 if (is_fatal(r)) {
3636 stuff->ok = r;
3637 return NULL;
3639 continue;
3642 session_set_access_time(SESSION(session));
3644 count = 0;
3645 for(i = 0 ; i < carray_count(env_list) ; i += 2) {
3646 struct imap_fetch_env_info * info;
3647 MsgInfo * msginfo;
3648 GSList *tags = NULL, *cur = NULL;
3649 info = carray_get(env_list, i);
3650 tags = carray_get(env_list, i+1);
3651 msginfo = imap_envelope_from_lep(info, item);
3652 if (msginfo == NULL) {
3653 slist_free_strings_full(tags);
3654 continue;
3656 g_slist_free(msginfo->tags);
3657 msginfo->tags = NULL;
3659 for (cur = tags; cur; cur = cur->next) {
3660 gchar *real_tag = imap_modified_utf7_to_utf8(cur->data, TRUE);
3661 gint id = 0;
3662 id = tags_get_id_for_str(real_tag);
3663 if (id == -1) {
3664 id = tags_add_tag(real_tag);
3665 got_alien_tags = TRUE;
3667 if (!g_slist_find(msginfo->tags, GINT_TO_POINTER(id))) {
3668 msginfo->tags = g_slist_prepend(
3669 msginfo->tags,
3670 GINT_TO_POINTER(id));
3672 g_free(real_tag);
3674 if (msginfo->tags)
3675 msginfo->tags = g_slist_reverse(msginfo->tags);
3676 slist_free_strings_full(tags);
3677 msginfo->folder = item;
3678 if (!newlist)
3679 llast = newlist = g_slist_append(newlist, msginfo);
3680 else {
3681 llast = g_slist_append(llast, msginfo);
3682 llast = llast->next;
3684 count ++;
3687 imap_fetch_env_free(env_list);
3690 if (got_alien_tags) {
3691 tags_write_tags();
3692 main_window_reflect_tags_changes(mainwindow_get_mainwindow());
3695 imap_lep_set_free(seq_list);
3697 session_set_access_time(SESSION(session));
3698 stuff->done = TRUE;
3699 return newlist;
3702 #define MAX_MSG_NUM 50
3704 static GSList *imap_get_uncached_messages(IMAPSession *session,
3705 FolderItem *item,
3706 MsgNumberList *numlist,
3707 int *r)
3709 GSList *result = NULL;
3710 GSList * cur;
3711 uncached_data *data = g_new0(uncached_data, 1);
3713 cur = numlist;
3714 data->total = g_slist_length(numlist);
3715 data->ok = MAILIMAP_NO_ERROR;
3716 debug_print("messages list : %i\n", data->total);
3718 while (cur != NULL) {
3719 GSList * partial_result;
3720 int count;
3721 GSList * newlist;
3722 GSList * llast;
3724 llast = NULL;
3725 count = 0;
3726 newlist = NULL;
3727 while (count < MAX_MSG_NUM) {
3728 void * p;
3730 p = cur->data;
3732 if (newlist == NULL)
3733 llast = newlist = g_slist_append(newlist, p);
3734 else {
3735 llast = g_slist_append(llast, p);
3736 llast = llast->next;
3738 count ++;
3740 cur = cur->next;
3741 if (cur == NULL)
3742 break;
3745 data->done = FALSE;
3746 data->session = session;
3747 data->item = item;
3748 data->numlist = newlist;
3749 data->cur += count;
3751 if (prefs_common.work_offline &&
3752 !inc_offline_should_override(FALSE,
3753 _("Claws Mail needs network access in order "
3754 "to access the IMAP server."))) {
3755 g_free(data);
3756 return NULL;
3759 partial_result =
3760 (GSList *)imap_get_uncached_messages_thread(data);
3761 *r = data->ok;
3762 if (data->ok != MAILIMAP_NO_ERROR) {
3763 goto bail;
3765 statusbar_progress_all(data->cur,data->total, 1);
3767 g_slist_free(newlist);
3769 result = g_slist_concat(result, partial_result);
3771 bail:
3772 g_free(data);
3774 statusbar_progress_all(0,0,0);
3775 statusbar_pop_all();
3777 return result;
3780 static void imap_delete_all_cached_messages(FolderItem *item)
3782 gchar *dir;
3784 g_return_if_fail(item != NULL);
3785 g_return_if_fail(item->folder != NULL);
3786 g_return_if_fail(FOLDER_CLASS(item->folder) == &imap_class);
3788 debug_print("Deleting all cached messages...\n");
3790 dir = folder_item_get_path(item);
3791 if (is_dir_exist(dir))
3792 remove_all_numbered_files(dir);
3793 g_free(dir);
3795 debug_print("Deleting all cached messages done.\n");
3798 gchar imap_get_path_separator_for_item(FolderItem *item)
3800 Folder *folder = NULL;
3801 IMAPFolder *imap_folder = NULL;
3802 IMAPSession *session = NULL;
3803 gchar result = '/';
3804 gint ok = MAILIMAP_NO_ERROR;
3805 if (!item)
3806 return '/';
3807 folder = item->folder;
3809 if (!folder)
3810 return '/';
3812 imap_folder = IMAP_FOLDER(folder);
3814 debug_print("getting session...\n");
3815 session = imap_session_get(FOLDER(folder));
3816 result = imap_get_path_separator(session, imap_folder, item->path, &ok);
3817 return result;
3820 static gchar imap_refresh_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *subfolder, gint *ok)
3822 clist * lep_list;
3823 int r;
3824 gchar separator = '\0';
3826 g_return_val_if_fail(session != NULL, '/');
3827 r = imap_threaded_list((Folder *)folder, "", subfolder, &lep_list);
3829 if (r != MAILIMAP_NO_ERROR) {
3830 imap_handle_error(SESSION(session), NULL, r);
3831 log_warning(LOG_PROTOCOL, _("LIST failed\n"));
3832 *ok = r;
3833 return '\0';
3836 if (lep_list != NULL && clist_count(lep_list) > 0) {
3837 clistiter * iter = clist_begin(lep_list);
3838 struct mailimap_mailbox_list * mb;
3839 mb = clist_content(iter);
3841 separator = mb->mb_delimiter;
3842 debug_print("got separator: %c\n", folder->last_seen_separator);
3844 *ok = MAILIMAP_NO_ERROR;
3845 mailimap_list_result_free(lep_list);
3846 return separator;
3849 static gchar imap_get_path_separator(IMAPSession *session, IMAPFolder *folder, const gchar *path, gint *ok)
3851 gchar separator = '/';
3852 *ok = MAILIMAP_NO_ERROR;
3853 if (folder->last_seen_separator == 0) {
3854 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "", ok);
3857 if (folder->last_seen_separator == 0) {
3858 folder->last_seen_separator = imap_refresh_path_separator(session, folder, "INBOX", ok);
3861 if (folder->last_seen_separator != 0) {
3862 debug_print("using separator: %c\n", folder->last_seen_separator);
3863 return folder->last_seen_separator;
3866 return separator;
3869 static gchar *imap_get_real_path(IMAPSession *session, IMAPFolder *folder, const gchar *path, gint *ok)
3871 gchar *real_path = NULL;
3872 gchar separator;
3874 g_return_val_if_fail(folder != NULL, NULL);
3875 g_return_val_if_fail(path != NULL, NULL);
3877 *ok = MAILIMAP_NO_ERROR;
3879 real_path = imap_utf8_to_modified_utf7(path, FALSE);
3880 separator = imap_get_path_separator(session, folder, path, ok);
3881 if (*ok == MAILIMAP_NO_ERROR)
3882 imap_path_separator_subst(real_path, separator);
3884 return real_path;
3887 static gint imap_set_message_flags(IMAPSession *session,
3888 IMAPFolderItem *item,
3889 MsgNumberList *numlist,
3890 IMAPFlags flags,
3891 GSList *tags,
3892 gboolean is_set)
3894 gint ok = 0;
3895 GSList *seq_list;
3896 GSList * cur;
3897 gint total = 0;
3898 IMAPFolder *folder = NULL;
3899 GSList *sorted_list = NULL;
3901 if (numlist == NULL || session == NULL)
3902 return MAILIMAP_ERROR_BAD_STATE;
3904 folder = IMAP_FOLDER(session->folder);
3906 sorted_list = g_slist_copy(numlist);
3907 sorted_list = g_slist_sort(sorted_list, g_int_compare);
3909 cur = g_slist_last(sorted_list);
3911 if (cur)
3912 total = GPOINTER_TO_INT(cur->data);
3914 seq_list = imap_get_lep_set_from_numlist(IMAP_FOLDER(session->folder), sorted_list);
3916 statusbar_print_all(_("Flagging messages..."));
3918 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
3919 struct mailimap_set * imapset = (struct mailimap_set *)cur->data;
3920 struct mailimap_set_item *set_item = NULL;
3922 if (imapset->set_list)
3923 set_item = clist_content(clist_begin(imapset->set_list));
3924 else
3925 continue;
3927 if (set_item == NULL)
3928 continue;
3930 statusbar_progress_all(set_item->set_first, total, 1);
3932 ok = imap_cmd_store(session, item, imapset,
3933 flags, tags, is_set);
3934 statusbar_progress_all(set_item->set_last, total, 1);
3935 if (ok != MAILIMAP_NO_ERROR && folder->max_set_size > 20) {
3936 /* reduce max set size */
3937 folder->max_set_size /= 2;
3939 if (ok != MAILIMAP_NO_ERROR && is_fatal(ok)) {
3940 break;
3944 g_slist_free(sorted_list);
3946 statusbar_progress_all(0,0,0);
3947 statusbar_pop_all();
3949 imap_lep_set_free(seq_list);
3951 return ok;
3954 typedef struct _select_data {
3955 IMAPSession *session;
3956 gchar *real_path;
3957 gint *exists;
3958 gint *recent;
3959 gint *unseen;
3960 guint32 *uid_validity;
3961 gboolean done;
3962 } select_data;
3964 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
3965 FolderItem *item,
3966 gint *exists, gint *recent, gint *unseen,
3967 guint32 *uid_validity, gint *can_create_flags,
3968 gboolean block)
3970 gchar *real_path;
3971 gint ok = MAILIMAP_NO_ERROR;
3972 gint exists_, recent_, unseen_;
3973 guint32 uid_validity_;
3974 gint can_create_flags_;
3975 const gchar *path = item ? item->path:NULL;
3977 if (!item) {
3978 return MAILIMAP_ERROR_BAD_STATE;
3981 if (!exists && !recent && !unseen && !uid_validity && !can_create_flags) {
3982 if (session->mbox && strcmp(session->mbox, path) == 0)
3983 return MAILIMAP_NO_ERROR;
3985 if (!exists && !recent && !unseen && !uid_validity && can_create_flags) {
3986 if (session->mbox && strcmp(session->mbox, path) == 0) {
3987 if (IMAP_FOLDER_ITEM(item)->can_create_flags != ITEM_CAN_CREATE_FLAGS_UNKNOWN)
3988 return MAILIMAP_NO_ERROR;
3991 if (!exists)
3992 exists = &exists_;
3993 if (!recent)
3994 recent = &recent_;
3995 if (!unseen)
3996 unseen = &unseen_;
3997 if (!uid_validity)
3998 uid_validity = &uid_validity_;
3999 if (!can_create_flags)
4000 can_create_flags = &can_create_flags_;
4002 g_free(session->mbox);
4003 session->mbox = NULL;
4004 session->exists = 0;
4005 session->recent = 0;
4006 session->expunge = 0;
4008 real_path = imap_get_real_path(session, folder, path, &ok);
4009 if (is_fatal(ok)) {
4010 g_free(real_path);
4011 return ok;
4013 g_slist_free(IMAP_FOLDER_ITEM(item)->ok_flags);
4014 IMAP_FOLDER_ITEM(item)->ok_flags = NULL;
4015 ok = imap_cmd_select(session, real_path,
4016 exists, recent, unseen, uid_validity, can_create_flags,
4017 &(IMAP_FOLDER_ITEM(item)->ok_flags), block);
4018 if (ok != MAILIMAP_NO_ERROR) {
4019 log_warning(LOG_PROTOCOL, _("can't select folder: %s\n"), real_path);
4020 } else {
4021 session->mbox = g_strdup(path);
4022 session->folder_content_changed = FALSE;
4023 session->exists = *exists;
4024 session->recent = *recent;
4025 session->expunge = 0;
4026 session->unseen = *unseen;
4027 session->uid_validity = *uid_validity;
4028 debug_print("select: exists %d recent %d expunge %d uid_validity %d can_create_flags %d\n",
4029 session->exists, session->recent, session->expunge,
4030 session->uid_validity, *can_create_flags);
4032 if (*can_create_flags) {
4033 IMAP_FOLDER_ITEM(item)->can_create_flags = ITEM_CAN_CREATE_FLAGS;
4034 } else {
4035 IMAP_FOLDER_ITEM(item)->can_create_flags = ITEM_CANNOT_CREATE_FLAGS;
4037 g_free(real_path);
4039 return ok;
4042 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
4043 const gchar *path, IMAPFolderItem *item,
4044 gint *messages,
4045 guint32 *uid_next, guint32 *uid_validity,
4046 gint *unseen, gboolean block)
4048 int r = MAILIMAP_NO_ERROR;
4049 clistiter * iter;
4050 struct mailimap_mailbox_data_status * data_status;
4051 int got_values;
4052 gchar *real_path;
4053 guint mask = 0;
4055 real_path = imap_get_real_path(session, folder, path, &r);
4056 if (is_fatal(r)) {
4057 g_free(real_path);
4058 return r;
4060 if (messages) {
4061 mask |= 1 << 0;
4062 *messages = 0;
4064 if (uid_next) {
4065 mask |= 1 << 2;
4066 *uid_next = 0;
4068 if (uid_validity) {
4069 mask |= 1 << 3;
4070 *uid_validity = 0;
4072 if (unseen) {
4073 mask |= 1 << 4;
4074 *unseen = 0;
4077 if (session->mbox != NULL &&
4078 !strcmp(session->mbox, item->item.path)) {
4079 r = imap_cmd_close(session);
4080 if (r != MAILIMAP_NO_ERROR) {
4081 debug_print("close err %d\n", r);
4082 g_free(real_path);
4083 return r;
4087 r = imap_threaded_status(FOLDER(folder), real_path,
4088 &data_status, mask);
4090 g_free(real_path);
4091 if (r != MAILIMAP_NO_ERROR) {
4092 imap_handle_error(SESSION(session), NULL, r);
4093 debug_print("status err %d\n", r);
4094 return r;
4097 if (data_status == NULL || data_status->st_info_list == NULL) {
4098 debug_print("data_status %p\n", data_status);
4099 if (data_status) {
4100 debug_print("data_status->st_info_list %p\n", data_status->st_info_list);
4101 mailimap_mailbox_data_status_free(data_status);
4103 return MAILIMAP_ERROR_BAD_STATE;
4106 got_values = 0;
4107 if (data_status->st_info_list) {
4108 for(iter = clist_begin(data_status->st_info_list) ; iter != NULL ;
4109 iter = clist_next(iter)) {
4110 struct mailimap_status_info * info;
4112 info = clist_content(iter);
4113 switch (info->st_att) {
4114 case MAILIMAP_STATUS_ATT_MESSAGES:
4115 if (messages) {
4116 * messages = info->st_value;
4117 got_values |= 1 << 0;
4119 break;
4121 case MAILIMAP_STATUS_ATT_UIDNEXT:
4122 if (uid_next) {
4123 * uid_next = info->st_value;
4124 got_values |= 1 << 2;
4126 break;
4128 case MAILIMAP_STATUS_ATT_UIDVALIDITY:
4129 if (uid_validity) {
4130 * uid_validity = info->st_value;
4131 got_values |= 1 << 3;
4133 break;
4135 case MAILIMAP_STATUS_ATT_UNSEEN:
4136 if (unseen) {
4137 * unseen = info->st_value;
4138 got_values |= 1 << 4;
4140 break;
4144 mailimap_mailbox_data_status_free(data_status);
4146 if (got_values != mask) {
4147 g_warning("status: incomplete values received (%d)", got_values);
4149 return MAILIMAP_NO_ERROR;
4152 static void imap_free_capabilities(IMAPSession *session)
4154 slist_free_strings_full(session->capability);
4155 session->capability = NULL;
4158 /* low-level IMAP4rev1 commands */
4160 static gint imap_cmd_login(IMAPSession *session,
4161 const gchar *user, const gchar *pass,
4162 const gchar *type)
4164 int r;
4165 gint ok;
4167 if (!strcmp(type, "plaintext") && imap_has_capability(session, "LOGINDISABLED")) {
4168 ok = MAILIMAP_ERROR_BAD_STATE;
4169 if (imap_has_capability(session, "STARTTLS")) {
4170 #ifdef USE_GNUTLS
4171 log_warning(LOG_PROTOCOL, _("Server requires STARTTLS to log in.\n"));
4172 ok = imap_cmd_starttls(session);
4173 if (ok != MAILIMAP_NO_ERROR) {
4174 log_warning(LOG_PROTOCOL, _("Can't start STARTTLS session.\n"));
4175 return ok;
4176 } else {
4177 /* refresh capas */
4178 imap_free_capabilities(session);
4179 if ((r = imap_get_capabilities(session)) != MAILIMAP_NO_ERROR) {
4180 imap_handle_error(SESSION(session), NULL, r);
4181 log_warning(LOG_PROTOCOL, _("Can't refresh capabilities.\n"));
4182 return r;
4185 #else
4186 log_error(LOG_PROTOCOL, _("Connection to %s failed: "
4187 "server requires STARTTLS, but Claws Mail "
4188 "has been compiled without STARTTLS "
4189 "support.\n"),
4190 SESSION(session)->server);
4191 return MAILIMAP_ERROR_LOGIN;
4192 #endif
4193 } else {
4194 log_error(LOG_PROTOCOL, _("Server logins are disabled.\n"));
4195 return MAILIMAP_ERROR_LOGIN;
4199 log_print(LOG_PROTOCOL, "IMAP> Logging %s to %s using %s\n",
4200 user,
4201 SESSION(session)->server,
4202 type);
4203 r = imap_threaded_login(session->folder, user, pass, type);
4204 if (r != MAILIMAP_NO_ERROR) {
4205 imap_handle_error(SESSION(session), NULL, r);
4206 log_print(LOG_PROTOCOL, "IMAP< Error logging in to %s\n",
4207 SESSION(session)->server);
4208 ok = r;
4209 } else {
4210 log_print(LOG_PROTOCOL, "IMAP< Login to %s successful\n",
4211 SESSION(session)->server);
4212 ok = MAILIMAP_NO_ERROR;
4214 return ok;
4217 static gint imap_cmd_noop(IMAPSession *session)
4219 int r;
4220 unsigned int exists, recent, expunge, unseen, uidnext, uidval;
4222 r = imap_threaded_noop(session->folder, &exists, &recent, &expunge, &unseen, &uidnext, &uidval);
4223 if (r != MAILIMAP_NO_ERROR) {
4224 imap_handle_error(SESSION(session), NULL, r);
4225 debug_print("noop err %d\n", r);
4226 return r;
4229 if ((exists && exists != session->exists)
4230 || (recent && recent != session->recent)
4231 || (expunge && expunge != session->expunge)
4232 || (unseen && unseen != session->unseen)) {
4233 session->folder_content_changed = TRUE;
4235 if (uidnext != 0 && uidnext != session->uid_next) {
4236 session->uid_next = uidnext;
4237 session->folder_content_changed = TRUE;
4239 if (uidval != 0 && uidval != session->uid_validity) {
4240 session->uid_validity = uidval;
4241 session->folder_content_changed = TRUE;
4244 session->exists = exists;
4245 session->recent = recent;
4246 session->expunge = expunge;
4247 session->unseen = unseen;
4249 session_set_access_time(SESSION(session));
4251 return MAILIMAP_NO_ERROR;
4254 #ifdef USE_GNUTLS
4255 static gint imap_cmd_starttls(IMAPSession *session)
4257 int r;
4259 r = imap_threaded_starttls(session->folder,
4260 SESSION(session)->server, SESSION(session)->port);
4261 if (r != MAILIMAP_NO_ERROR) {
4262 imap_handle_error(SESSION(session), NULL, r);
4263 debug_print("STARTTLS err %d\n", r);
4264 return r;
4266 return MAILIMAP_NO_ERROR;
4268 #endif
4270 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
4271 gint *exists, gint *recent, gint *unseen,
4272 guint32 *uid_validity, gint *can_create_flags,
4273 GSList **ok_flags, gboolean block)
4275 int r;
4277 r = imap_threaded_select(session->folder, folder,
4278 exists, recent, unseen, uid_validity, can_create_flags, ok_flags);
4279 if (r != MAILIMAP_NO_ERROR) {
4280 imap_handle_error(SESSION(session), NULL, r);
4281 debug_print("select err %d\n", r);
4282 return r;
4284 return MAILIMAP_NO_ERROR;
4287 static gint imap_cmd_close(IMAPSession *session)
4289 int r;
4291 r = imap_threaded_close(session->folder);
4292 if (r != MAILIMAP_NO_ERROR) {
4293 imap_handle_error(SESSION(session), NULL, r);
4294 debug_print("close err %d\n", r);
4295 return r;
4297 g_free(session->mbox);
4298 session->mbox = NULL;
4299 session->exists = 0;
4300 session->recent = 0;
4301 session->expunge = 0;
4302 return MAILIMAP_NO_ERROR;
4305 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
4306 gint *exists, gint *recent, gint *unseen,
4307 guint32 *uid_validity, gboolean block)
4309 int r;
4311 r = imap_threaded_examine(session->folder, folder,
4312 exists, recent, unseen, uid_validity);
4313 if (r != MAILIMAP_NO_ERROR) {
4314 imap_handle_error(SESSION(session), NULL, r);
4315 debug_print("examine err %d\n", r);
4317 return r;
4319 return MAILIMAP_NO_ERROR;
4322 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
4324 int r;
4326 r = imap_threaded_create(session->folder, folder);
4327 if (r != MAILIMAP_NO_ERROR) {
4328 imap_handle_error(SESSION(session), NULL, r);
4329 return r;
4332 return MAILIMAP_NO_ERROR;
4335 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
4336 const gchar *new_folder)
4338 int r;
4340 r = imap_threaded_rename(session->folder, old_folder,
4341 new_folder);
4342 if (r != MAILIMAP_NO_ERROR) {
4343 imap_handle_error(SESSION(session), NULL, r);
4344 return r;
4347 return MAILIMAP_NO_ERROR;
4350 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
4352 int r;
4355 r = imap_threaded_delete(session->folder, folder);
4356 if (r != MAILIMAP_NO_ERROR) {
4357 imap_handle_error(SESSION(session), NULL, r);
4358 return r;
4361 return MAILIMAP_NO_ERROR;
4364 typedef struct _fetch_data {
4365 IMAPSession *session;
4366 guint32 uid;
4367 const gchar *filename;
4368 gboolean headers;
4369 gboolean body;
4370 gboolean done;
4371 } fetch_data;
4373 static void *imap_cmd_fetch_thread(void *data)
4375 fetch_data *stuff = (fetch_data *)data;
4376 IMAPSession *session = stuff->session;
4377 guint32 uid = stuff->uid;
4378 const gchar *filename = stuff->filename;
4379 int r;
4381 if (stuff->body) {
4382 r = imap_threaded_fetch_content(session->folder,
4383 uid, 1, filename);
4385 else {
4386 r = imap_threaded_fetch_content(session->folder,
4387 uid, 0, filename);
4389 if (r != MAILIMAP_NO_ERROR) {
4390 imap_handle_error(SESSION(session), NULL, r);
4391 debug_print("fetch err %d\n", r);
4392 return GINT_TO_POINTER(r);
4394 return GINT_TO_POINTER(MAILIMAP_NO_ERROR);
4397 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
4398 const gchar *filename, gboolean headers,
4399 gboolean body)
4401 fetch_data *data = g_new0(fetch_data, 1);
4402 int result = 0;
4403 data->done = FALSE;
4404 data->session = session;
4405 data->uid = uid;
4406 data->filename = filename;
4407 data->headers = headers;
4408 data->body = body;
4410 if (prefs_common.work_offline &&
4411 !inc_offline_should_override(FALSE,
4412 _("Claws Mail needs network access in order "
4413 "to access the IMAP server."))) {
4414 g_free(data);
4415 return -1;
4417 statusbar_print_all(_("Fetching message..."));
4418 result = GPOINTER_TO_INT(imap_cmd_fetch_thread(data));
4419 statusbar_pop_all();
4420 g_free(data);
4421 return result;
4425 static gint imap_cmd_append(IMAPSession *session,
4426 IMAPFolderItem *item,
4427 const gchar *destfolder,
4428 const gchar *file, IMAPFlags flags,
4429 guint32 *new_uid)
4431 struct mailimap_flag_list * flag_list;
4432 int r;
4434 cm_return_val_if_fail(file != NULL, MAILIMAP_ERROR_BAD_STATE);
4436 flag_list = imap_flag_to_lep(item, flags, NULL);
4437 lock_session(session);
4438 r = imap_threaded_append(session->folder, destfolder,
4439 file, flag_list, (int *)new_uid);
4440 mailimap_flag_list_free(flag_list);
4442 if (r != MAILIMAP_NO_ERROR) {
4443 imap_handle_error(SESSION(session), NULL, r);
4444 debug_print("append err %d\n", r);
4445 return r;
4448 unlock_session(session);
4450 return MAILIMAP_NO_ERROR;
4453 static gint imap_cmd_copy(IMAPSession *session, struct mailimap_set * set,
4454 const gchar *destfolder,
4455 struct mailimap_set **source, struct mailimap_set **dest)
4457 int r;
4459 g_return_val_if_fail(session != NULL, MAILIMAP_ERROR_BAD_STATE);
4460 g_return_val_if_fail(set != NULL, MAILIMAP_ERROR_BAD_STATE);
4461 g_return_val_if_fail(destfolder != NULL, MAILIMAP_ERROR_BAD_STATE);
4463 r = imap_threaded_copy(session->folder, set, destfolder, source, dest);
4464 if (r != MAILIMAP_NO_ERROR) {
4465 imap_handle_error(SESSION(session), NULL, r);
4466 return r;
4469 return MAILIMAP_NO_ERROR;
4472 static gint imap_cmd_store(IMAPSession *session,
4473 IMAPFolderItem *item,
4474 struct mailimap_set * set,
4475 IMAPFlags flags, GSList *tags, int do_add)
4477 int r;
4478 struct mailimap_flag_list * flag_list = NULL;
4479 struct mailimap_store_att_flags * store_att_flags;
4481 flag_list = imap_flag_to_lep(item, flags, tags);
4483 if (do_add)
4484 store_att_flags =
4485 mailimap_store_att_flags_new_add_flags_silent(flag_list);
4486 else
4487 store_att_flags =
4488 mailimap_store_att_flags_new_remove_flags_silent(flag_list);
4490 r = imap_threaded_store(session->folder, set, store_att_flags);
4491 mailimap_store_att_flags_free(store_att_flags);
4492 if (r != MAILIMAP_NO_ERROR) {
4493 imap_handle_error(SESSION(session), NULL, r);
4494 return r;
4497 return MAILIMAP_NO_ERROR;
4500 static gint imap_cmd_expunge(IMAPSession *session)
4502 int r;
4504 if (prefs_common.work_offline &&
4505 !inc_offline_should_override(FALSE,
4506 _("Claws Mail needs network access in order "
4507 "to access the IMAP server."))) {
4508 return -1;
4511 r = imap_threaded_expunge(session->folder);
4512 if (r != MAILIMAP_NO_ERROR) {
4513 imap_handle_error(SESSION(session), NULL, r);
4514 return r;
4517 return MAILIMAP_NO_ERROR;
4520 gint imap_expunge(Folder *folder, FolderItem *item)
4522 IMAPSession *session = imap_session_get(folder);
4523 if (session == NULL)
4524 return -1;
4526 return imap_cmd_expunge(session);
4529 static void imap_path_separator_subst(gchar *str, gchar separator)
4531 gchar *p;
4532 gboolean in_escape = FALSE;
4534 if (!separator || separator == '/') return;
4536 for (p = str; *p != '\0'; p++) {
4537 if (*p == '/' && !in_escape)
4538 *p = separator;
4539 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
4540 in_escape = TRUE;
4541 else if (*p == '-' && in_escape)
4542 in_escape = FALSE;
4546 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
4548 FolderItem *item = node->data;
4549 gchar **paths = data;
4550 const gchar *oldpath = paths[0];
4551 const gchar *newpath = paths[1];
4552 gchar *real_oldpath, *real_newpath;
4553 gchar *base;
4554 gchar *new_itempath;
4555 gint oldpathlen;
4556 IMAPSession *session = imap_session_get(item->folder);
4557 gint ok = MAILIMAP_NO_ERROR;
4558 oldpathlen = strlen(oldpath);
4559 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
4560 g_warning("path doesn't match: %s, %s", oldpath, item->path);
4561 return TRUE;
4564 base = item->path + oldpathlen;
4565 while (*base == '/') base++;
4566 if (*base == '\0')
4567 new_itempath = g_strdup(newpath);
4568 else
4569 new_itempath = g_strconcat(newpath, "/", base,
4570 NULL);
4572 real_oldpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path, &ok);
4573 g_free(item->path);
4574 item->path = new_itempath;
4576 real_newpath = imap_get_real_path(session, IMAP_FOLDER(item->folder), item->path, &ok);
4578 imap_threaded_subscribe(item->folder, real_oldpath, FALSE);
4579 imap_threaded_subscribe(item->folder, real_newpath, TRUE);
4581 g_free(real_oldpath);
4582 g_free(real_newpath);
4583 return FALSE;
4586 static gint get_list_of_uids(IMAPSession *session, Folder *folder, IMAPFolderItem *item, GSList **msgnum_list)
4588 GSList *uidlist, *elem;
4589 int r = -1;
4590 clist * lep_uidlist;
4591 gint ok, nummsgs = 0;
4593 if (session == NULL) {
4594 return -1;
4597 ok = imap_select(session, IMAP_FOLDER(folder), FOLDER_ITEM(item),
4598 NULL, NULL, NULL, NULL, NULL, TRUE);
4599 if (ok != MAILIMAP_NO_ERROR) {
4600 return -1;
4603 g_slist_free(item->uid_list);
4604 item->uid_list = NULL;
4606 uidlist = NULL;
4608 if (folder->account && folder->account->low_bandwidth) {
4609 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SIMPLE,
4610 NULL, NULL, NULL, &lep_uidlist);
4613 if (r == MAILIMAP_NO_ERROR) {
4614 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
4615 mailimap_search_result_free(lep_uidlist);
4616 } else {
4617 carray * lep_uidtab;
4618 if (r != -1) { /* inited */
4619 imap_handle_error(SESSION(session), NULL, r);
4620 if (is_fatal(r))
4621 return -1;
4623 r = imap_threaded_fetch_uid(folder, 1,
4624 &lep_uidtab);
4625 if (r == MAILIMAP_NO_ERROR) {
4626 uidlist = imap_uid_list_from_lep_tab(lep_uidtab);
4627 imap_fetch_uid_list_free(lep_uidtab);
4631 if (r != MAILIMAP_NO_ERROR) {
4632 imap_handle_error(SESSION(session), NULL, r);
4633 return -1;
4636 for (elem = uidlist; elem != NULL; elem = g_slist_next(elem)) {
4637 guint msgnum;
4639 msgnum = GPOINTER_TO_INT(elem->data);
4641 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
4642 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
4643 nummsgs++;
4645 g_slist_free(uidlist);
4647 return nummsgs;
4651 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list, gboolean *old_uids_valid)
4653 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4654 IMAPSession *session;
4655 gint nummsgs;
4656 GSList *uidlist = NULL;
4657 gchar *dir;
4658 gint known_list_len = 0;
4659 gchar *path;
4661 debug_print("get_num_list\n");
4663 g_return_val_if_fail(folder != NULL, -1);
4664 g_return_val_if_fail(item != NULL, -1);
4665 g_return_val_if_fail(item->item.path != NULL, -1);
4666 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
4667 g_return_val_if_fail(folder->account != NULL, -1);
4669 known_list_len = g_slist_length(item->uid_list);
4670 if (!item->should_update) {
4671 debug_print("get_num_list: nothing to update\n");
4672 *old_uids_valid = TRUE;
4673 if (known_list_len == item->item.total_msgs
4674 && known_list_len > 0) {
4675 *msgnum_list = g_slist_copy(item->uid_list);
4676 return known_list_len;
4677 } else {
4678 debug_print("don't know the list length...\n");
4682 if (prefs_common.work_offline &&
4683 !inc_offline_should_override(FALSE,
4684 _("Claws Mail needs network access in order "
4685 "to access the IMAP server."))) {
4686 return -1;
4689 path = folder_item_get_path(_item);
4690 if (!is_dir_exist(path)) {
4691 if(is_file_exist(path))
4692 claws_unlink(path);
4693 make_dir_hier(path);
4695 g_free(path);
4697 debug_print("getting session...\n");
4698 session = imap_session_get(folder);
4699 g_return_val_if_fail(session != NULL, -1);
4701 lock_session(session);
4702 if (FOLDER_ITEM(item)->path)
4703 statusbar_print_all(_("Scanning folder %s/%s..."),
4704 FOLDER_ITEM(item)->folder->name,
4705 FOLDER_ITEM(item)->path);
4706 else
4707 statusbar_print_all(_("Scanning folder %s..."),
4708 FOLDER_ITEM(item)->folder->name);
4710 if (item->should_trash_cache) {
4711 *old_uids_valid = FALSE;
4712 debug_print("get_num_list: trashing num list\n");
4713 debug_print("Freeing imap uid cache\n");
4714 item->lastuid = 0;
4715 g_slist_free(item->uid_list);
4716 item->uid_list = NULL;
4718 imap_delete_all_cached_messages((FolderItem *)item);
4719 } else {
4720 debug_print("get_num_list: updating num list\n");
4721 *old_uids_valid = TRUE;
4724 nummsgs = get_list_of_uids(session, folder, item, &uidlist);
4726 unlock_session(session);
4728 /* session could be broken now, in case of fatal error */
4730 debug_print("get_num_list: got %d msgs\n", nummsgs);
4732 if (nummsgs < 0) {
4733 statusbar_pop_all();
4734 return -1;
4737 *msgnum_list = uidlist;
4739 dir = folder_item_get_path((FolderItem *)item);
4740 debug_print("removing old messages from %s\n", dir);
4741 remove_numbered_files_not_in_list(dir, *msgnum_list);
4742 g_free(dir);
4744 debug_print("get_num_list - ok - %i\n", nummsgs);
4745 statusbar_pop_all();
4746 item->should_trash_cache = FALSE;
4747 item->should_update = FALSE;
4748 return nummsgs;
4751 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
4753 MsgInfo *msginfo;
4754 MsgFlags flags;
4756 flags.perm_flags = MSG_NEW|MSG_UNREAD;
4757 flags.tmp_flags = 0;
4759 g_return_val_if_fail(item != NULL, NULL);
4760 g_return_val_if_fail(file != NULL, NULL);
4762 if (folder_has_parent_of_type(item, F_QUEUE)) {
4763 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
4764 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
4765 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
4768 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
4769 if (!msginfo) return NULL;
4771 msginfo->plaintext_file = g_strdup(file);
4772 msginfo->folder = item;
4774 return msginfo;
4777 GSList *imap_get_msginfos(Folder *folder, FolderItem *item,
4778 GSList *msgnum_list)
4780 IMAPSession *session;
4781 MsgInfoList *ret = NULL;
4782 gint ok;
4784 debug_print("get_msginfos\n");
4786 g_return_val_if_fail(folder != NULL, NULL);
4787 g_return_val_if_fail(item != NULL, NULL);
4788 g_return_val_if_fail(msgnum_list != NULL, NULL);
4790 debug_print("getting session...\n");
4791 session = imap_session_get(folder);
4792 g_return_val_if_fail(session != NULL, NULL);
4794 lock_session(session); /* unlocked later in the function */
4796 debug_print("IMAP getting msginfos\n");
4797 ok = imap_select(session, IMAP_FOLDER(folder), item,
4798 NULL, NULL, NULL, NULL, NULL, FALSE);
4799 if (ok != MAILIMAP_NO_ERROR) {
4800 return NULL;
4802 if (!(folder_has_parent_of_type(item, F_DRAFT) ||
4803 folder_has_parent_of_type(item, F_QUEUE))) {
4804 ret = g_slist_concat(ret,
4805 imap_get_uncached_messages(session, item,
4806 msgnum_list, &ok));
4807 if (ok != MAILIMAP_NO_ERROR)
4808 return NULL;
4809 unlock_session(session);
4810 } else {
4811 MsgNumberList *sorted_list, *elem, *llast = NULL;
4812 gint startnum, lastnum;
4814 unlock_session(session);
4816 sorted_list = g_slist_sort(g_slist_copy(msgnum_list), g_int_compare);
4818 startnum = lastnum = GPOINTER_TO_INT(sorted_list->data);
4820 llast = g_slist_last(ret);
4821 for (elem = sorted_list;; elem = g_slist_next(elem)) {
4822 guint num = 0;
4824 if (elem)
4825 num = GPOINTER_TO_INT(elem->data);
4827 if (num > lastnum + 1 || elem == NULL) {
4828 int i;
4829 for (i = startnum; i <= lastnum; ++i) {
4830 gchar *file;
4831 file = imap_fetch_msg(folder, item, i);
4832 if (file != NULL) {
4833 MsgInfo *msginfo = imap_parse_msg(file, item);
4834 if (msginfo != NULL) {
4835 msginfo->msgnum = i;
4836 if (llast == NULL)
4837 llast = ret = g_slist_append(ret, msginfo);
4838 else {
4839 llast = g_slist_append(llast, msginfo);
4840 llast = llast->next;
4843 g_free(file);
4847 if (elem == NULL)
4848 break;
4850 startnum = num;
4852 lastnum = num;
4855 g_slist_free(sorted_list);
4857 return ret;
4860 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
4862 MsgInfo *msginfo = NULL;
4863 MsgInfoList *msginfolist;
4864 MsgNumberList numlist;
4866 numlist.next = NULL;
4867 numlist.data = GINT_TO_POINTER(uid);
4869 msginfolist = imap_get_msginfos(folder, item, &numlist);
4870 if (msginfolist != NULL) {
4871 msginfo = msginfolist->data;
4872 g_slist_free(msginfolist);
4875 return msginfo;
4878 gboolean imap_scan_required(Folder *folder, FolderItem *_item)
4880 IMAPSession *session;
4881 IMAPFolderItem *item = (IMAPFolderItem *)_item;
4882 gint ok, exists = 0, unseen = 0;
4883 guint32 uid_next = 0, uid_val = 0;
4884 gboolean selected_folder;
4886 g_return_val_if_fail(folder != NULL, FALSE);
4887 g_return_val_if_fail(item != NULL, FALSE);
4888 g_return_val_if_fail(item->item.folder != NULL, FALSE);
4889 g_return_val_if_fail(FOLDER_CLASS(item->item.folder) == &imap_class, FALSE);
4891 if (item->item.path == NULL)
4892 return FALSE;
4894 if (item->should_update) {
4895 debug_print("scan already required\n");
4896 return TRUE;
4898 debug_print("getting session...\n");
4899 session = imap_session_get(folder);
4901 g_return_val_if_fail(session != NULL, FALSE);
4902 lock_session(session); /* unlocked later in the function */
4904 selected_folder = (session->mbox != NULL) &&
4905 (!strcmp(session->mbox, item->item.path));
4906 if (selected_folder) {
4907 if (!session->folder_content_changed) {
4908 ok = imap_cmd_noop(session);
4909 if (ok != MAILIMAP_NO_ERROR) {
4910 debug_print("disconnected!\n");
4911 if (!is_fatal(ok))
4912 session = imap_reconnect_if_possible(folder, session);
4913 else
4914 session = imap_session_get(folder);
4915 if (session == NULL)
4916 return FALSE;
4919 if (session->folder_content_changed) {
4920 debug_print("CHANGED (self-noop)! scan_required\n");
4921 item->should_update = TRUE;
4922 if (session->uid_validity && session->uid_validity != item->item.mtime) {
4923 item->item.mtime = session->uid_validity;
4924 item->should_trash_cache = TRUE;
4926 unlock_session(session);
4927 return TRUE;
4929 } else {
4930 debug_print("CHANGED (previous noop)! scan_required\n");
4931 item->should_update = TRUE;
4932 if (session->uid_validity && session->uid_validity != item->item.mtime) {
4933 item->item.mtime = session->uid_validity;
4934 item->should_trash_cache = TRUE;
4936 unlock_session(session);
4937 return TRUE;
4939 } else {
4940 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path, IMAP_FOLDER_ITEM(item),
4941 &exists, &uid_next, &uid_val, &unseen, FALSE);
4942 if (ok != MAILIMAP_NO_ERROR) {
4943 return FALSE;
4946 debug_print("exists %d, item->item.total_msgs %d\n"
4947 "\tunseen %d, item->item.unread_msgs %d\n"
4948 "\tuid_next %d, item->uid_next %d\n"
4949 "\tuid_val %d, item->item.mtime %ld\n",
4950 exists, item->item.total_msgs, unseen, item->item.unread_msgs,
4951 uid_next, item->uid_next, uid_val, (long)(item->item.mtime));
4952 if (exists != item->item.total_msgs
4953 || unseen != item->item.unread_msgs
4954 || uid_next != item->uid_next
4955 || uid_val != item->item.mtime) {
4956 debug_print("CHANGED (status)! scan_required\n");
4957 item->last_change = time(NULL);
4958 item->should_update = TRUE;
4959 item->uid_next = uid_next;
4960 if (uid_val != item->item.mtime) {
4961 item->item.mtime = uid_val;
4962 item->should_trash_cache = TRUE;
4964 unlock_session(session);
4965 return TRUE;
4968 unlock_session(session);
4970 item->should_update = FALSE;
4971 return FALSE;
4974 void imap_change_flags(Folder *folder, FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
4976 IMAPSession *session;
4977 IMAPFlags flags_set = 0, flags_unset = 0;
4978 gint ok = MAILIMAP_NO_ERROR;
4979 MsgNumberList numlist;
4980 hashtable_data *ht_data = NULL;
4982 g_return_if_fail(folder != NULL);
4983 g_return_if_fail(folder->klass == &imap_class);
4984 g_return_if_fail(item != NULL);
4985 g_return_if_fail(item->folder == folder);
4986 g_return_if_fail(msginfo != NULL);
4987 g_return_if_fail(msginfo->folder == item);
4989 if (!MSG_IS_MARKED(msginfo->flags) && (newflags & MSG_MARKED))
4990 flags_set |= IMAP_FLAG_FLAGGED;
4991 if ( MSG_IS_MARKED(msginfo->flags) && !(newflags & MSG_MARKED))
4992 flags_unset |= IMAP_FLAG_FLAGGED;
4994 if (!MSG_IS_UNREAD(msginfo->flags) && (newflags & MSG_UNREAD))
4995 flags_unset |= IMAP_FLAG_SEEN;
4996 if ( MSG_IS_UNREAD(msginfo->flags) && !(newflags & MSG_UNREAD))
4997 flags_set |= IMAP_FLAG_SEEN;
4999 if (!MSG_IS_REPLIED(msginfo->flags) && (newflags & MSG_REPLIED))
5000 flags_set |= IMAP_FLAG_ANSWERED;
5001 if ( MSG_IS_REPLIED(msginfo->flags) && !(newflags & MSG_REPLIED))
5002 flags_unset |= IMAP_FLAG_ANSWERED;
5004 if (!MSG_IS_FORWARDED(msginfo->flags) && (newflags & MSG_FORWARDED))
5005 flags_set |= IMAP_FLAG_FORWARDED;
5006 if ( MSG_IS_FORWARDED(msginfo->flags) && !(newflags & MSG_FORWARDED))
5007 flags_unset |= IMAP_FLAG_FORWARDED;
5009 if (!MSG_IS_SPAM(msginfo->flags) && (newflags & MSG_SPAM)) {
5010 flags_set |= IMAP_FLAG_SPAM;
5011 flags_unset |= IMAP_FLAG_HAM;
5013 if ( MSG_IS_SPAM(msginfo->flags) && !(newflags & MSG_SPAM)) {
5014 flags_set |= IMAP_FLAG_HAM;
5015 flags_unset |= IMAP_FLAG_SPAM;
5017 if (!MSG_IS_DELETED(msginfo->flags) && (newflags & MSG_DELETED))
5018 flags_set |= IMAP_FLAG_DELETED;
5019 if ( MSG_IS_DELETED(msginfo->flags) && !(newflags & MSG_DELETED))
5020 flags_unset |= IMAP_FLAG_DELETED;
5022 if (!flags_set && !flags_unset) {
5023 /* the changed flags were not translatable to IMAP-speak.
5024 * like MSG_POSTFILTERED, so just apply. */
5025 msginfo->flags.perm_flags = newflags;
5026 return;
5029 debug_print("getting session...\n");
5030 session = imap_session_get(folder);
5031 if (!session) {
5032 return;
5035 if ((ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder,
5036 NULL, NULL, NULL, NULL, NULL, FALSE)) != MAILIMAP_NO_ERROR) {
5037 return;
5039 numlist.next = NULL;
5040 numlist.data = GINT_TO_POINTER(msginfo->msgnum);
5042 if (IMAP_FOLDER_ITEM(item)->batching) {
5043 /* instead of performing an UID STORE command for each message change,
5044 * as a lot of them can change "together", we just fill in hashtables
5045 * and defer the treatment so that we're able to send only one
5046 * command.
5048 debug_print("IMAP batch mode on, deferring flags change\n");
5049 if (flags_set) {
5050 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_set_table,
5051 GINT_TO_POINTER(flags_set));
5052 if (ht_data == NULL) {
5053 ht_data = g_new0(hashtable_data, 1);
5054 ht_data->item = IMAP_FOLDER_ITEM(item);
5055 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_set_table,
5056 GINT_TO_POINTER(flags_set), ht_data);
5058 ht_data->msglist = g_slist_prepend(ht_data->msglist, GINT_TO_POINTER(msginfo->msgnum));
5060 if (flags_unset) {
5061 ht_data = g_hash_table_lookup(IMAP_FOLDER_ITEM(item)->flags_unset_table,
5062 GINT_TO_POINTER(flags_unset));
5063 if (ht_data == NULL) {
5064 ht_data = g_new0(hashtable_data, 1);
5065 ht_data->item = IMAP_FOLDER_ITEM(item);
5066 g_hash_table_insert(IMAP_FOLDER_ITEM(item)->flags_unset_table,
5067 GINT_TO_POINTER(flags_unset), ht_data);
5069 ht_data->msglist = g_slist_prepend(ht_data->msglist,
5070 GINT_TO_POINTER(msginfo->msgnum));
5072 } else {
5073 debug_print("IMAP changing flags\n");
5074 if (flags_set) {
5075 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item), &numlist, flags_set, NULL, TRUE);
5076 if (ok != MAILIMAP_NO_ERROR) {
5077 return;
5081 if (flags_unset) {
5082 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item), &numlist, flags_unset, NULL, FALSE);
5083 if (ok != MAILIMAP_NO_ERROR) {
5084 return;
5088 msginfo->flags.perm_flags = newflags;
5089 return;
5092 static gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
5094 gint ok;
5095 IMAPSession *session;
5096 gchar *dir;
5097 MsgNumberList numlist;
5099 g_return_val_if_fail(folder != NULL, -1);
5100 g_return_val_if_fail(FOLDER_CLASS(folder) == &imap_class, -1);
5101 g_return_val_if_fail(item != NULL, -1);
5103 debug_print("getting session...\n");
5104 session = imap_session_get(folder);
5105 if (!session) return -1;
5107 ok = imap_select(session, IMAP_FOLDER(folder), item,
5108 NULL, NULL, NULL, NULL, NULL, FALSE);
5109 if (ok != MAILIMAP_NO_ERROR) {
5110 return ok;
5112 numlist.next = NULL;
5113 numlist.data = GINT_TO_POINTER(uid);
5115 ok = imap_set_message_flags
5116 (session, IMAP_FOLDER_ITEM(item), &numlist, IMAP_FLAG_DELETED, NULL, TRUE);
5117 if (ok != MAILIMAP_NO_ERROR) {
5118 log_warning(LOG_PROTOCOL, _("can't set deleted flags: %d\n"), uid);
5119 return ok;
5122 ok = imap_cmd_expunge(session);
5124 if (ok != MAILIMAP_NO_ERROR) {
5125 log_warning(LOG_PROTOCOL, _("can't expunge\n"));
5126 return ok;
5129 IMAP_FOLDER_ITEM(item)->uid_list = g_slist_remove(
5130 IMAP_FOLDER_ITEM(item)->uid_list, numlist.data);
5131 dir = folder_item_get_path(item);
5132 if (is_dir_exist(dir))
5133 remove_numbered_files(dir, uid, uid);
5134 g_free(dir);
5135 return MAILIMAP_NO_ERROR;
5138 static gint compare_msginfo(gconstpointer a, gconstpointer b)
5140 return ((MsgInfo *)a)->msgnum - ((MsgInfo *)b)->msgnum;
5143 static guint gslist_find_next_num(MsgNumberList **list, guint num)
5145 GSList *elem;
5147 g_return_val_if_fail(list != NULL, -1);
5149 for (elem = *list; elem != NULL; elem = g_slist_next(elem))
5150 if (GPOINTER_TO_INT(elem->data) >= num)
5151 break;
5152 *list = elem;
5153 return elem != NULL ? GPOINTER_TO_INT(elem->data) : (gint)-1;
5156 static gboolean flag_ok(IMAPFolderItem *item, guint flag)
5158 if (item->ok_flags && g_slist_find(item->ok_flags, GUINT_TO_POINTER(flag))) {
5159 debug_print("flag %d is OK\n", flag);
5160 return TRUE;
5162 if (item->can_create_flags == ITEM_CAN_CREATE_FLAGS) {
5163 debug_print("creating flags is OK\n");
5164 return TRUE;
5166 return FALSE;
5170 * NEW and DELETED flags are not syncronized
5171 * - The NEW/RECENT flags in IMAP folders can not really be directly
5172 * modified by Claws Mail
5173 * - The DELETE/DELETED flag in IMAP and Claws Mail do not have the same
5174 * meaning, in IMAP it always removes the messages from the FolderItem
5175 * in Claws Mail it can mean to move the message to Trash
5178 typedef struct _get_flags_data {
5179 Folder *folder;
5180 FolderItem *item;
5181 MsgInfoList *msginfo_list;
5182 GHashTable *msgflags;
5183 gboolean full_search;
5184 gboolean done;
5185 } get_flags_data;
5187 static /*gint*/ void *imap_get_flags_thread(void *data)
5189 get_flags_data *stuff = (get_flags_data *)data;
5190 Folder *folder = stuff->folder;
5191 FolderItem *fitem = (FolderItem *) stuff->item;
5192 MsgInfoList *msginfo_list = stuff->msginfo_list;
5193 GHashTable *msgflags = stuff->msgflags;
5194 GSList *elem;
5195 carray * lep_uidtab;
5196 IMAPSession *session;
5197 gint ok;
5198 int r = MAILIMAP_NO_ERROR;
5199 GHashTable *flags_hash = NULL;
5200 GHashTable *tags_hash = NULL;
5201 gboolean full_search = stuff->full_search;
5202 GSList *sorted_list = NULL;
5203 GSList *unseen = NULL, *answered = NULL, *flagged = NULL, *deleted = NULL, *forwarded = NULL, *spam = NULL;
5204 GSList *seq_list, *cur;
5205 gboolean reverse_seen = FALSE;
5206 gboolean selected_folder;
5207 gint exists_cnt, unseen_cnt;
5208 gboolean got_alien_tags = FALSE;
5210 session = imap_session_get(folder);
5212 if (session == NULL) {
5213 stuff->done = TRUE;
5214 return GINT_TO_POINTER(-1);
5216 selected_folder = (session->mbox != NULL) &&
5217 (!strcmp(session->mbox, fitem->path));
5219 lock_session(session);
5220 if (!selected_folder) {
5221 ok = imap_select(session, IMAP_FOLDER(folder), fitem,
5222 &exists_cnt, NULL, &unseen_cnt, NULL, NULL, TRUE);
5223 if (ok != MAILIMAP_NO_ERROR) {
5224 stuff->done = TRUE;
5225 return GINT_TO_POINTER(-1);
5228 if (unseen_cnt > exists_cnt / 2)
5229 reverse_seen = TRUE;
5231 else {
5232 if (fitem->unread_msgs > fitem->total_msgs / 2)
5233 reverse_seen = TRUE;
5236 sorted_list = g_slist_sort(g_slist_copy(msginfo_list), compare_msginfo);
5237 if (!full_search) {
5238 seq_list = imap_get_lep_set_from_msglist(IMAP_FOLDER(folder), msginfo_list);
5239 } else {
5240 struct mailimap_set * set;
5241 set = mailimap_set_new_interval(1, 0);
5242 seq_list = g_slist_append(NULL, set);
5245 if (folder->account && folder->account->low_bandwidth) {
5246 for (cur = seq_list; cur != NULL; cur = g_slist_next(cur)) {
5247 struct mailimap_set * imapset;
5248 clist * lep_uidlist;
5249 int r;
5251 imapset = cur->data;
5252 if (reverse_seen) {
5253 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SEEN, NULL,
5254 NULL, full_search ? NULL:imapset, &lep_uidlist);
5256 else {
5257 r = imap_threaded_search(folder,
5258 IMAP_SEARCH_TYPE_UNSEEN, NULL,
5259 NULL, full_search ? NULL:imapset, &lep_uidlist);
5261 if (r == MAILIMAP_NO_ERROR) {
5262 GSList * uidlist;
5264 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
5265 mailimap_search_result_free(lep_uidlist);
5267 unseen = g_slist_concat(unseen, uidlist);
5268 } else {
5269 imap_handle_error(SESSION(session), NULL, r);
5270 goto bail;
5273 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FLAGGED, NULL,
5274 NULL, full_search ? NULL:imapset, &lep_uidlist);
5275 if (r == MAILIMAP_NO_ERROR) {
5276 GSList * uidlist;
5278 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
5279 mailimap_search_result_free(lep_uidlist);
5281 flagged = g_slist_concat(flagged, uidlist);
5282 } else {
5283 imap_handle_error(SESSION(session), NULL, r);
5284 goto bail;
5287 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
5288 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_ANSWERED, NULL,
5289 NULL, full_search ? NULL:imapset, &lep_uidlist);
5290 if (r == MAILIMAP_NO_ERROR) {
5291 GSList * uidlist;
5293 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
5294 mailimap_search_result_free(lep_uidlist);
5296 answered = g_slist_concat(answered, uidlist);
5297 } else {
5298 imap_handle_error(SESSION(session), NULL, r);
5299 goto bail;
5302 if (flag_ok(IMAP_FOLDER_ITEM(fitem), IMAP_FLAG_FORWARDED)) {
5303 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_FORWARDED, NULL,
5304 NULL, full_search ? NULL:imapset, &lep_uidlist);
5305 if (r == MAILIMAP_NO_ERROR) {
5306 GSList * uidlist;
5308 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
5309 mailimap_search_result_free(lep_uidlist);
5311 forwarded = g_slist_concat(forwarded, uidlist);
5312 } else {
5313 imap_handle_error(SESSION(session), NULL, r);
5314 goto bail;
5318 if (flag_ok(IMAP_FOLDER_ITEM(fitem), IMAP_FLAG_SPAM)) {
5319 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_SPAM, NULL,
5320 NULL, full_search ? NULL:imapset, &lep_uidlist);
5321 if (r == MAILIMAP_NO_ERROR) {
5322 GSList * uidlist;
5324 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
5325 mailimap_search_result_free(lep_uidlist);
5327 spam = g_slist_concat(spam, uidlist);
5328 } else {
5329 imap_handle_error(SESSION(session), NULL, r);
5330 goto bail;
5334 r = imap_threaded_search(folder, IMAP_SEARCH_TYPE_DELETED, NULL,
5335 NULL, full_search ? NULL:imapset, &lep_uidlist);
5336 if (r == MAILIMAP_NO_ERROR) {
5337 GSList * uidlist;
5339 uidlist = imap_uid_list_from_lep(lep_uidlist, NULL);
5340 mailimap_search_result_free(lep_uidlist);
5342 deleted = g_slist_concat(deleted, uidlist);
5343 } else {
5344 imap_handle_error(SESSION(session), NULL, r);
5345 goto bail;
5350 } else {
5351 r = imap_threaded_fetch_uid_flags(folder, 1, &lep_uidtab);
5352 if (r == MAILIMAP_NO_ERROR) {
5353 flags_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
5354 tags_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, NULL);
5355 imap_flags_hash_from_lep_uid_flags_tab(lep_uidtab, flags_hash, tags_hash);
5356 imap_fetch_uid_flags_list_free(lep_uidtab);
5357 } else {
5358 imap_handle_error(SESSION(session), NULL, r);
5359 goto bail;
5363 bail:
5364 if (r == MAILIMAP_NO_ERROR)
5365 unlock_session(session);
5367 for (elem = sorted_list; elem != NULL; elem = g_slist_next(elem)) {
5368 MsgInfo *msginfo;
5369 MsgPermFlags flags, oldflags;
5370 gboolean wasnew;
5372 msginfo = (MsgInfo *) elem->data;
5373 flags = msginfo->flags.perm_flags;
5374 wasnew = (flags & MSG_NEW);
5375 oldflags = flags & ~(MSG_NEW|MSG_UNREAD|MSG_REPLIED|MSG_FORWARDED|MSG_MARKED|MSG_DELETED|MSG_SPAM);
5377 if (folder->account && folder->account->low_bandwidth) {
5378 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
5379 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW) | MSG_REPLIED | MSG_FORWARDED | MSG_MARKED | MSG_SPAM);
5380 } else {
5381 flags &= ~((reverse_seen ? 0 : MSG_UNREAD | MSG_NEW | MSG_MARKED));
5383 if (reverse_seen)
5384 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
5385 if (gslist_find_next_num(&unseen, msginfo->msgnum) == msginfo->msgnum) {
5386 if (!reverse_seen) {
5387 flags |= MSG_UNREAD | (wasnew ? MSG_NEW : 0);
5388 } else {
5389 flags &= ~(MSG_UNREAD | MSG_NEW);
5393 if (gslist_find_next_num(&flagged, msginfo->msgnum) == msginfo->msgnum)
5394 flags |= MSG_MARKED;
5395 else
5396 flags &= ~MSG_MARKED;
5398 if (fitem->opened || fitem->processing_pending || fitem == folder->inbox) {
5399 if (gslist_find_next_num(&answered, msginfo->msgnum) == msginfo->msgnum)
5400 flags |= MSG_REPLIED;
5401 else
5402 flags &= ~MSG_REPLIED;
5403 if (gslist_find_next_num(&forwarded, msginfo->msgnum) == msginfo->msgnum)
5404 flags |= MSG_FORWARDED;
5405 else
5406 flags &= ~MSG_FORWARDED;
5407 if (gslist_find_next_num(&spam, msginfo->msgnum) == msginfo->msgnum)
5408 flags |= MSG_SPAM;
5409 else
5410 flags &= ~MSG_SPAM;
5411 if (gslist_find_next_num(&deleted, msginfo->msgnum) == msginfo->msgnum)
5412 flags |= MSG_DELETED;
5413 else
5414 flags &= ~MSG_DELETED;
5416 } else {
5417 if (flags_hash != NULL) {
5419 flags = GPOINTER_TO_INT(g_hash_table_lookup(flags_hash,
5420 GINT_TO_POINTER(msginfo->msgnum)));
5423 if ((flags & MSG_UNREAD) == 0)
5424 flags &= ~MSG_NEW;
5425 else if (wasnew)
5426 flags |= MSG_NEW;
5427 flags |= oldflags;
5429 if (tags_hash != NULL) {
5430 GSList *tags = g_hash_table_lookup(tags_hash, GINT_TO_POINTER(msginfo->msgnum));
5431 GSList *cur;
5433 g_slist_free(msginfo->tags);
5434 msginfo->tags = NULL;
5436 for (cur = tags; cur; cur = cur->next) {
5437 gchar *real_tag = imap_modified_utf7_to_utf8(cur->data, TRUE);
5438 gint id = 0;
5439 id = tags_get_id_for_str(real_tag);
5440 if (id == -1) {
5441 id = tags_add_tag(real_tag);
5442 got_alien_tags = TRUE;
5444 msginfo->tags = g_slist_append(
5445 msginfo->tags,
5446 GINT_TO_POINTER(id));
5447 g_free(real_tag);
5449 slist_free_strings_full(tags);
5453 g_hash_table_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
5456 if (got_alien_tags) {
5457 tags_write_tags();
5458 main_window_reflect_tags_changes(mainwindow_get_mainwindow());
5461 if (flags_hash)
5462 g_hash_table_destroy(flags_hash);
5463 if (tags_hash)
5464 g_hash_table_destroy(tags_hash);
5466 imap_lep_set_free(seq_list);
5467 g_slist_free(flagged);
5468 g_slist_free(deleted);
5469 g_slist_free(answered);
5470 g_slist_free(forwarded);
5471 g_slist_free(spam);
5472 g_slist_free(unseen);
5473 g_slist_free(sorted_list);
5475 stuff->done = TRUE;
5476 return GINT_TO_POINTER(0);
5479 static gint imap_get_flags(Folder *folder, FolderItem *item,
5480 MsgInfoList *msginfo_list, GHashTable *msgflags)
5482 gint result;
5483 get_flags_data *data = g_new0(get_flags_data, 1);
5484 data->done = FALSE;
5485 data->folder = folder;
5486 data->item = item;
5487 data->msginfo_list = msginfo_list;
5488 data->msgflags = msgflags;
5489 data->full_search = FALSE;
5491 GSList *tmp = NULL, *cur;
5493 if (prefs_common.work_offline &&
5494 !inc_offline_should_override(FALSE,
5495 _("Claws Mail needs network access in order "
5496 "to access the IMAP server."))) {
5497 g_free(data);
5498 return -1;
5501 tmp = folder_item_get_msg_list(item);
5503 if (g_slist_length(tmp) <= g_slist_length(msginfo_list))
5504 data->full_search = TRUE;
5506 for (cur = tmp; cur; cur = cur->next)
5507 procmsg_msginfo_free((MsgInfo **)&(cur->data));
5509 g_slist_free(tmp);
5511 result = GPOINTER_TO_INT(imap_get_flags_thread(data));
5513 g_free(data);
5514 return result;
5518 static gboolean process_flags(gpointer key, gpointer value, gpointer user_data)
5520 gboolean flags_set = GPOINTER_TO_INT(user_data);
5521 gint flags_value = GPOINTER_TO_INT(key);
5522 hashtable_data *data = (hashtable_data *)value;
5523 IMAPFolderItem *_item = data->item;
5524 FolderItem *item = (FolderItem *)_item;
5525 gint ok = MAILIMAP_ERROR_BAD_STATE;
5526 IMAPSession *session = NULL;
5528 debug_print("getting session...\n");
5529 session = imap_session_get(item->folder);
5531 data->msglist = g_slist_reverse(data->msglist);
5533 debug_print("IMAP %ssetting flags to %d for %d messages\n",
5534 flags_set?"":"un",
5535 flags_value,
5536 g_slist_length(data->msglist));
5538 lock_session(session);
5539 if (session) {
5540 ok = imap_select(session, IMAP_FOLDER(item->folder), item,
5541 NULL, NULL, NULL, NULL, NULL, FALSE);
5543 if (ok == MAILIMAP_NO_ERROR) {
5544 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item),
5545 data->msglist, flags_value, NULL, flags_set);
5546 } else {
5547 g_warning("can't select mailbox %s", item->path);
5550 if (!is_fatal(ok))
5551 unlock_session(session);
5553 g_slist_free(data->msglist);
5554 g_free(data);
5555 return TRUE;
5558 static gboolean process_tags(gpointer key, gpointer value, gpointer user_data)
5560 gboolean tags_set = GPOINTER_TO_INT(user_data);
5561 TagsData *data = (TagsData *)value;
5562 IMAPFolderItem *_item = data->item;
5563 FolderItem *item = (FolderItem *)_item;
5564 gchar *str = data->str;
5565 gint ok = MAILIMAP_ERROR_BAD_STATE;
5566 IMAPSession *session = NULL;
5568 debug_print("getting session...\n");
5569 session = imap_session_get(item->folder);
5571 data->msglist = g_slist_reverse(data->msglist);
5573 debug_print("IMAP %ssetting tags %s for %d messages\n",
5574 tags_set?"":"un",
5575 str,
5576 g_slist_length(data->msglist));
5578 lock_session(session);
5579 if (session) {
5580 ok = imap_select(session, IMAP_FOLDER(item->folder), item,
5581 NULL, NULL, NULL, NULL, NULL, FALSE);
5583 if (ok == MAILIMAP_NO_ERROR) {
5584 GSList list;
5585 list.data = str;
5586 list.next = NULL;
5587 ok = imap_set_message_flags(session, IMAP_FOLDER_ITEM(item),
5588 data->msglist, 0, &list, tags_set);
5589 } else {
5590 g_warning("can't select mailbox %s", item->path);
5593 if (!is_fatal(ok))
5594 unlock_session(session);
5596 g_slist_free(data->msglist);
5597 g_free(data->str);
5598 g_free(data);
5599 return TRUE;
5602 static void process_hashtable(IMAPFolderItem *item)
5604 if (item->flags_set_table) {
5605 g_hash_table_foreach_remove(item->flags_set_table, process_flags, GINT_TO_POINTER(TRUE));
5606 g_hash_table_destroy(item->flags_set_table);
5607 item->flags_set_table = NULL;
5609 if (item->flags_unset_table) {
5610 g_hash_table_foreach_remove(item->flags_unset_table, process_flags, GINT_TO_POINTER(FALSE));
5611 g_hash_table_destroy(item->flags_unset_table);
5612 item->flags_unset_table = NULL;
5614 if (item->tags_set_table) {
5615 g_hash_table_foreach_remove(item->tags_set_table, process_tags, GINT_TO_POINTER(TRUE));
5616 g_hash_table_destroy(item->tags_set_table);
5617 item->tags_set_table = NULL;
5619 if (item->tags_unset_table) {
5620 g_hash_table_foreach_remove(item->tags_unset_table, process_tags, GINT_TO_POINTER(FALSE));
5621 g_hash_table_destroy(item->tags_unset_table);
5622 item->tags_unset_table = NULL;
5627 static void imap_set_batch (Folder *folder, FolderItem *_item, gboolean batch)
5629 IMAPFolderItem *item = (IMAPFolderItem *)_item;
5630 IMAPSession *session;
5632 g_return_if_fail(item != NULL);
5634 if (item->batching == batch)
5635 return;
5637 if (batch) {
5638 item->batching = TRUE;
5639 debug_print("IMAP switching to batch mode\n");
5640 if (!item->flags_set_table) {
5641 item->flags_set_table = g_hash_table_new(NULL, g_direct_equal);
5643 if (!item->flags_unset_table) {
5644 item->flags_unset_table = g_hash_table_new(NULL, g_direct_equal);
5646 if (!item->tags_set_table) {
5647 item->tags_set_table = g_hash_table_new(NULL, g_direct_equal);
5649 if (!item->tags_unset_table) {
5650 item->tags_unset_table = g_hash_table_new(NULL, g_direct_equal);
5652 session = imap_session_get(folder);
5653 if (session) {
5654 imap_refresh_sensitivity(session);
5655 session->sens_update_block = TRUE;
5657 } else {
5658 debug_print("IMAP switching away from batch mode\n");
5659 /* process stuff */
5660 process_hashtable(item);
5661 item->batching = FALSE;
5662 session = imap_session_get(folder);
5663 if (session) {
5664 session->sens_update_block = FALSE;
5665 imap_refresh_sensitivity(session);
5672 /* data types conversion libetpan <-> claws */
5676 #define ETPAN_IMAP_MB_MARKED 1
5677 #define ETPAN_IMAP_MB_UNMARKED 2
5678 #define ETPAN_IMAP_MB_NOSELECT 4
5679 #define ETPAN_IMAP_MB_NOINFERIORS 8
5681 static int imap_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags)
5683 int flags;
5684 clistiter * cur;
5686 flags = 0;
5687 if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) {
5688 switch (imap_flags->mbf_sflag) {
5689 case MAILIMAP_MBX_LIST_SFLAG_MARKED:
5690 flags |= ETPAN_IMAP_MB_MARKED;
5691 break;
5692 case MAILIMAP_MBX_LIST_SFLAG_NOSELECT:
5693 flags |= ETPAN_IMAP_MB_NOSELECT;
5694 break;
5695 case MAILIMAP_MBX_LIST_SFLAG_UNMARKED:
5696 flags |= ETPAN_IMAP_MB_UNMARKED;
5697 break;
5701 if (imap_flags->mbf_oflags) {
5702 for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ;
5703 cur = clist_next(cur)) {
5704 struct mailimap_mbx_list_oflag * oflag;
5706 oflag = clist_content(cur);
5708 switch (oflag->of_type) {
5709 case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS:
5710 flags |= ETPAN_IMAP_MB_NOINFERIORS;
5711 break;
5715 return flags;
5718 static GSList * imap_list_from_lep(IMAPFolder * folder,
5719 clist * list, const gchar * real_path, gboolean all)
5721 clistiter * iter;
5722 GSList * item_list = NULL, *llast = NULL;
5724 if (list) {
5725 for(iter = clist_begin(list) ; iter != NULL ;
5726 iter = clist_next(iter)) {
5727 struct mailimap_mailbox_list * mb;
5728 int flags;
5729 char delimiter;
5730 char * name;
5731 char * dup_name;
5732 gchar * base;
5733 gchar * loc_name;
5734 gchar * loc_path;
5735 FolderItem *new_item;
5737 mb = clist_content(iter);
5739 if (mb == NULL)
5740 continue;
5742 flags = 0;
5743 if (mb->mb_flag != NULL)
5744 flags = imap_flags_to_flags(mb->mb_flag);
5746 delimiter = mb->mb_delimiter;
5747 name = mb->mb_name;
5749 dup_name = strdup(name);
5750 if (delimiter != '\0')
5751 subst_char(dup_name, delimiter, '/');
5753 base = g_path_get_basename(dup_name);
5754 if (base[0] == '.') {
5755 g_free(base);
5756 free(dup_name);
5757 continue;
5759 if (!all && path_cmp(name, real_path) == 0) {
5760 g_free(base);
5761 free(dup_name);
5762 continue;
5765 if (!all && dup_name[strlen(dup_name)-1] == '/') {
5766 g_free(base);
5767 free(dup_name);
5768 continue;
5771 loc_name = imap_modified_utf7_to_utf8(base, FALSE);
5772 loc_path = imap_modified_utf7_to_utf8(dup_name, FALSE);
5774 new_item = folder_item_new(FOLDER(folder), loc_name, loc_path);
5775 if ((flags & ETPAN_IMAP_MB_NOINFERIORS) != 0)
5776 new_item->no_sub = TRUE;
5777 if (strcasecmp(dup_name, "INBOX") != 0 &&
5778 ((flags & ETPAN_IMAP_MB_NOSELECT) != 0))
5779 new_item->no_select = TRUE;
5781 if (item_list == NULL)
5782 llast = item_list = g_slist_append(item_list, new_item);
5783 else {
5784 llast = g_slist_append(llast, new_item);
5785 llast = llast->next;
5787 debug_print("folder '%s' found.\n", loc_path);
5788 g_free(base);
5789 g_free(loc_path);
5790 g_free(loc_name);
5792 free(dup_name);
5795 return item_list;
5798 static GSList * imap_get_lep_set_from_numlist(IMAPFolder *folder, MsgNumberList *numlist)
5800 GSList *sorted_list, *cur;
5801 guint first, last, next;
5802 GSList *ret_list = NULL, *llast = NULL;
5803 struct mailimap_set * current_set;
5804 unsigned int item_count;
5806 if (numlist == NULL)
5807 return NULL;
5809 current_set = mailimap_set_new_empty();
5811 sorted_list = g_slist_copy(numlist);
5812 sorted_list = g_slist_sort(sorted_list, g_int_compare);
5814 first = GPOINTER_TO_INT(sorted_list->data);
5816 item_count = 0;
5817 for (cur = sorted_list; cur != NULL; cur = g_slist_next(cur)) {
5818 if (GPOINTER_TO_INT(cur->data) == 0)
5819 continue;
5821 item_count ++;
5823 last = GPOINTER_TO_INT(cur->data);
5824 if (cur->next)
5825 next = GPOINTER_TO_INT(cur->next->data);
5826 else
5827 next = 0;
5829 if (last + 1 != next || next == 0 || item_count >= folder->max_set_size) {
5831 struct mailimap_set_item * item;
5832 item = mailimap_set_item_new(first, last);
5833 mailimap_set_add(current_set, item);
5835 first = next;
5837 if (item_count >= folder->max_set_size) {
5838 if (ret_list == NULL)
5839 llast = ret_list = g_slist_append(ret_list,
5840 current_set);
5841 else {
5842 llast = g_slist_append(llast, current_set);
5843 llast = llast->next;
5846 current_set = mailimap_set_new_empty();
5847 item_count = 0;
5852 if (clist_count(current_set->set_list) > 0) {
5853 ret_list = g_slist_append(ret_list,
5854 current_set);
5857 g_slist_free(sorted_list);
5859 return ret_list;
5862 static GSList * imap_get_lep_set_from_msglist(IMAPFolder *folder, MsgInfoList *msglist)
5864 MsgNumberList *numlist = NULL;
5865 GSList *seq_list;
5867 numlist = procmsg_get_number_list_for_msgs(msglist);
5869 seq_list = imap_get_lep_set_from_numlist(folder, numlist);
5870 g_slist_free(numlist);
5872 return seq_list;
5875 static GSList * imap_uid_list_from_lep(clist * list, gint* length)
5877 clistiter * iter;
5878 GSList * result;
5879 gint len = 0;
5881 result = NULL;
5883 if (list) {
5884 for(iter = clist_begin(list) ; iter != NULL ;
5885 iter = clist_next(iter)) {
5886 uint32_t * puid;
5888 puid = clist_content(iter);
5889 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
5890 len++;
5892 result = g_slist_reverse(result);
5894 if (length)
5895 *length = len;
5896 return result;
5899 static GSList * imap_uid_list_from_lep_tab(carray * list)
5901 unsigned int i;
5902 GSList * result;
5904 result = NULL;
5906 for(i = 0 ; i < carray_count(list) ; i ++) {
5907 uint32_t * puid;
5909 puid = carray_get(list, i);
5910 result = g_slist_prepend(result, GINT_TO_POINTER(* puid));
5912 result = g_slist_reverse(result);
5913 return result;
5916 static void imap_flags_hash_from_lep_uid_flags_tab(carray * list,
5917 GHashTable * hash,
5918 GHashTable * tags_hash)
5920 unsigned int i;
5922 for(i = 0 ; i < carray_count(list) ; i += 3) {
5923 uint32_t * puid;
5924 int * pflags;
5925 GSList *tags;
5927 puid = carray_get(list, i);
5928 pflags = carray_get(list, i + 1);
5929 tags = carray_get(list, i + 2);
5931 g_hash_table_insert(hash, GINT_TO_POINTER(*puid), GINT_TO_POINTER(* pflags));
5932 g_hash_table_insert(tags_hash, GINT_TO_POINTER(*puid), tags);
5936 static MsgInfo *imap_envelope_from_lep(struct imap_fetch_env_info * info,
5937 FolderItem *item)
5939 MsgInfo *msginfo = NULL;
5940 guint32 uid = 0;
5941 goffset size = 0;
5942 MsgFlags flags = {0, 0};
5944 if (info->headers == NULL)
5945 return NULL;
5947 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
5948 if (folder_has_parent_of_type(item, F_QUEUE)) {
5949 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
5950 } else if (folder_has_parent_of_type(item, F_DRAFT)) {
5951 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
5953 flags.perm_flags = info->flags;
5955 uid = info->uid;
5956 size = (goffset) info->size;
5957 msginfo = procheader_parse_str(info->headers, flags, FALSE, FALSE);
5959 if (msginfo) {
5960 msginfo->msgnum = uid;
5961 msginfo->size = size;
5964 return msginfo;
5967 static void imap_lep_set_free(GSList *seq_list)
5969 GSList * cur;
5971 for(cur = seq_list ; cur != NULL ; cur = g_slist_next(cur)) {
5972 struct mailimap_set * imapset;
5974 imapset = cur->data;
5975 mailimap_set_free(imapset);
5977 g_slist_free(seq_list);
5980 static struct mailimap_flag_list * imap_flag_to_lep(IMAPFolderItem *item, IMAPFlags flags, GSList *tags)
5982 struct mailimap_flag_list * flag_list;
5983 GSList *cur = tags;
5985 flag_list = mailimap_flag_list_new_empty();
5987 if (IMAP_IS_SEEN(flags))
5988 mailimap_flag_list_add(flag_list,
5989 mailimap_flag_new_seen());
5990 if (IMAP_IS_ANSWERED(flags))
5991 mailimap_flag_list_add(flag_list,
5992 mailimap_flag_new_answered());
5993 if (IMAP_IS_FLAGGED(flags))
5994 mailimap_flag_list_add(flag_list,
5995 mailimap_flag_new_flagged());
5996 if (IMAP_IS_DELETED(flags))
5997 mailimap_flag_list_add(flag_list,
5998 mailimap_flag_new_deleted());
5999 if (IMAP_IS_DRAFT(flags))
6000 mailimap_flag_list_add(flag_list,
6001 mailimap_flag_new_draft());
6002 if (IMAP_IS_FORWARDED(flags) && flag_ok(item, IMAP_FLAG_FORWARDED))
6003 mailimap_flag_list_add(flag_list,
6004 mailimap_flag_new_flag_keyword(strdup(RTAG_FORWARDED)));
6005 if (IMAP_IS_SPAM(flags) && flag_ok(item, IMAP_FLAG_SPAM))
6006 mailimap_flag_list_add(flag_list,
6007 mailimap_flag_new_flag_keyword(strdup(RTAG_JUNK)));
6008 else if (IMAP_IS_HAM(flags) && flag_ok(item, IMAP_FLAG_HAM))
6009 mailimap_flag_list_add(flag_list,
6010 mailimap_flag_new_flag_keyword(strdup(RTAG_NON_JUNK)));
6012 for (; cur; cur = cur->next) {
6013 gchar *enc_str =
6014 imap_utf8_to_modified_utf7(cur->data, TRUE);
6015 g_strstrip(enc_str);
6017 mailimap_flag_list_add(flag_list,
6018 mailimap_flag_new_flag_keyword(enc_str));
6021 return flag_list;
6024 guint imap_folder_get_refcnt(Folder *folder)
6026 return ((IMAPFolder *)folder)->refcnt;
6029 void imap_folder_ref(Folder *folder)
6031 ((IMAPFolder *)folder)->refcnt++;
6034 void imap_disconnect_all(gboolean have_connectivity)
6036 GList *list;
6037 gboolean short_timeout;
6038 #ifdef HAVE_NETWORKMANAGER_SUPPORT
6039 GError *error = NULL;
6041 short_timeout = !networkmanager_is_online(&error);
6042 if(error) {
6043 short_timeout = TRUE;
6044 g_error_free(error);
6046 #else
6047 short_timeout = TRUE;
6048 #endif
6050 if(short_timeout)
6051 imap_main_set_timeout(1);
6053 for (list = account_get_list(); list != NULL; list = list->next) {
6054 PrefsAccount *account = list->data;
6055 if (account->protocol == A_IMAP4) {
6056 RemoteFolder *folder = (RemoteFolder *)account->folder;
6057 if (folder && folder->session) {
6058 if (imap_is_busy(FOLDER(folder)))
6059 imap_threaded_cancel(FOLDER(folder));
6061 IMAPSession *session = (IMAPSession *)folder->session;
6062 if (have_connectivity)
6063 imap_threaded_disconnect(FOLDER(folder));
6064 SESSION(session)->state = SESSION_DISCONNECTED;
6065 SESSION(session)->sock = NULL;
6066 imap_safe_destroy(session);
6067 folder->session = NULL;
6072 if(short_timeout)
6073 imap_main_set_timeout(prefs_common.io_timeout_secs);
6076 void imap_folder_unref(Folder *folder)
6078 if (((IMAPFolder *)folder)->refcnt > 0)
6079 ((IMAPFolder *)folder)->refcnt--;
6082 void imap_cancel_all(void)
6084 GList *folderlist;
6085 GList *cur;
6087 folderlist = folder_get_list();
6088 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
6089 Folder *folder = (Folder *) cur->data;
6091 if (folder->klass == &imap_class) {
6092 if (imap_is_busy(folder)) {
6093 IMAPSession *imap_session;
6094 RemoteFolder *rfolder;
6096 g_printerr("cancelled\n");
6097 imap_threaded_cancel(folder);
6098 rfolder = (RemoteFolder *) folder;
6099 imap_session = (IMAPSession *) rfolder->session;
6100 if (imap_session)
6101 imap_session->cancelled = 1;
6107 gboolean imap_cancel_all_enabled(void)
6109 GList *folderlist;
6110 GList *cur;
6112 folderlist = folder_get_list();
6113 for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
6114 Folder *folder = (Folder *) cur->data;
6116 if (folder->klass == &imap_class) {
6117 if (imap_is_busy(folder)) {
6118 return TRUE;
6123 return FALSE;
6126 static gboolean imap_is_busy(Folder *folder)
6128 IMAPSession *imap_session;
6129 RemoteFolder *rfolder;
6131 rfolder = (RemoteFolder *) folder;
6132 imap_session = (IMAPSession *) rfolder->session;
6133 if (imap_session == NULL)
6134 return FALSE;
6136 return imap_session->busy;
6139 #else /* HAVE_LIBETPAN */
6141 static FolderClass imap_class;
6143 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item);
6144 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag);
6146 static Folder *imap_folder_new (const gchar *name,
6147 const gchar *path)
6149 static gboolean missing_imap_warning = TRUE;
6150 if (missing_imap_warning) {
6151 missing_imap_warning = FALSE;
6152 alertpanel_error(
6153 _("You have one or more IMAP accounts "
6154 "defined. However this version of "
6155 "Claws Mail has been built without "
6156 "IMAP support; your IMAP accounts are "
6157 "disabled.\n\n"
6158 "You probably need to "
6159 "install libetpan and recompile "
6160 "Claws Mail."));
6162 return NULL;
6164 static gint imap_create_tree (Folder *folder)
6166 return -1;
6168 static FolderItem *imap_create_folder (Folder *folder,
6169 FolderItem *parent,
6170 const gchar *name)
6172 return NULL;
6174 static gint imap_rename_folder (Folder *folder,
6175 FolderItem *item,
6176 const gchar *name)
6178 return -1;
6181 gchar imap_get_path_separator_for_item(FolderItem *item)
6183 return '/';
6186 FolderClass *imap_get_class(void)
6188 if (imap_class.idstr == NULL) {
6189 imap_class.type = F_IMAP;
6190 imap_class.idstr = "imap";
6191 imap_class.uistr = "IMAP";
6193 imap_class.new_folder = imap_folder_new;
6194 imap_class.create_tree = imap_create_tree;
6195 imap_class.create_folder = imap_create_folder;
6196 imap_class.rename_folder = imap_rename_folder;
6198 imap_class.set_xml = folder_set_xml;
6199 imap_class.get_xml = folder_get_xml;
6200 imap_class.item_set_xml = imap_item_set_xml;
6201 imap_class.item_get_xml = imap_item_get_xml;
6202 /* nothing implemented */
6205 return &imap_class;
6208 void imap_disconnect_all(gboolean have_connectivity)
6212 gint imap_subscribe(Folder *folder, FolderItem *item, gchar *rpath, gboolean sub)
6214 return -1;
6217 GList * imap_scan_subtree(Folder *folder, FolderItem *item, gboolean unsubs_only, gboolean recursive)
6219 return NULL;
6222 void imap_cache_msg(FolderItem *item, gint msgnum)
6226 void imap_cancel_all(void)
6230 gboolean imap_cancel_all_enabled(void)
6232 return FALSE;
6235 #endif
6237 #ifdef HAVE_LIBETPAN
6238 static void imap_synchronise(FolderItem *item, gint days)
6240 if (IMAP_FOLDER_ITEM(item)->last_sync == IMAP_FOLDER_ITEM(item)->last_change) {
6241 debug_print("%s already synced\n", item->path?item->path:item->name);
6242 return;
6244 debug_print("syncing %s\n", item->path?item->path:item->name);
6245 imap_gtk_synchronise(item, days);
6246 IMAP_FOLDER_ITEM(item)->last_sync = IMAP_FOLDER_ITEM(item)->last_change;
6248 #endif
6250 static void imap_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
6252 #ifdef HAVE_LIBETPAN
6253 GList *cur;
6254 #endif
6255 folder_item_set_xml(folder, item, tag);
6257 #ifdef HAVE_LIBETPAN
6258 for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
6259 XMLAttr *attr = (XMLAttr *) cur->data;
6261 if (!attr || !attr->name || !attr->value) continue;
6262 if (!strcmp(attr->name, "uidnext"))
6263 IMAP_FOLDER_ITEM(item)->uid_next = atoi(attr->value);
6264 if (!strcmp(attr->name, "last_sync"))
6265 IMAP_FOLDER_ITEM(item)->last_sync = atoi(attr->value);
6266 if (!strcmp(attr->name, "last_change"))
6267 IMAP_FOLDER_ITEM(item)->last_change = atoi(attr->value);
6269 if (IMAP_FOLDER_ITEM(item)->last_change == 0)
6270 IMAP_FOLDER_ITEM(item)->last_change = time(NULL);
6271 #endif
6274 static XMLTag *imap_item_get_xml(Folder *folder, FolderItem *item)
6276 XMLTag *tag;
6278 tag = folder_item_get_xml(folder, item);
6280 #ifdef HAVE_LIBETPAN
6281 xml_tag_add_attr(tag, xml_attr_new_int("uidnext",
6282 IMAP_FOLDER_ITEM(item)->uid_next));
6283 xml_tag_add_attr(tag, xml_attr_new_int("last_sync",
6284 IMAP_FOLDER_ITEM(item)->last_sync));
6285 xml_tag_add_attr(tag, xml_attr_new_int("last_change",
6286 IMAP_FOLDER_ITEM(item)->last_change));
6288 #endif
6289 return tag;
6292 /* ===================================================================
6293 * UTF-7 conversion routines as in RFC 2192
6294 * ===================================================================
6295 * These two functions from:
6296 * libimap library.
6297 * Copyright (C) 2003-2004 Pawel Salek. */
6299 /* UTF7 modified base64 alphabet */
6300 static char base64chars[] =
6301 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
6302 #define UNDEFINED 64
6304 /* UTF16 definitions */
6305 #define UTF16MASK 0x03FFUL
6306 #define UTF16SHIFT 10
6307 #define UTF16BASE 0x10000UL
6308 #define UTF16HIGHSTART 0xD800UL
6309 #define UTF16HIGHEND 0xDBFFUL
6310 #define UTF16LOSTART 0xDC00UL
6311 #define UTF16LOEND 0xDFFFUL
6314 /* Convert an IMAP mailbox to a UTF-8 string.
6315 * dst needs to have roughly 4 times the storage space of src
6316 * Hex encoding can triple the size of the input
6317 * UTF-7 can be slightly denser than UTF-8
6318 * (worst case: 8 octets UTF-7 becomes 9 octets UTF-8)
6320 char* imap_modified_utf7_to_utf8(const char *mbox, gboolean change_spaces)
6322 unsigned c, i, bitcount;
6323 unsigned long ucs4, utf16, bitbuf;
6324 unsigned char base64[256];
6325 const char *src;
6326 char *dst, *res = g_malloc(2*strlen(mbox)+1);
6328 dst = res;
6329 src = mbox;
6330 if(!dst) return NULL;
6331 /* initialize modified base64 decoding table */
6332 memset(base64, UNDEFINED, sizeof (base64));
6333 for (i = 0; i < sizeof (base64chars); ++i) {
6334 base64[(unsigned)base64chars[i]] = i;
6337 /* loop until end of string */
6338 while (*src != '\0') {
6339 c = *src++;
6340 /* deal with literal characters and &- */
6341 if (c != '&' || *src == '-') {
6342 /* encode literally */
6343 if (change_spaces && c == '_')
6344 *dst++ = ' ';
6345 else
6346 *dst++ = c;
6347 /* skip over the '-' if this is an &- sequence */
6348 if (c == '&') ++src;
6349 } else {
6350 /* convert modified UTF-7 -> UTF-16 -> UCS-4 -> UTF-8 -> HEX */
6351 bitbuf = 0;
6352 bitcount = 0;
6353 ucs4 = 0;
6354 while ((c = base64[(unsigned char) *src]) != UNDEFINED) {
6355 ++src;
6356 bitbuf = (bitbuf << 6) | c;
6357 bitcount += 6;
6358 /* enough bits for a UTF-16 character? */
6359 if (bitcount >= 16) {
6360 bitcount -= 16;
6361 utf16 = (bitcount ? bitbuf >> bitcount
6362 : bitbuf) & 0xffff;
6363 /* convert UTF16 to UCS4 */
6365 (utf16 >= UTF16HIGHSTART && utf16 <= UTF16HIGHEND) {
6366 ucs4 = (utf16 - UTF16HIGHSTART) << UTF16SHIFT;
6367 continue;
6368 } else if
6369 (utf16 >= UTF16LOSTART && utf16 <= UTF16LOEND) {
6370 ucs4 += utf16 - UTF16LOSTART + UTF16BASE;
6371 } else {
6372 ucs4 = utf16;
6375 /* convert UTF-16 range of UCS4 to UTF-8 */
6376 if (ucs4 <= 0x7fUL) {
6377 dst[0] = ucs4;
6378 dst += 1;
6379 } else if (ucs4 <= 0x7ffUL) {
6380 dst[0] = 0xc0 | (ucs4 >> 6);
6381 dst[1] = 0x80 | (ucs4 & 0x3f);
6382 dst += 2;
6383 } else if (ucs4 <= 0xffffUL) {
6384 dst[0] = 0xe0 | (ucs4 >> 12);
6385 dst[1] = 0x80 | ((ucs4 >> 6) & 0x3f);
6386 dst[2] = 0x80 | (ucs4 & 0x3f);
6387 dst += 3;
6388 } else {
6389 dst[0] = 0xf0 | (ucs4 >> 18);
6390 dst[1] = 0x80 | ((ucs4 >> 12) & 0x3f);
6391 dst[2] = 0x80 | ((ucs4 >> 6) & 0x3f);
6392 dst[3] = 0x80 | (ucs4 & 0x3f);
6393 dst += 4;
6397 /* skip over trailing '-' in modified UTF-7 encoding */
6398 if (*src == '-') ++src;
6401 /* terminate destination string */
6402 *dst = '\0';
6403 return res;
6406 /* Convert hex coded UTF-8 string to modified UTF-7 IMAP mailbox
6407 * dst should be about twice the length of src to deal with non-hex
6408 * coded URLs
6410 char* imap_utf8_to_modified_utf7(const char *src, gboolean change_spaces)
6412 unsigned int utf8pos, utf8total, c, utf7mode, bitstogo, utf16flag;
6413 unsigned long ucs4 = 0, bitbuf = 0;
6415 /* initialize hex lookup table */
6416 char *dst, *res;
6418 if (!src) return NULL;
6420 res = malloc(2*strlen(src)+1);
6421 dst = res;
6422 if(!dst) return NULL;
6424 utf7mode = 0;
6425 utf8total = 0;
6426 bitstogo = 0;
6427 utf8pos = 0;
6428 while ((c = (unsigned char)*src) != '\0') {
6429 ++src;
6430 /* normal character? */
6431 if (c >= ' ' && c <= '~' && (c != '_' || !change_spaces)) {
6432 /* switch out of UTF-7 mode */
6433 if (utf7mode) {
6434 if (bitstogo) {
6435 *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
6437 *dst++ = '-';
6438 utf7mode = 0;
6439 utf8pos = 0;
6440 bitstogo = 0;
6441 utf8total= 0;
6443 if (change_spaces && c == ' ')
6444 *dst++ = '_';
6445 else
6446 *dst++ = c;
6447 /* encode '&' as '&-' */
6448 if (c == '&') {
6449 *dst++ = '-';
6451 continue;
6453 /* switch to UTF-7 mode */
6454 if (!utf7mode) {
6455 *dst++ = '&';
6456 utf7mode = 1;
6458 /* Encode US-ASCII characters as themselves */
6459 if (c < 0x80) {
6460 ucs4 = c;
6461 } else if (utf8total) {
6462 /* save UTF8 bits into UCS4 */
6463 ucs4 = (ucs4 << 6) | (c & 0x3FUL);
6464 if (++utf8pos < utf8total) {
6465 continue;
6467 } else {
6468 utf8pos = 1;
6469 if (c < 0xE0) {
6470 utf8total = 2;
6471 ucs4 = c & 0x1F;
6472 } else if (c < 0xF0) {
6473 utf8total = 3;
6474 ucs4 = c & 0x0F;
6475 } else {
6476 /* NOTE: can't convert UTF8 sequences longer than 4 */
6477 utf8total = 4;
6478 ucs4 = c & 0x03;
6480 continue;
6482 /* loop to split ucs4 into two utf16 chars if necessary */
6483 utf8total = 0;
6484 do {
6485 if (ucs4 >= UTF16BASE) {
6486 ucs4 -= UTF16BASE;
6487 bitbuf = (bitbuf << 16) | ((ucs4 >> UTF16SHIFT)
6488 + UTF16HIGHSTART);
6489 ucs4 = (ucs4 & UTF16MASK) + UTF16LOSTART;
6490 utf16flag = 1;
6491 } else {
6492 bitbuf = (bitbuf << 16) | ucs4;
6493 utf16flag = 0;
6495 bitstogo += 16;
6496 /* spew out base64 */
6497 while (bitstogo >= 6) {
6498 bitstogo -= 6;
6499 *dst++ = base64chars[(bitstogo ? (bitbuf >> bitstogo)
6500 : bitbuf)
6501 & 0x3F];
6503 } while (utf16flag);
6505 /* if in UTF-7 mode, finish in ASCII */
6506 if (utf7mode) {
6507 if (bitstogo) {
6508 *dst++ = base64chars[(bitbuf << (6 - bitstogo)) & 0x3F];
6510 *dst++ = '-';
6512 /* tie off string */
6513 *dst = '\0';
6514 return res;