talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

imap.c (147130B)


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