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