1 /*
2  * LibSylph -- E-Mail client library
3  * Copyright (C) 1999-2013 Hiroyuki Yamamoto
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.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 <stdlib.h>
31 #include <dirent.h>
32 #include <unistd.h>
33 #include <ctype.h>
34 #include <time.h>
35 #if HAVE_ICONV
36 #  include <iconv.h>
37 #endif
38 
39 #include "sylmain.h"
40 #include "imap.h"
41 #include "socket.h"
42 #include "socks.h"
43 #include "ssl.h"
44 #include "recv.h"
45 #include "procmsg.h"
46 #include "procheader.h"
47 #include "folder.h"
48 #include "prefs_account.h"
49 #include "codeconv.h"
50 #include "md5_hmac.h"
51 #include "base64.h"
52 #include "utils.h"
53 #include "prefs_common.h"
54 #include "virtual.h"
55 
56 #define IMAP4_PORT	143
57 #if USE_SSL
58 #define IMAPS_PORT	993
59 #endif
60 
61 #define IMAP_COPY_LIMIT	200
62 #define IMAP_CMD_LIMIT	1000
63 
64 #define QUOTE_IF_REQUIRED(out, str)					\
65 {									\
66 	if (!str || *str == '\0') {					\
67 		Xstrdup_a(out, "\"\"", return IMAP_ERROR);		\
68 	} else if (strpbrk(str, " \t(){}[]%&*\"\\") != NULL) {		\
69 		gchar *__tmp;						\
70 		gint len;						\
71 		const gchar *p;						\
72 		gchar *tp;						\
73 									\
74 		len = strlen(str) * 2 + 3;				\
75 		Xalloca(__tmp, len, return IMAP_ERROR);			\
76 		tp = __tmp;						\
77 		*tp++ = '\"';						\
78 		for (p = str; *p != '\0'; p++) {			\
79 			if (*p == '\"' || *p == '\\')			\
80 				*tp++ = '\\';				\
81 			*tp++ = *p;					\
82 		}							\
83 		*tp++ = '\"';						\
84 		*tp = '\0';						\
85 		out = __tmp;						\
86 	} else {							\
87 		Xstrdup_a(out, str, return IMAP_ERROR);			\
88 	}								\
89 }
90 
91 typedef gint (*IMAPThreadFunc)		(IMAPSession	*session,
92 					 gpointer	 data);
93 typedef gint (*IMAPProgressFunc)	(IMAPSession	*session,
94 					 gint		 count,
95 					 gint		 total,
96 					 gpointer	 data);
97 
98 typedef struct _IMAPRealSession
99 {
100 	IMAPSession imap_session;
101 #if USE_THREADS
102 	GThreadPool *pool;
103 	IMAPThreadFunc thread_func;
104 	gpointer thread_data;
105 	gboolean is_running;
106 	gint prog_count;
107 	gint prog_total;
108 	gint flag;
109 	gint retval;
110 #endif
111 } IMAPRealSession;
112 
113 static GList *session_list = NULL;
114 
115 static void imap_folder_init		(Folder		*folder,
116 					 const gchar	*name,
117 					 const gchar	*path);
118 
119 static Folder	*imap_folder_new	(const gchar	*name,
120 					 const gchar	*path);
121 static void	 imap_folder_destroy	(Folder		*folder);
122 
123 static Session *imap_session_new	(PrefsAccount	*account);
124 static gint imap_session_connect	(IMAPSession	*session);
125 static gint imap_session_reconnect	(IMAPSession	*session);
126 static void imap_session_destroy	(Session	*session);
127 /* static void imap_session_destroy_all	(void); */
128 
129 static gint imap_search_flags		(IMAPSession	*session,
130 					 GArray	       **uids,
131 					 GHashTable    **flags_table);
132 static gint imap_fetch_flags		(IMAPSession	*session,
133 					 GArray	       **uids,
134 					 GHashTable    **flags_table);
135 
136 static GSList *imap_get_msg_list	(Folder		*folder,
137 					 FolderItem	*item,
138 					 gboolean	 use_cache);
139 static GSList *imap_get_uncached_msg_list
140 					(Folder		*folder,
141 					 FolderItem	*item);
142 static gchar *imap_fetch_msg		(Folder		*folder,
143 					 FolderItem	*item,
144 					 gint		 uid);
145 static MsgInfo *imap_get_msginfo	(Folder		*folder,
146 					 FolderItem	*item,
147 					 gint		 uid);
148 static gint imap_add_msg		(Folder		*folder,
149 					 FolderItem	*dest,
150 					 const gchar	*file,
151 					 MsgFlags	*flags,
152 					 gboolean	 remove_source);
153 static gint imap_add_msgs		(Folder		*folder,
154 					 FolderItem	*dest,
155 					 GSList		*file_list,
156 					 gboolean	 remove_source,
157 					 gint		*first);
158 static gint imap_add_msg_msginfo	(Folder		*folder,
159 					 FolderItem	*dest,
160 					 MsgInfo	*msginfo,
161 					 gboolean	 remove_source);
162 static gint imap_add_msgs_msginfo	(Folder		*folder,
163 					 FolderItem	*dest,
164 					 GSList		*msglist,
165 					 gboolean	 remove_source,
166 					 gint		*first);
167 
168 static gint imap_move_msg		(Folder		*folder,
169 					 FolderItem	*dest,
170 					 MsgInfo	*msginfo);
171 static gint imap_move_msgs		(Folder		*folder,
172 					 FolderItem	*dest,
173 					 GSList		*msglist);
174 static gint imap_copy_msg		(Folder		*folder,
175 					 FolderItem	*dest,
176 					 MsgInfo	*msginfo);
177 static gint imap_copy_msgs		(Folder		*folder,
178 					 FolderItem	*dest,
179 					 GSList		*msglist);
180 
181 static gint imap_remove_msg		(Folder		*folder,
182 					 FolderItem	*item,
183 					 MsgInfo	*msginfo);
184 static gint imap_remove_msgs		(Folder		*folder,
185 					 FolderItem	*item,
186 					 GSList		*msglist);
187 static gint imap_remove_all_msg		(Folder		*folder,
188 					 FolderItem	*item);
189 
190 static gboolean imap_is_msg_changed	(Folder		*folder,
191 					 FolderItem	*item,
192 					 MsgInfo	*msginfo);
193 
194 static gint imap_close			(Folder		*folder,
195 					 FolderItem	*item);
196 
197 static gint imap_scan_folder		(Folder		*folder,
198 					 FolderItem	*item);
199 static gint imap_scan_tree		(Folder		*folder);
200 
201 static gint imap_create_tree		(Folder		*folder);
202 
203 static FolderItem *imap_create_folder	(Folder		*folder,
204 					 FolderItem	*parent,
205 					 const gchar	*name);
206 static gint imap_rename_folder		(Folder		*folder,
207 					 FolderItem	*item,
208 					 const gchar	*name);
209 static gint imap_move_folder		(Folder		*folder,
210 					 FolderItem	*item,
211 					 FolderItem	*new_parent);
212 static gint imap_remove_folder		(Folder		*folder,
213 					 FolderItem	*item);
214 
215 static IMAPSession *imap_session_get	(Folder		*folder);
216 
217 static gint imap_greeting		(IMAPSession	*session);
218 static gint imap_auth			(IMAPSession	*session,
219 					 const gchar	*user,
220 					 const gchar	*pass,
221 					 IMAPAuthType	 type);
222 
223 static gint imap_scan_tree_recursive	(IMAPSession	*session,
224 					 FolderItem	*item,
225 					 GSList		*item_list);
226 static GSList *imap_get_folder_list	(IMAPSession	*session,
227 					 FolderItem	*item);
228 static GSList *imap_parse_list		(IMAPSession	*session,
229 					 const gchar	*real_path,
230 					 gchar		*separator);
231 static GSList *imap_add_inter_folders	(GSList		*item_list,
232 					 const gchar	*root_path);
233 static GSList *imap_get_part_folder_list(GSList		*item_list,
234 					 FolderItem	*item);
235 
236 static void imap_create_missing_folders	(Folder			*folder);
237 static FolderItem *imap_create_special_folder
238 					(Folder			*folder,
239 					 SpecialFolderItemType	 stype,
240 					 const gchar		*name);
241 
242 static gint imap_do_copy_msgs		(Folder		*folder,
243 					 FolderItem	*dest,
244 					 GSList		*msglist,
245 					 gboolean	 remove_source);
246 static gint imap_remove_msgs_by_seq_set	(Folder		*folder,
247 					 FolderItem	*item,
248 					 GSList		*seq_list);
249 
250 static GSList *imap_get_uncached_messages	(IMAPSession	*session,
251 						 FolderItem	*item,
252 						 guint32	 first_uid,
253 						 guint32	 last_uid,
254 						 gint		 exists,
255 						 gboolean	 update_count);
256 static void imap_delete_cached_message		(FolderItem	*item,
257 						 guint32	 uid);
258 static GSList *imap_delete_cached_messages	(GSList		*mlist,
259 						 FolderItem	*item,
260 						 guint32	 first_uid,
261 						 guint32	 last_uid);
262 static void imap_delete_all_cached_messages	(FolderItem	*item);
263 
264 #if USE_SSL
265 static SockInfo *imap_open		(const gchar	*server,
266 					 gushort	 port,
267 					 SocksInfo	*socks_info,
268 					 SSLType	 ssl_type);
269 #else
270 static SockInfo *imap_open		(const gchar	*server,
271 					 gushort	 port,
272 					 SocksInfo	*socks_info);
273 #endif
274 
275 static gint imap_msg_list_change_perm_flags	(GSList		*msglist,
276 						 MsgPermFlags	 flags,
277 						 gboolean	 is_set);
278 static gchar *imap_get_flag_str			(IMAPFlags	 flags);
279 static gint imap_set_message_flags		(IMAPSession	*session,
280 						 const gchar	*seq_set,
281 						 IMAPFlags	 flags,
282 						 gboolean	 is_set);
283 static gint imap_select				(IMAPSession	*session,
284 						 IMAPFolder	*folder,
285 						 const gchar	*path,
286 						 gint		*exists,
287 						 gint		*recent,
288 						 gint		*unseen,
289 						 guint32	*uid_validity);
290 static gint imap_status				(IMAPSession	*session,
291 						 IMAPFolder	*folder,
292 						 const gchar	*path,
293 						 gint		*messages,
294 						 gint		*recent,
295 						 guint32	*uid_next,
296 						 guint32	*uid_validity,
297 						 gint		*unseen);
298 
299 static void imap_parse_namespace		(IMAPSession	*session,
300 						 IMAPFolder	*folder);
301 static void imap_get_namespace_by_list		(IMAPSession	*session,
302 						 IMAPFolder	*folder);
303 static IMAPNameSpace *imap_find_namespace	(IMAPFolder	*folder,
304 						 const gchar	*path);
305 static gchar imap_get_path_separator		(IMAPFolder	*folder,
306 						 const gchar	*path);
307 static gchar *imap_get_real_path		(IMAPFolder	*folder,
308 						 const gchar	*path);
309 
310 static gchar *imap_parse_atom		(IMAPSession	*session,
311 					 gchar		*src,
312 					 gchar		*dest,
313 					 gint		 dest_len,
314 					 GString	*str);
315 static MsgFlags imap_parse_flags	(const gchar	*flag_str);
316 static IMAPFlags imap_parse_imap_flags	(const gchar	*flag_str);
317 static MsgInfo *imap_parse_envelope	(IMAPSession	*session,
318 					 FolderItem	*item,
319 					 GString	*line_str);
320 
321 static gboolean imap_has_capability	(IMAPSession	*session,
322 					 const gchar	*capability);
323 static void imap_capability_free	(IMAPSession	*session);
324 
325 /* low-level IMAP4rev1 commands */
326 static gint imap_cmd_capability	(IMAPSession	*session);
327 static gint imap_cmd_authenticate
328 				(IMAPSession	*session,
329 				 const gchar	*user,
330 				 const gchar	*pass,
331 				 IMAPAuthType	 type);
332 static gint imap_cmd_login	(IMAPSession	*session,
333 				 const gchar	*user,
334 				 const gchar	*pass);
335 static gint imap_cmd_logout	(IMAPSession	*session);
336 static gint imap_cmd_noop	(IMAPSession	*session);
337 #if USE_SSL
338 static gint imap_cmd_starttls	(IMAPSession	*session);
339 #endif
340 static gint imap_cmd_namespace	(IMAPSession	*session,
341 				 gchar	       **ns_str);
342 static gint imap_cmd_list	(IMAPSession	*session,
343 				 const gchar	*ref,
344 				 const gchar	*mailbox,
345 				 GPtrArray	*argbuf);
346 static gint imap_cmd_do_select	(IMAPSession	*session,
347 				 const gchar	*folder,
348 				 gboolean	 examine,
349 				 gint		*exists,
350 				 gint		*recent,
351 				 gint		*unseen,
352 				 guint32	*uid_validity);
353 static gint imap_cmd_select	(IMAPSession	*session,
354 				 const gchar	*folder,
355 				 gint		*exists,
356 				 gint		*recent,
357 				 gint		*unseen,
358 				 guint32	*uid_validity);
359 static gint imap_cmd_examine	(IMAPSession	*session,
360 				 const gchar	*folder,
361 				 gint		*exists,
362 				 gint		*recent,
363 				 gint		*unseen,
364 				 guint32	*uid_validity);
365 static gint imap_cmd_create	(IMAPSession	*session,
366 				 const gchar	*folder);
367 static gint imap_cmd_rename	(IMAPSession	*session,
368 				 const gchar	*oldfolder,
369 				 const gchar	*newfolder);
370 static gint imap_cmd_delete	(IMAPSession	*session,
371 				 const gchar	*folder);
372 static gint imap_cmd_subscribe	(IMAPSession	*session,
373 				 const gchar	*folder);
374 static gint imap_cmd_envelope	(IMAPSession	*session,
375 				 const gchar	*seq_set);
376 static gint imap_cmd_search	(IMAPSession	*session,
377 				 const gchar	*criteria,
378 				 GArray        **result);
379 static gint imap_cmd_fetch	(IMAPSession	*session,
380 				 guint32	 uid,
381 				 const gchar	*filename);
382 static gint imap_cmd_append	(IMAPSession	*session,
383 				 const gchar	*destfolder,
384 				 const gchar	*file,
385 				 IMAPFlags	 flags,
386 				 guint32	*new_uid);
387 static gint imap_cmd_copy	(IMAPSession	*session,
388 				 const gchar	*seq_set,
389 				 const gchar	*destfolder);
390 static gint imap_cmd_store	(IMAPSession	*session,
391 				 const gchar	*seq_set,
392 				 const gchar	*sub_cmd);
393 static gint imap_cmd_expunge	(IMAPSession	*session);
394 static gint imap_cmd_close	(IMAPSession	*session);
395 
396 static gint imap_cmd_ok		(IMAPSession	*session,
397 				 GPtrArray	*argbuf);
398 static gint imap_cmd_ok_real	(IMAPSession	*session,
399 				 GPtrArray	*argbuf);
400 static gint imap_cmd_gen_send	(IMAPSession	*session,
401 				 const gchar	*format, ...);
402 static gint imap_cmd_gen_recv	(IMAPSession	*session,
403 				 gchar	       **ret);
404 
405 static gint imap_cmd_gen_recv_silent	(IMAPSession	*session,
406 					 gchar	       **ret);
407 
408 /* misc utility functions */
409 static gchar *strchr_cpy			(const gchar	*src,
410 						 gchar		 ch,
411 						 gchar		*dest,
412 						 gint		 len);
413 static gchar *get_quoted			(const gchar	*src,
414 						 gchar		 ch,
415 						 gchar		*dest,
416 						 gint		 len);
417 static gchar *search_array_contain_str		(GPtrArray	*array,
418 						 gchar		*str);
419 static gchar *search_array_str			(GPtrArray	*array,
420 						 gchar		*str);
421 static void imap_path_separator_subst		(gchar		*str,
422 						 gchar		 separator);
423 
424 static gchar *imap_modified_utf7_to_utf8	(const gchar	*mutf7_str);
425 static gchar *imap_utf8_to_modified_utf7	(const gchar	*from);
426 
427 static GSList *imap_get_seq_set_from_msglist	(GSList		*msglist,
428 						 gint		 limit);
429 static gint imap_seq_set_get_count		(const gchar	*seq_set);
430 static void imap_seq_set_free			(GSList		*seq_list);
431 
432 static GHashTable *imap_get_uid_table		(GArray		*array);
433 
434 static gboolean imap_rename_folder_func		(GNode		*node,
435 						 gpointer	 data);
436 
437 #if USE_THREADS
438 static gint imap_thread_run		(IMAPSession		*session,
439 					 IMAPThreadFunc		 func,
440 					 gpointer		 data);
441 static gint imap_thread_run_progress	(IMAPSession		*session,
442 					 IMAPThreadFunc		 func,
443 					 IMAPProgressFunc	 progress_func,
444 					 gpointer		 data);
445 #endif
446 
447 static FolderClass imap_class =
448 {
449 	F_IMAP,
450 
451 	imap_folder_new,
452 	imap_folder_destroy,
453 
454 	imap_scan_tree,
455 	imap_create_tree,
456 
457 	imap_get_msg_list,
458 	imap_get_uncached_msg_list,
459 	imap_fetch_msg,
460 	imap_get_msginfo,
461 	imap_add_msg,
462 	imap_add_msgs,
463 	imap_add_msg_msginfo,
464 	imap_add_msgs_msginfo,
465 	imap_move_msg,
466 	imap_move_msgs,
467 	imap_copy_msg,
468 	imap_copy_msgs,
469 	imap_remove_msg,
470 	imap_remove_msgs,
471 	imap_remove_all_msg,
472 	imap_is_msg_changed,
473 	imap_close,
474 	imap_scan_folder,
475 
476 	imap_create_folder,
477 	imap_rename_folder,
478 	imap_move_folder,
479 	imap_remove_folder
480 };
481 
482 
imap_get_class(void)483 FolderClass *imap_get_class(void)
484 {
485 	return &imap_class;
486 }
487 
imap_folder_new(const gchar * name,const gchar * path)488 static Folder *imap_folder_new(const gchar *name, const gchar *path)
489 {
490 	Folder *folder;
491 
492 	folder = (Folder *)g_new0(IMAPFolder, 1);
493 	imap_folder_init(folder, name, path);
494 
495 	return folder;
496 }
497 
imap_folder_destroy(Folder * folder)498 static void imap_folder_destroy(Folder *folder)
499 {
500 	g_return_if_fail(folder->account != NULL);
501 
502 	if (REMOTE_FOLDER(folder)->remove_cache_on_destroy) {
503 		gchar *dir;
504 		gchar *server;
505 
506 		dir = folder_get_path(folder);
507 		if (is_dir_exist(dir))
508 			remove_dir_recursive(dir);
509 		g_free(dir);
510 
511 		server = uriencode_for_filename(folder->account->recv_server);
512 		dir = g_strconcat(get_imap_cache_dir(), G_DIR_SEPARATOR_S,
513 				  server, NULL);
514 		if (is_dir_exist(dir))
515 			g_rmdir(dir);
516 		g_free(dir);
517 		g_free(server);
518 	}
519 
520 	folder_remote_folder_destroy(REMOTE_FOLDER(folder));
521 }
522 
imap_folder_init(Folder * folder,const gchar * name,const gchar * path)523 static void imap_folder_init(Folder *folder, const gchar *name,
524 			     const gchar *path)
525 {
526 	folder->klass = imap_get_class();
527 	folder_remote_folder_init(folder, name, path);
528 }
529 
imap_session_get(Folder * folder)530 static IMAPSession *imap_session_get(Folder *folder)
531 {
532 	RemoteFolder *rfolder = REMOTE_FOLDER(folder);
533 	gint ret;
534 
535 	g_return_val_if_fail(folder != NULL, NULL);
536 	g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL);
537 	g_return_val_if_fail(folder->account != NULL, NULL);
538 
539 	if (!prefs_common.online_mode)
540 		return NULL;
541 
542 	if (!rfolder->session) {
543 		rfolder->session = imap_session_new(folder->account);
544 		if (rfolder->session)
545 			imap_parse_namespace(IMAP_SESSION(rfolder->session),
546 					     IMAP_FOLDER(folder));
547 		return IMAP_SESSION(rfolder->session);
548 	}
549 
550 	if (imap_is_session_active(IMAP_FOLDER(folder))) {
551 		g_warning("imap_session_get: session is busy.");
552 		return NULL;
553 	}
554 
555 	if (time(NULL) - rfolder->session->last_access_time <
556 		SESSION_TIMEOUT_INTERVAL) {
557 		return IMAP_SESSION(rfolder->session);
558 	}
559 
560 	if ((ret = imap_cmd_noop(IMAP_SESSION(rfolder->session))) != IMAP_SUCCESS) {
561 		if (ret == IMAP_EAGAIN) {
562 			g_warning("imap_session_get: session is busy.");
563 			return NULL;
564 		}
565 
566 		log_warning(_("IMAP4 connection to %s has been"
567 			      " disconnected. Reconnecting...\n"),
568 			    folder->account->recv_server);
569 		if (imap_session_reconnect(IMAP_SESSION(rfolder->session))
570 		    == IMAP_SUCCESS)
571 			imap_parse_namespace(IMAP_SESSION(rfolder->session),
572 					     IMAP_FOLDER(folder));
573 		else {
574 			session_destroy(rfolder->session);
575 			rfolder->session = NULL;
576 		}
577 	}
578 
579 	return IMAP_SESSION(rfolder->session);
580 }
581 
imap_greeting(IMAPSession * session)582 static gint imap_greeting(IMAPSession *session)
583 {
584 	gchar *greeting;
585 	gint ok;
586 
587 	if ((ok = imap_cmd_gen_recv(session, &greeting)) != IMAP_SUCCESS) {
588 		log_warning("Cannot get greeting message (%d)\n", ok);
589 		return ok;
590 	}
591 
592 	if (greeting[0] != '*' || greeting[1] != ' ')
593 		ok = IMAP_ERROR;
594 	else if (!strncmp(greeting + 2, "OK", 2))
595 		ok = IMAP_SUCCESS;
596 	else if (!strncmp(greeting + 2, "PREAUTH", 7)) {
597 		session->authenticated = TRUE;
598 		ok = IMAP_SUCCESS;
599 	} else
600 		ok = IMAP_ERROR;
601 
602 	g_free(greeting);
603 	return ok;
604 }
605 
imap_auth(IMAPSession * session,const gchar * user,const gchar * pass,IMAPAuthType type)606 static gint imap_auth(IMAPSession *session, const gchar *user,
607 		      const gchar *pass, IMAPAuthType type)
608 {
609 	gboolean nologin;
610 	gint ok = IMAP_AUTHFAIL;
611 
612 	nologin = imap_has_capability(session, "LOGINDISABLED");
613 
614 	switch (type) {
615 	case 0:
616 		if (imap_has_capability(session, "AUTH=CRAM-MD5"))
617 			ok = imap_cmd_authenticate(session, user, pass,
618 						   IMAP_AUTH_CRAM_MD5);
619 		else if (imap_has_capability(session, "AUTH=PLAIN"))
620 			ok = imap_cmd_authenticate(session, user, pass,
621 						   IMAP_AUTH_PLAIN);
622 		else if (nologin)
623 			log_print(_("IMAP4 server disables LOGIN.\n"));
624 		else
625 			ok = imap_cmd_login(session, user, pass);
626 		break;
627 	case IMAP_AUTH_LOGIN:
628 		if (nologin)
629 			log_warning(_("IMAP4 server disables LOGIN.\n"));
630 		else
631 			ok = imap_cmd_login(session, user, pass);
632 		break;
633 	case IMAP_AUTH_CRAM_MD5:
634 	case IMAP_AUTH_PLAIN:
635 		ok = imap_cmd_authenticate(session, user, pass, type);
636 		break;
637 	default:
638 		break;
639 	}
640 
641 	if (ok == IMAP_SUCCESS)
642 		session->authenticated = TRUE;
643 
644 	return ok;
645 }
646 
imap_session_new(PrefsAccount * account)647 static Session *imap_session_new(PrefsAccount *account)
648 {
649 	IMAPSession *session;
650 	gushort port;
651 
652 	g_return_val_if_fail(account != NULL, NULL);
653 	g_return_val_if_fail(account->recv_server != NULL, NULL);
654 	g_return_val_if_fail(account->userid != NULL, NULL);
655 
656 #if USE_SSL
657 	port = account->set_imapport ? account->imapport
658 		: account->ssl_imap == SSL_TUNNEL ? IMAPS_PORT : IMAP4_PORT;
659 #else
660 	port = account->set_imapport ? account->imapport : IMAP4_PORT;
661 #endif
662 
663 	session = IMAP_SESSION(g_new0(IMAPRealSession, 1));
664 
665 	session_init(SESSION(session));
666 
667 	SESSION(session)->type             = SESSION_IMAP;
668 	SESSION(session)->sock             = NULL;
669 	SESSION(session)->server           = g_strdup(account->recv_server);
670 	SESSION(session)->port             = port;
671 #if USE_SSL
672 	SESSION(session)->ssl_type         = account->ssl_imap;
673 #endif
674 	SESSION(session)->last_access_time = time(NULL);
675 	SESSION(session)->data             = account;
676 
677 	SESSION(session)->destroy          = imap_session_destroy;
678 
679 	session->authenticated = FALSE;
680 	session->capability    = NULL;
681 	session->uidplus       = FALSE;
682 	session->mbox          = NULL;
683 	session->cmd_count     = 0;
684 
685 	session_list = g_list_append(session_list, session);
686 
687 	if (imap_session_connect(session) != IMAP_SUCCESS) {
688 		log_warning(_("Could not establish IMAP connection.\n"));
689 		session_destroy(SESSION(session));
690 		return NULL;
691 	}
692 
693 	return SESSION(session);
694 }
695 
imap_session_connect(IMAPSession * session)696 static gint imap_session_connect(IMAPSession *session)
697 {
698 	SockInfo *sock;
699 	SocksInfo *socks_info = NULL;
700 	PrefsAccount *account;
701 	const gchar *pass;
702 
703 	g_return_val_if_fail(session != NULL, IMAP_ERROR);
704 
705 	account = (PrefsAccount *)(SESSION(session)->data);
706 
707 	log_message(_("creating IMAP4 connection to %s:%d ...\n"),
708 		    SESSION(session)->server, SESSION(session)->port);
709 
710 	pass = account->passwd;
711 	if (!pass)
712 		pass = account->tmp_pass;
713 	if (!pass) {
714 		gchar *tmp_pass;
715 
716 		tmp_pass = input_query_password(account->recv_server,
717 						account->userid);
718 		if (!tmp_pass)
719 			return IMAP_ERROR;
720 
721 		account->tmp_pass = tmp_pass;
722 		pass = account->tmp_pass;
723 	}
724 
725 	if (account->use_socks && account->use_socks_for_recv &&
726 	    account->proxy_host) {
727 		socks_info = socks_info_new(account->socks_type, account->proxy_host, account->proxy_port, account->use_proxy_auth ? account->proxy_name : NULL, account->use_proxy_auth ? account->proxy_pass : NULL);
728 	}
729 
730 #if USE_SSL
731 	if ((sock = imap_open(SESSION(session)->server, SESSION(session)->port,
732 			      socks_info, SESSION(session)->ssl_type)) == NULL)
733 #else
734 	if ((sock = imap_open(SESSION(session)->server, SESSION(session)->port,
735 			      socks_info))
736 	    == NULL)
737 #endif
738 		return IMAP_ERROR;
739 
740 	if (socks_info)
741 		socks_info_free(socks_info);
742 
743 	SESSION(session)->sock = sock;
744 
745 	if (imap_greeting(session) != IMAP_SUCCESS)
746 		return IMAP_ERROR;
747 	if (imap_cmd_capability(session) != IMAP_SUCCESS)
748 		return IMAP_ERROR;
749 
750 	if (imap_has_capability(session, "UIDPLUS"))
751 		session->uidplus = TRUE;
752 
753 #if USE_SSL
754 	if (account->ssl_imap == SSL_STARTTLS &&
755 	    imap_has_capability(session, "STARTTLS")) {
756 		gint ok;
757 
758 		ok = imap_cmd_starttls(session);
759 		if (ok != IMAP_SUCCESS) {
760 			log_warning(_("Can't start TLS session.\n"));
761 			return IMAP_ERROR;
762 		}
763 		if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1))
764 			return IMAP_SOCKET;
765 
766 		/* capability can be changed after STARTTLS */
767 		if (imap_cmd_capability(session) != IMAP_SUCCESS)
768 			return IMAP_ERROR;
769 	}
770 #endif
771 
772 	if (!session->authenticated &&
773 	    imap_auth(session, account->userid, pass, account->imap_auth_type)
774 	    != IMAP_SUCCESS) {
775 		if (account->tmp_pass) {
776 			g_free(account->tmp_pass);
777 			account->tmp_pass = NULL;
778 		}
779 		imap_cmd_logout(session);
780 		return IMAP_AUTHFAIL;
781 	}
782 
783 	return IMAP_SUCCESS;
784 }
785 
imap_session_reconnect(IMAPSession * session)786 static gint imap_session_reconnect(IMAPSession *session)
787 {
788 	g_return_val_if_fail(session != NULL, IMAP_ERROR);
789 
790 	session_disconnect(SESSION(session));
791 
792 	imap_capability_free(session);
793 	session->uidplus = FALSE;
794 	g_free(session->mbox);
795 	session->mbox = NULL;
796 	session->authenticated = FALSE;
797 	SESSION(session)->state = SESSION_READY;
798 
799 	return imap_session_connect(session);
800 }
801 
imap_session_destroy(Session * session)802 static void imap_session_destroy(Session *session)
803 {
804 #if USE_THREADS
805 	IMAPRealSession *real = (IMAPRealSession *)session;
806 
807 	if (real->pool)
808 		g_thread_pool_free(real->pool, TRUE, TRUE);
809 #endif
810 	imap_capability_free(IMAP_SESSION(session));
811 	g_free(IMAP_SESSION(session)->mbox);
812 	session_list = g_list_remove(session_list, session);
813 }
814 
815 #if 0
816 static void imap_session_destroy_all(void)
817 {
818 	while (session_list != NULL) {
819 		IMAPSession *session = (IMAPSession *)session_list->data;
820 
821 		imap_cmd_logout(session);
822 		session_destroy(SESSION(session));
823 	}
824 }
825 #endif
826 
827 #define THROW goto catch
828 
imap_search_flags(IMAPSession * session,GArray ** uids,GHashTable ** flags_table)829 static gint imap_search_flags(IMAPSession *session, GArray **uids,
830 			      GHashTable **flags_table)
831 {
832 	gint ok;
833 	gint i;
834 	GArray *flag_uids;
835 	GHashTable *unseen_table;
836 	GHashTable *flagged_table;
837 	GHashTable *answered_table;
838 	guint32 uid;
839 	IMAPFlags flags;
840 
841 	ok = imap_cmd_search(session, "ALL", uids);
842 	if (ok != IMAP_SUCCESS) return ok;
843 
844 	ok = imap_cmd_search(session, "UNSEEN", &flag_uids);
845 	if (ok != IMAP_SUCCESS) {
846 		g_array_free(*uids, TRUE);
847 		return ok;
848 	}
849 	unseen_table = imap_get_uid_table(flag_uids);
850 	g_array_free(flag_uids, TRUE);
851 	ok = imap_cmd_search(session, "FLAGGED", &flag_uids);
852 	if (ok != IMAP_SUCCESS) {
853 		g_hash_table_destroy(unseen_table);
854 		g_array_free(*uids, TRUE);
855 		return ok;
856 	}
857 	flagged_table = imap_get_uid_table(flag_uids);
858 	g_array_free(flag_uids, TRUE);
859 	ok = imap_cmd_search(session, "ANSWERED", &flag_uids);
860 	if (ok != IMAP_SUCCESS) {
861 		g_hash_table_destroy(flagged_table);
862 		g_hash_table_destroy(unseen_table);
863 		g_array_free(*uids, TRUE);
864 		return ok;
865 	}
866 	answered_table = imap_get_uid_table(flag_uids);
867 	g_array_free(flag_uids, TRUE);
868 
869 	*flags_table = g_hash_table_new(NULL, g_direct_equal);
870 
871 	for (i = 0; i < (*uids)->len; i++) {
872 		uid = g_array_index(*uids, guint32, i);
873 		flags = IMAP_FLAG_DRAFT;
874 		if (!g_hash_table_lookup(unseen_table, GUINT_TO_POINTER(uid)))
875 			flags |= IMAP_FLAG_SEEN;
876 		if (g_hash_table_lookup(flagged_table, GUINT_TO_POINTER(uid)))
877 			flags |= IMAP_FLAG_FLAGGED;
878 		if (g_hash_table_lookup(answered_table, GUINT_TO_POINTER(uid)))
879 			flags |= IMAP_FLAG_ANSWERED;
880 		g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid),
881 				    GINT_TO_POINTER(flags));
882 	}
883 
884 	g_hash_table_destroy(answered_table);
885 	g_hash_table_destroy(flagged_table);
886 	g_hash_table_destroy(unseen_table);
887 
888 	return IMAP_SUCCESS;
889 }
890 
imap_fetch_flags(IMAPSession * session,GArray ** uids,GHashTable ** flags_table)891 static gint imap_fetch_flags(IMAPSession *session, GArray **uids,
892 			     GHashTable **flags_table)
893 {
894 	gint ok;
895 	gchar *tmp;
896 	gchar *cur_pos;
897 	gchar buf[IMAPBUFSIZE];
898 	guint32 uid;
899 	IMAPFlags flags;
900 
901 	if (imap_cmd_gen_send(session, "UID FETCH 1:* (UID FLAGS)") != IMAP_SUCCESS)
902 		return IMAP_ERROR;
903 
904 	*uids = g_array_new(FALSE, FALSE, sizeof(guint32));
905 	*flags_table = g_hash_table_new(NULL, g_direct_equal);
906 
907 	log_print("IMAP4< %s\n", _("(retrieving FLAGS...)"));
908 
909 	while ((ok = imap_cmd_gen_recv_silent(session, &tmp)) == IMAP_SUCCESS) {
910 		if (tmp[0] != '*' || tmp[1] != ' ') {
911 			log_print("IMAP4< %s\n", tmp);
912 			g_free(tmp);
913 			break;
914 		}
915 		cur_pos = tmp + 2;
916 
917 #define PARSE_ONE_ELEMENT(ch)					\
918 {								\
919 	cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));	\
920 	if (cur_pos == NULL) {					\
921 		g_warning("cur_pos == NULL\n");			\
922 		g_free(tmp);					\
923 		g_hash_table_destroy(*flags_table);		\
924 		g_array_free(*uids, TRUE);			\
925 		return IMAP_ERROR;				\
926 	}							\
927 }
928 
929 		PARSE_ONE_ELEMENT(' ');
930 		PARSE_ONE_ELEMENT(' ');
931 		if (strcmp(buf, "FETCH") != 0) {
932 			g_free(tmp);
933 			continue;
934 		}
935 		if (*cur_pos != '(') {
936 			g_free(tmp);
937 			continue;
938 		}
939 		cur_pos++;
940 		uid = 0;
941 		flags = 0;
942 
943 		while (*cur_pos != '\0' && *cur_pos != ')') {
944 			while (*cur_pos == ' ') cur_pos++;
945 
946 			if (!strncmp(cur_pos, "UID ", 4)) {
947 				cur_pos += 4;
948 				uid = strtoul(cur_pos, &cur_pos, 10);
949 			} else if (!strncmp(cur_pos, "FLAGS ", 6)) {
950 				cur_pos += 6;
951 				if (*cur_pos != '(') {
952 					g_warning("*cur_pos != '('\n");
953 					break;
954 				}
955 				cur_pos++;
956 				PARSE_ONE_ELEMENT(')');
957 				flags = imap_parse_imap_flags(buf);
958 				flags |= IMAP_FLAG_DRAFT;
959 			} else {
960 				g_warning("invalid FETCH response: %s\n", cur_pos);
961 				break;
962 			}
963 		}
964 
965 #undef PARSE_ONE_ELEMENT
966 
967 		if (uid > 0) {
968 			g_array_append_val(*uids, uid);
969 			g_hash_table_insert(*flags_table, GUINT_TO_POINTER(uid),
970 					    GINT_TO_POINTER(flags));
971 		}
972 
973 		g_free(tmp);
974 	}
975 
976 	if (ok != IMAP_SUCCESS) {
977 		g_hash_table_destroy(*flags_table);
978 		g_array_free(*uids, TRUE);
979 	}
980 
981 	return ok;
982 }
983 
imap_get_msg_list_full(Folder * folder,FolderItem * item,gboolean use_cache,gboolean uncached_only)984 static GSList *imap_get_msg_list_full(Folder *folder, FolderItem *item,
985 				      gboolean use_cache,
986 				      gboolean uncached_only)
987 {
988 	GSList *mlist = NULL;
989 	IMAPSession *session;
990 	gint ok, exists = 0, recent = 0, unseen = 0;
991 	guint32 uid_validity = 0;
992 	guint32 first_uid = 0, last_uid = 0;
993 	GSList *newlist = NULL;
994 
995 	g_return_val_if_fail(folder != NULL, NULL);
996 	g_return_val_if_fail(item != NULL, NULL);
997 	g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, NULL);
998 	g_return_val_if_fail(folder->account != NULL, NULL);
999 
1000 	item->new = item->unread = item->total = 0;
1001 
1002 	session = imap_session_get(folder);
1003 
1004 	if (!session) {
1005 		if (uncached_only)
1006 			return NULL;
1007 		mlist = procmsg_read_cache(item, FALSE);
1008 		item->last_num = procmsg_get_last_num_in_msg_list(mlist);
1009 		procmsg_set_flags(mlist, item);
1010 		return mlist;
1011 	}
1012 
1013 	ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1014 			 &exists, &recent, &unseen, &uid_validity);
1015 	if (ok != IMAP_SUCCESS) THROW;
1016 
1017 	if (exists == 0) {
1018 		imap_delete_all_cached_messages(item);
1019 		return NULL;
1020 	}
1021 
1022 	/* invalidate current cache if UIDVALIDITY has been changed */
1023 	if (item->mtime != uid_validity) {
1024 		debug_print("imap_get_msg_list: "
1025 			    "UIDVALIDITY has been changed.\n");
1026 		use_cache = FALSE;
1027 	}
1028 
1029 	if (use_cache) {
1030 		GArray *uids;
1031 		GHashTable *msg_table;
1032 		GHashTable *flags_table;
1033 		guint32 cache_last;
1034 		guint32 begin = 0;
1035 		GSList *cur, *next = NULL;
1036 		MsgInfo *msginfo;
1037 		IMAPFlags imap_flags;
1038 		guint color;
1039 
1040 		/* get cache data */
1041 		mlist = procmsg_read_cache(item, FALSE);
1042 		procmsg_set_flags(mlist, item);
1043 		cache_last = procmsg_get_last_num_in_msg_list(mlist);
1044 
1045 		/* get all UID list and flags */
1046 #if 0
1047 		ok = imap_search_flags(session, &uids, &flags_table);
1048 		if (ok != IMAP_SUCCESS) {
1049 			if (ok == IMAP_SOCKET || ok == IMAP_IOERR) THROW;
1050 			ok = imap_fetch_flags(session, &uids, &flags_table);
1051 			if (ok != IMAP_SUCCESS) THROW;
1052 		}
1053 #else
1054 		ok = imap_fetch_flags(session, &uids, &flags_table);
1055 		if (ok != IMAP_SUCCESS) THROW;
1056 #endif
1057 
1058 		if (uids->len > 0) {
1059 			first_uid = g_array_index(uids, guint32, 0);
1060 			last_uid = g_array_index(uids, guint32, uids->len - 1);
1061 		} else {
1062 			g_array_free(uids, TRUE);
1063 			g_hash_table_destroy(flags_table);
1064 			THROW;
1065 		}
1066 
1067 		/* sync message flags with server */
1068 		for (cur = mlist; cur != NULL; cur = next) {
1069 			msginfo = (MsgInfo *)cur->data;
1070 			next = cur->next;
1071 			imap_flags = GPOINTER_TO_INT(g_hash_table_lookup
1072 				(flags_table,
1073 				 GUINT_TO_POINTER(msginfo->msgnum)));
1074 
1075 			if (imap_flags == 0) {
1076 				debug_print("imap_get_msg_list: "
1077 					    "message %u has been deleted.\n",
1078 					    msginfo->msgnum);
1079 				imap_delete_cached_message
1080 					(item, msginfo->msgnum);
1081 				if (MSG_IS_NEW(msginfo->flags))
1082 					item->new--;
1083 				if (MSG_IS_UNREAD(msginfo->flags))
1084 					item->unread--;
1085 				item->total--;
1086 				mlist = g_slist_remove(mlist, msginfo);
1087 				procmsg_msginfo_free(msginfo);
1088 				item->cache_dirty = TRUE;
1089 				item->mark_dirty = TRUE;
1090 				continue;
1091 			}
1092 
1093 			if (!IMAP_IS_SEEN(imap_flags)) {
1094 				if (!MSG_IS_UNREAD(msginfo->flags)) {
1095 					item->unread++;
1096 					MSG_SET_PERM_FLAGS(msginfo->flags,
1097 							   MSG_UNREAD);
1098 					item->mark_dirty = TRUE;
1099 				}
1100 			} else {
1101 				if (MSG_IS_NEW(msginfo->flags)) {
1102 					item->new--;
1103 					item->mark_dirty = TRUE;
1104 				}
1105 				if (MSG_IS_UNREAD(msginfo->flags)) {
1106 					item->unread--;
1107 					item->mark_dirty = TRUE;
1108 				}
1109 				MSG_UNSET_PERM_FLAGS(msginfo->flags,
1110 						     MSG_NEW|MSG_UNREAD);
1111 			}
1112 
1113 			if (IMAP_IS_FLAGGED(imap_flags)) {
1114 				if (!MSG_IS_MARKED(msginfo->flags)) {
1115 					MSG_SET_PERM_FLAGS(msginfo->flags,
1116 							   MSG_MARKED);
1117 					item->mark_dirty = TRUE;
1118 				}
1119 			} else {
1120 				if (MSG_IS_MARKED(msginfo->flags)) {
1121 					MSG_UNSET_PERM_FLAGS(msginfo->flags,
1122 							     MSG_MARKED);
1123 					item->mark_dirty = TRUE;
1124 				}
1125 			}
1126 			if (IMAP_IS_ANSWERED(imap_flags)) {
1127 				if (!MSG_IS_REPLIED(msginfo->flags)) {
1128 					MSG_SET_PERM_FLAGS(msginfo->flags,
1129 							   MSG_REPLIED);
1130 					item->mark_dirty = TRUE;
1131 				}
1132 			} else {
1133 				if (MSG_IS_REPLIED(msginfo->flags)) {
1134 					MSG_UNSET_PERM_FLAGS(msginfo->flags,
1135 							     MSG_REPLIED);
1136 					item->mark_dirty = TRUE;
1137 				}
1138 			}
1139 
1140 			color = IMAP_GET_COLORLABEL_VALUE(imap_flags);
1141 			if (MSG_GET_COLORLABEL_VALUE(msginfo->flags) != color) {
1142 				MSG_UNSET_PERM_FLAGS(msginfo->flags,
1143 						     MSG_CLABEL_FLAG_MASK);
1144 				MSG_SET_COLORLABEL_VALUE(msginfo->flags, color);
1145 				item->mark_dirty = TRUE;
1146 			}
1147 		}
1148 
1149 		/* check for the first new message */
1150 		msg_table = procmsg_msg_hash_table_create(mlist);
1151 		if (msg_table == NULL)
1152 			begin = first_uid;
1153 		else {
1154 			gint i;
1155 
1156 			for (i = 0; i < uids->len; i++) {
1157 				guint32 uid;
1158 
1159 				uid = g_array_index(uids, guint32, i);
1160 				if (g_hash_table_lookup
1161 					(msg_table, GUINT_TO_POINTER(uid))
1162 					== NULL) {
1163 					debug_print("imap_get_msg_list: "
1164 						    "first new UID: %u\n", uid);
1165 					begin = uid;
1166 					break;
1167 				}
1168 			}
1169 			g_hash_table_destroy(msg_table);
1170 		}
1171 
1172 		g_array_free(uids, TRUE);
1173 		g_hash_table_destroy(flags_table);
1174 
1175 		/* remove ununsed caches */
1176 		if (first_uid > 0 && last_uid > 0) {
1177 			mlist = imap_delete_cached_messages
1178 				(mlist, item, 0, first_uid - 1);
1179 			mlist = imap_delete_cached_messages
1180 				(mlist, item, begin > 0 ? begin : last_uid + 1,
1181 				 UINT_MAX);
1182 		}
1183 
1184 		if (begin > 0 && begin <= last_uid) {
1185 			newlist = imap_get_uncached_messages
1186 				(session, item, begin, last_uid,
1187 				 exists - item->total, TRUE);
1188 			if (newlist) {
1189 				item->cache_dirty = TRUE;
1190 				item->mark_dirty = TRUE;
1191 			}
1192 			mlist = g_slist_concat(mlist, newlist);
1193 		}
1194 	} else {
1195 		imap_delete_all_cached_messages(item);
1196 		mlist = imap_get_uncached_messages(session, item, 0, 0, exists,
1197 						   TRUE);
1198 		last_uid = procmsg_get_last_num_in_msg_list(mlist);
1199 		item->cache_dirty = TRUE;
1200 		item->mark_dirty = TRUE;
1201 		newlist = mlist;
1202 	}
1203 
1204 	if (!uncached_only)
1205 		mlist = procmsg_sort_msg_list(mlist, item->sort_key,
1206 					      item->sort_type);
1207 
1208 	item->last_num = last_uid;
1209 
1210 	if (item->mark_queue)
1211 		item->mark_dirty = TRUE;
1212 
1213 	debug_print("cache_dirty: %d, mark_dirty: %d\n",
1214 		    item->cache_dirty, item->mark_dirty);
1215 
1216 	if (!item->opened) {
1217 		item->mtime = uid_validity;
1218 		if (item->cache_dirty)
1219 			procmsg_write_cache_list(item, mlist);
1220 		if (item->mark_dirty)
1221 			procmsg_write_flags_list(item, mlist);
1222 	}
1223 
1224 catch:
1225 	if (uncached_only) {
1226 		GSList *cur;
1227 
1228 		if (newlist == NULL) {
1229 			procmsg_msg_list_free(mlist);
1230 			return NULL;
1231 		}
1232 		if (mlist == newlist)
1233 			return newlist;
1234 		for (cur = mlist; cur != NULL; cur = cur->next) {
1235 			if (cur->next == newlist) {
1236 				cur->next = NULL;
1237 				procmsg_msg_list_free(mlist);
1238 				return newlist;
1239 			}
1240 		}
1241 		procmsg_msg_list_free(mlist);
1242 		return NULL;
1243 	}
1244 
1245 	return mlist;
1246 }
1247 
1248 #undef THROW
1249 
imap_get_msg_list(Folder * folder,FolderItem * item,gboolean use_cache)1250 static GSList *imap_get_msg_list(Folder *folder, FolderItem *item,
1251 				 gboolean use_cache)
1252 {
1253 	return imap_get_msg_list_full(folder, item, use_cache, FALSE);
1254 }
1255 
imap_get_uncached_msg_list(Folder * folder,FolderItem * item)1256 static GSList *imap_get_uncached_msg_list(Folder *folder, FolderItem *item)
1257 {
1258 	return imap_get_msg_list_full(folder, item, TRUE, TRUE);
1259 }
1260 
imap_fetch_msg(Folder * folder,FolderItem * item,gint uid)1261 static gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
1262 {
1263 	gchar *path, *filename;
1264 	IMAPSession *session;
1265 	gchar nstr[16];
1266 	guint32 uid32 = (guint32)uid;
1267 	gint ok;
1268 
1269 	g_return_val_if_fail(folder != NULL, NULL);
1270 	g_return_val_if_fail(item != NULL, NULL);
1271 
1272 	path = folder_item_get_path(item);
1273 	if (!is_dir_exist(path))
1274 		make_dir_hier(path);
1275 	g_snprintf(nstr, sizeof(nstr), "%u", uid32);
1276 	filename = g_strconcat(path, G_DIR_SEPARATOR_S, nstr, NULL);
1277 	g_free(path);
1278 
1279 	if (is_file_exist(filename) && get_file_size(filename) > 0) {
1280 		debug_print("message %u has been already cached.\n", uid32);
1281 		return filename;
1282 	}
1283 
1284 	session = imap_session_get(folder);
1285 	if (!session) {
1286 		g_free(filename);
1287 		return NULL;
1288 	}
1289 
1290 	ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1291 			 NULL, NULL, NULL, NULL);
1292 	if (ok != IMAP_SUCCESS) {
1293 		g_warning("can't select mailbox %s\n", item->path);
1294 		g_free(filename);
1295 		return NULL;
1296 	}
1297 
1298 	status_print(_("Getting message %u"), uid32);
1299 	debug_print("getting message %u...\n", uid32);
1300 	ok = imap_cmd_fetch(session, uid32, filename);
1301 
1302 	if (ok != IMAP_SUCCESS) {
1303 		g_warning("can't fetch message %u\n", uid32);
1304 		g_free(filename);
1305 		return NULL;
1306 	}
1307 
1308 	return filename;
1309 }
1310 
imap_get_msginfo(Folder * folder,FolderItem * item,gint uid)1311 static MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
1312 {
1313 	IMAPSession *session;
1314 	GSList *list;
1315 	MsgInfo *msginfo = NULL;
1316 	gint ok;
1317 
1318 	g_return_val_if_fail(folder != NULL, NULL);
1319 	g_return_val_if_fail(item != NULL, NULL);
1320 
1321 	session = imap_session_get(folder);
1322 	g_return_val_if_fail(session != NULL, NULL);
1323 
1324 	ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1325 			 NULL, NULL, NULL, NULL);
1326 	if (ok != IMAP_SUCCESS)
1327 		return NULL;
1328 
1329 	list = imap_get_uncached_messages(session, item, uid, uid, 0, FALSE);
1330 	if (list) {
1331 		msginfo = (MsgInfo *)list->data;
1332 		list->data = NULL;
1333 	}
1334 	procmsg_msg_list_free(list);
1335 
1336 	return msginfo;
1337 }
1338 
imap_add_msg(Folder * folder,FolderItem * dest,const gchar * file,MsgFlags * flags,gboolean remove_source)1339 static gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1340 			 MsgFlags *flags, gboolean remove_source)
1341 {
1342 	GSList file_list;
1343 	MsgFileInfo fileinfo;
1344 
1345 	g_return_val_if_fail(file != NULL, -1);
1346 
1347 	fileinfo.file = (gchar *)file;
1348 	fileinfo.flags = flags;
1349 	file_list.data = &fileinfo;
1350 	file_list.next = NULL;
1351 
1352 	return imap_add_msgs(folder, dest, &file_list, remove_source, NULL);
1353 }
1354 
imap_add_msgs(Folder * folder,FolderItem * dest,GSList * file_list,gboolean remove_source,gint * first)1355 static gint imap_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
1356 			  gboolean remove_source, gint *first)
1357 {
1358 	gchar *destdir;
1359 	IMAPSession *session;
1360 	gint messages, recent, unseen;
1361 	guint32 uid_next, uid_validity;
1362 	guint32 last_uid = 0;
1363 	GSList *cur;
1364 	MsgFileInfo *fileinfo;
1365 	gint count = 1;
1366 	gint total;
1367 	gint ok;
1368 	GTimeVal tv_prev, tv_cur;
1369 
1370 	g_return_val_if_fail(folder != NULL, -1);
1371 	g_return_val_if_fail(dest != NULL, -1);
1372 	g_return_val_if_fail(file_list != NULL, -1);
1373 
1374 	session = imap_session_get(folder);
1375 	if (!session) return -1;
1376 
1377 	g_get_current_time(&tv_prev);
1378 	ui_update();
1379 
1380 	ok = imap_status(session, IMAP_FOLDER(folder), dest->path,
1381 			 &messages, &recent, &uid_next, &uid_validity, &unseen);
1382 	if (ok != IMAP_SUCCESS) {
1383 		g_warning("can't append messages\n");
1384 		return -1;
1385 	}
1386 
1387 	destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1388 
1389 	if (!session->uidplus)
1390 		last_uid = uid_next - 1;
1391 	if (first)
1392 		*first = uid_next;
1393 
1394 	total = g_slist_length(file_list);
1395 
1396 	for (cur = file_list; cur != NULL; cur = cur->next) {
1397 		IMAPFlags iflags = 0;
1398 		guint32 new_uid = 0;
1399 
1400 		fileinfo = (MsgFileInfo *)cur->data;
1401 
1402 		if (fileinfo->flags) {
1403 			if (MSG_IS_MARKED(*fileinfo->flags))
1404 				iflags |= IMAP_FLAG_FLAGGED;
1405 			if (MSG_IS_REPLIED(*fileinfo->flags))
1406 				iflags |= IMAP_FLAG_ANSWERED;
1407 			if (!MSG_IS_UNREAD(*fileinfo->flags))
1408 				iflags |= IMAP_FLAG_SEEN;
1409 		}
1410 
1411 		if (dest->stype == F_OUTBOX ||
1412 		    dest->stype == F_QUEUE  ||
1413 		    dest->stype == F_DRAFT)
1414 			iflags |= IMAP_FLAG_SEEN;
1415 
1416 		g_get_current_time(&tv_cur);
1417 		if (tv_cur.tv_sec > tv_prev.tv_sec ||
1418 		    tv_cur.tv_usec - tv_prev.tv_usec >
1419 		    PROGRESS_UPDATE_INTERVAL * 1000) {
1420 			status_print(_("Appending messages to %s (%d / %d)"),
1421 				     dest->path, count, total);
1422 			progress_show(count, total);
1423 			ui_update();
1424 			tv_prev = tv_cur;
1425 		}
1426 		++count;
1427 
1428 		ok = imap_cmd_append(session, destdir, fileinfo->file, iflags,
1429 				     &new_uid);
1430 
1431 		if (ok != IMAP_SUCCESS) {
1432 			g_warning("can't append message %s\n", fileinfo->file);
1433 			g_free(destdir);
1434 			progress_show(0, 0);
1435 			return -1;
1436 		}
1437 
1438 		if (syl_app_get())
1439 			g_signal_emit_by_name(syl_app_get(), "add-msg", dest, fileinfo->file, new_uid);
1440 
1441 		if (!session->uidplus)
1442 			last_uid++;
1443 		else if (last_uid < new_uid)
1444 			last_uid = new_uid;
1445 
1446 		dest->last_num = last_uid;
1447 		dest->total++;
1448 		dest->updated = TRUE;
1449 
1450 		if (fileinfo->flags) {
1451 			if (MSG_IS_UNREAD(*fileinfo->flags))
1452 				dest->unread++;
1453 		} else
1454 			dest->unread++;
1455 	}
1456 
1457 	progress_show(0, 0);
1458 	g_free(destdir);
1459 
1460 	if (remove_source) {
1461 		for (cur = file_list; cur != NULL; cur = cur->next) {
1462 			fileinfo = (MsgFileInfo *)cur->data;
1463 			if (g_unlink(fileinfo->file) < 0)
1464 				 FILE_OP_ERROR(fileinfo->file, "unlink");
1465 		}
1466 	}
1467 
1468 	return last_uid;
1469 }
1470 
imap_add_msg_msginfo(Folder * folder,FolderItem * dest,MsgInfo * msginfo,gboolean remove_source)1471 static gint imap_add_msg_msginfo(Folder *folder, FolderItem *dest,
1472 				 MsgInfo *msginfo, gboolean remove_source)
1473 {
1474 	GSList msglist;
1475 
1476 	g_return_val_if_fail(msginfo != NULL, -1);
1477 
1478 	msglist.data = msginfo;
1479 	msglist.next = NULL;
1480 
1481 	return imap_add_msgs_msginfo(folder, dest, &msglist, remove_source,
1482 				     NULL);
1483 }
1484 
imap_add_msgs_msginfo(Folder * folder,FolderItem * dest,GSList * msglist,gboolean remove_source,gint * first)1485 static gint imap_add_msgs_msginfo(Folder *folder, FolderItem *dest,
1486 				  GSList *msglist, gboolean remove_source,
1487 				  gint *first)
1488 {
1489 	GSList *file_list;
1490 	gint ret;
1491 
1492 	file_list = procmsg_get_message_file_list(msglist);
1493 	g_return_val_if_fail(file_list != NULL, -1);
1494 
1495 	ret = imap_add_msgs(folder, dest, file_list, remove_source, first);
1496 
1497 	procmsg_message_file_list_free(file_list);
1498 
1499 	return ret;
1500 }
1501 
imap_do_copy_msgs(Folder * folder,FolderItem * dest,GSList * msglist,gboolean remove_source)1502 static gint imap_do_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist,
1503 			      gboolean remove_source)
1504 {
1505 	FolderItem *src;
1506 	gchar *destdir;
1507 	GSList *seq_list, *cur;
1508 	MsgInfo *msginfo;
1509 	IMAPSession *session;
1510 	gint count = 0, total;
1511 	gint ok = IMAP_SUCCESS;
1512 
1513 	g_return_val_if_fail(folder != NULL, -1);
1514 	g_return_val_if_fail(dest != NULL, -1);
1515 	g_return_val_if_fail(msglist != NULL, -1);
1516 
1517 	session = imap_session_get(folder);
1518 	if (!session) return -1;
1519 
1520 	ui_update();
1521 
1522 	msginfo = (MsgInfo *)msglist->data;
1523 
1524 	src = msginfo->folder;
1525 	if (src == dest) {
1526 		g_warning("the src folder is identical to the dest.\n");
1527 		return -1;
1528 	}
1529 
1530 	ok = imap_select(session, IMAP_FOLDER(folder), src->path,
1531 			 NULL, NULL, NULL, NULL);
1532 	if (ok != IMAP_SUCCESS)
1533 		return ok;
1534 
1535 	destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
1536 
1537 	total = g_slist_length(msglist);
1538 	seq_list = imap_get_seq_set_from_msglist(msglist, IMAP_COPY_LIMIT);
1539 
1540 	for (cur = seq_list; cur != NULL; cur = cur->next) {
1541 		gchar *seq_set = (gchar *)cur->data;
1542 
1543 		count += imap_seq_set_get_count(seq_set);
1544 
1545 		if (remove_source) {
1546 			status_print(_("Moving messages %s to %s ..."),
1547 				     seq_set, dest->path);
1548 			debug_print("Moving message %s/[%s] to %s ...\n",
1549 				    src->path, seq_set, dest->path);
1550 		} else {
1551 			status_print(_("Copying messages %s to %s ..."),
1552 				     seq_set, dest->path);
1553 			debug_print("Copying message %s/[%s] to %s ...\n",
1554 				    src->path, seq_set, dest->path);
1555 		}
1556 		progress_show(count, total);
1557 		ui_update();
1558 
1559 		ok = imap_cmd_copy(session, seq_set, destdir);
1560 		if (ok != IMAP_SUCCESS) {
1561 			imap_seq_set_free(seq_list);
1562 			progress_show(0, 0);
1563 			return -1;
1564 		}
1565 	}
1566 
1567 	progress_show(0, 0);
1568 
1569 	dest->updated = TRUE;
1570 
1571 	imap_seq_set_free(seq_list);
1572 	g_free(destdir);
1573 
1574 	for (cur = msglist; cur != NULL; cur = cur->next) {
1575 		msginfo = (MsgInfo *)cur->data;
1576 
1577 		if (syl_app_get())
1578 			g_signal_emit_by_name(syl_app_get(), "add-msg", dest, NULL, 0);
1579 
1580 		dest->total++;
1581 		if (MSG_IS_NEW(msginfo->flags))
1582 			dest->new++;
1583 		if (MSG_IS_UNREAD(msginfo->flags))
1584 			dest->unread++;
1585 	}
1586 
1587 	if (remove_source) {
1588 		ok = imap_remove_msgs(folder, src, msglist);
1589 		if (ok != IMAP_SUCCESS)
1590 			return ok;
1591 	}
1592 
1593 	if (ok == IMAP_SUCCESS)
1594 		return 0;
1595 	else
1596 		return -1;
1597 }
1598 
imap_move_msg(Folder * folder,FolderItem * dest,MsgInfo * msginfo)1599 static gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1600 {
1601 	GSList msglist;
1602 
1603 	g_return_val_if_fail(msginfo != NULL, -1);
1604 
1605 	msglist.data = msginfo;
1606 	msglist.next = NULL;
1607 
1608 	return imap_move_msgs(folder, dest, &msglist);
1609 }
1610 
imap_move_msgs(Folder * folder,FolderItem * dest,GSList * msglist)1611 static gint imap_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
1612 {
1613 	MsgInfo *msginfo;
1614 	GSList *file_list;
1615 	gint ret = 0;
1616 
1617 	g_return_val_if_fail(folder != NULL, -1);
1618 	g_return_val_if_fail(dest != NULL, -1);
1619 	g_return_val_if_fail(msglist != NULL, -1);
1620 
1621 	msginfo = (MsgInfo *)msglist->data;
1622 	g_return_val_if_fail(msginfo->folder != NULL, -1);
1623 
1624 	if (folder == msginfo->folder->folder)
1625 		return imap_do_copy_msgs(folder, dest, msglist, TRUE);
1626 
1627 	file_list = procmsg_get_message_file_list(msglist);
1628 	g_return_val_if_fail(file_list != NULL, -1);
1629 
1630 	ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL);
1631 
1632 	procmsg_message_file_list_free(file_list);
1633 
1634 	if (ret != -1)
1635 		ret = folder_item_remove_msgs(msginfo->folder, msglist);
1636 
1637 	return ret;
1638 }
1639 
imap_copy_msg(Folder * folder,FolderItem * dest,MsgInfo * msginfo)1640 static gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1641 {
1642 	GSList msglist;
1643 
1644 	g_return_val_if_fail(msginfo != NULL, -1);
1645 
1646 	msglist.data = msginfo;
1647 	msglist.next = NULL;
1648 
1649 	return imap_copy_msgs(folder, dest, &msglist);
1650 }
1651 
imap_copy_msgs(Folder * folder,FolderItem * dest,GSList * msglist)1652 static gint imap_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
1653 {
1654 	MsgInfo *msginfo;
1655 	GSList *file_list;
1656 	gint ret;
1657 
1658 	g_return_val_if_fail(folder != NULL, -1);
1659 	g_return_val_if_fail(dest != NULL, -1);
1660 	g_return_val_if_fail(msglist != NULL, -1);
1661 
1662 	msginfo = (MsgInfo *)msglist->data;
1663 	g_return_val_if_fail(msginfo->folder != NULL, -1);
1664 
1665 	if (folder == msginfo->folder->folder)
1666 		return imap_do_copy_msgs(folder, dest, msglist, FALSE);
1667 
1668 	file_list = procmsg_get_message_file_list(msglist);
1669 	g_return_val_if_fail(file_list != NULL, -1);
1670 
1671 	ret = imap_add_msgs(folder, dest, file_list, FALSE, NULL);
1672 
1673 	procmsg_message_file_list_free(file_list);
1674 
1675 	return ret;
1676 }
1677 
imap_remove_msgs_by_seq_set(Folder * folder,FolderItem * item,GSList * seq_list)1678 static gint imap_remove_msgs_by_seq_set(Folder *folder, FolderItem *item,
1679 					GSList *seq_list)
1680 {
1681 	gint ok;
1682 	IMAPSession *session;
1683 	GSList *cur;
1684 
1685 	g_return_val_if_fail(seq_list != NULL, -1);
1686 
1687 	session = imap_session_get(folder);
1688 	if (!session) return -1;
1689 
1690 	for (cur = seq_list; cur != NULL; cur = cur->next) {
1691 		gchar *seq_set = (gchar *)cur->data;
1692 
1693 		status_print(_("Removing messages %s"), seq_set);
1694 		ui_update();
1695 
1696 		ok = imap_set_message_flags(session, seq_set, IMAP_FLAG_DELETED,
1697 					    TRUE);
1698 		if (ok != IMAP_SUCCESS) {
1699 			log_warning(_("can't set deleted flags: %s\n"),
1700 				    seq_set);
1701 			return ok;
1702 		}
1703 	}
1704 
1705 	ok = imap_cmd_expunge(session);
1706 	if (ok != IMAP_SUCCESS) {
1707 		log_warning(_("can't expunge\n"));
1708 	} else {
1709 		/* for some broken IMAP servers */
1710 		ok = imap_cmd_noop(session);
1711 	}
1712 
1713 	item->updated = TRUE;
1714 
1715 	return ok;
1716 }
1717 
imap_remove_msg(Folder * folder,FolderItem * item,MsgInfo * msginfo)1718 static gint imap_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1719 {
1720 	GSList msglist;
1721 
1722 	g_return_val_if_fail(msginfo != NULL, -1);
1723 
1724 	msglist.data = msginfo;
1725 	msglist.next = NULL;
1726 
1727 	return imap_remove_msgs(folder, item, &msglist);
1728 }
1729 
imap_remove_msgs(Folder * folder,FolderItem * item,GSList * msglist)1730 static gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist)
1731 {
1732 	gint ok;
1733 	IMAPSession *session;
1734 	GSList *seq_list, *cur;
1735 	gchar *dir;
1736 	gboolean dir_exist;
1737 
1738 	g_return_val_if_fail(folder != NULL, -1);
1739 	g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
1740 	g_return_val_if_fail(item != NULL, -1);
1741 	g_return_val_if_fail(msglist != NULL, -1);
1742 
1743 	session = imap_session_get(folder);
1744 	if (!session) return -1;
1745 
1746 	ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1747 			 NULL, NULL, NULL, NULL);
1748 	if (ok != IMAP_SUCCESS)
1749 		return ok;
1750 
1751 	seq_list = imap_get_seq_set_from_msglist(msglist, 0);
1752 	ok = imap_remove_msgs_by_seq_set(folder, item, seq_list);
1753 	imap_seq_set_free(seq_list);
1754 	if (ok != IMAP_SUCCESS)
1755 		return ok;
1756 
1757 	dir = folder_item_get_path(item);
1758 	dir_exist = is_dir_exist(dir);
1759 	for (cur = msglist; cur != NULL; cur = cur->next) {
1760 		MsgInfo *msginfo = (MsgInfo *)cur->data;
1761 		guint32 uid = msginfo->msgnum;
1762 
1763 		if (syl_app_get())
1764 			g_signal_emit_by_name(syl_app_get(), "remove-msg", item, NULL, uid);
1765 
1766 		if (dir_exist)
1767 			remove_numbered_files(dir, uid, uid);
1768 		item->total--;
1769 		if (MSG_IS_NEW(msginfo->flags))
1770 			item->new--;
1771 		if (MSG_IS_UNREAD(msginfo->flags))
1772 			item->unread--;
1773 		MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
1774 	}
1775 	g_free(dir);
1776 
1777 	return IMAP_SUCCESS;
1778 }
1779 
imap_remove_all_msg(Folder * folder,FolderItem * item)1780 static gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1781 {
1782 	gint ok;
1783 	IMAPSession *session;
1784 	gchar *dir;
1785 
1786 	g_return_val_if_fail(folder != NULL, -1);
1787 	g_return_val_if_fail(item != NULL, -1);
1788 
1789 	session = imap_session_get(folder);
1790 	if (!session) return -1;
1791 
1792 	ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1793 			 NULL, NULL, NULL, NULL);
1794 	if (ok != IMAP_SUCCESS)
1795 		return ok;
1796 
1797 	status_print(_("Removing all messages in %s"), item->path);
1798 	ui_update();
1799 
1800 	ok = imap_cmd_gen_send(session, "STORE 1:* +FLAGS.SILENT (\\Deleted)");
1801 	if (ok != IMAP_SUCCESS) {
1802 		log_warning(_("can't set deleted flags: 1:*\n"));
1803 		return ok;
1804 	}
1805 	ok = imap_cmd_ok(session, NULL);
1806 	if (ok != IMAP_SUCCESS) {
1807 		log_warning(_("can't set deleted flags: 1:*\n"));
1808 		return ok;
1809 	}
1810 
1811 	ok = imap_cmd_expunge(session);
1812 	if (ok != IMAP_SUCCESS) {
1813 		log_warning(_("can't expunge\n"));
1814 		return ok;
1815 	}
1816 
1817 	if (syl_app_get())
1818 		g_signal_emit_by_name(syl_app_get(), "remove-all-msg", item);
1819 
1820 	item->new = item->unread = item->total = 0;
1821 	item->updated = TRUE;
1822 
1823 	dir = folder_item_get_path(item);
1824 	if (is_dir_exist(dir))
1825 		remove_all_numbered_files(dir);
1826 	g_free(dir);
1827 
1828 	return IMAP_SUCCESS;
1829 }
1830 
imap_is_msg_changed(Folder * folder,FolderItem * item,MsgInfo * msginfo)1831 static gboolean imap_is_msg_changed(Folder *folder, FolderItem *item,
1832 				    MsgInfo *msginfo)
1833 {
1834 	/* TODO: properly implement this method */
1835 	return FALSE;
1836 }
1837 
imap_close(Folder * folder,FolderItem * item)1838 static gint imap_close(Folder *folder, FolderItem *item)
1839 {
1840 	gint ok;
1841 	IMAPSession *session;
1842 
1843 	g_return_val_if_fail(folder != NULL, -1);
1844 
1845 	if (!item->path) return 0;
1846 
1847 	if (!REMOTE_FOLDER(folder)->session)
1848 		return 0;
1849 
1850 	session = imap_session_get(folder);
1851 	if (!session) return -1;
1852 
1853 	if (session->mbox) {
1854 		if (strcmp2(session->mbox, item->path) != 0) return -1;
1855 
1856 		ok = imap_cmd_close(session);
1857 		if (ok != IMAP_SUCCESS)
1858 			log_warning(_("can't close folder\n"));
1859 
1860 		g_free(session->mbox);
1861 		session->mbox = NULL;
1862 
1863 		return ok;
1864 	} else
1865 		return 0;
1866 }
1867 
imap_scan_folder(Folder * folder,FolderItem * item)1868 static gint imap_scan_folder(Folder *folder, FolderItem *item)
1869 {
1870 	IMAPSession *session;
1871 	gint messages, recent, unseen;
1872 	guint32 uid_next, uid_validity;
1873 	gint ok;
1874 
1875 	g_return_val_if_fail(folder != NULL, -1);
1876 	g_return_val_if_fail(item != NULL, -1);
1877 
1878 	session = imap_session_get(folder);
1879 	if (!session) return -1;
1880 
1881 	ok = imap_status(session, IMAP_FOLDER(folder), item->path,
1882 			 &messages, &recent, &uid_next, &uid_validity, &unseen);
1883 	if (ok != IMAP_SUCCESS) return -1;
1884 
1885 	item->new = unseen > 0 ? recent : 0;
1886 	item->unread = unseen;
1887 	item->total = messages;
1888 	item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
1889 	/* item->mtime = uid_validity; */
1890 	item->updated = TRUE;
1891 
1892 	return 0;
1893 }
1894 
imap_scan_tree(Folder * folder)1895 static gint imap_scan_tree(Folder *folder)
1896 {
1897 	FolderItem *item = NULL;
1898 	IMAPSession *session;
1899 	gchar *root_folder = NULL;
1900 	GSList *item_list, *cur;
1901 
1902 	g_return_val_if_fail(folder != NULL, -1);
1903 	g_return_val_if_fail(folder->account != NULL, -1);
1904 
1905 	session = imap_session_get(folder);
1906 	if (!session) {
1907 		if (!folder->node) {
1908 			folder_tree_destroy(folder);
1909 			item = folder_item_new(folder->name, NULL);
1910 			item->folder = folder;
1911 			folder->node = item->node = g_node_new(item);
1912 		}
1913 		return -1;
1914 	}
1915 
1916 	if (folder->account->imap_dir && *folder->account->imap_dir) {
1917 		gchar *real_path;
1918 		GPtrArray *argbuf;
1919 		gint ok;
1920 
1921 		Xstrdup_a(root_folder, folder->account->imap_dir, return -1);
1922 		extract_quote(root_folder, '"');
1923 		subst_char(root_folder,
1924 			   imap_get_path_separator(IMAP_FOLDER(folder),
1925 						   root_folder),
1926 			   '/');
1927 		strtailchomp(root_folder, '/');
1928 		real_path = imap_get_real_path
1929 			(IMAP_FOLDER(folder), root_folder);
1930 		debug_print("IMAP root directory: %s\n", real_path);
1931 
1932 		/* check if root directory exist */
1933 		argbuf = g_ptr_array_new();
1934 		ok = imap_cmd_list(session, NULL, real_path, argbuf);
1935 		if (ok != IMAP_SUCCESS ||
1936 		    search_array_str(argbuf, "LIST ") == NULL) {
1937 			log_warning(_("root folder %s not exist\n"), real_path);
1938 			g_ptr_array_free(argbuf, TRUE);
1939 			g_free(real_path);
1940 			return -1;
1941 		}
1942 		g_ptr_array_free(argbuf, TRUE);
1943 		g_free(real_path);
1944 	}
1945 
1946 	if (folder->node)
1947 		item = FOLDER_ITEM(folder->node->data);
1948 	if (!item || ((item->path || root_folder) &&
1949 		      strcmp2(item->path, root_folder) != 0)) {
1950 		folder_tree_destroy(folder);
1951 		item = folder_item_new(folder->name, root_folder);
1952 		item->folder = folder;
1953 		folder->node = item->node = g_node_new(item);
1954 	}
1955 
1956 	item_list = imap_get_folder_list(session, item);
1957 	imap_scan_tree_recursive(session, item, item_list);
1958 	imap_create_missing_folders(folder);
1959 
1960 	for (cur = item_list; cur != NULL; cur = cur->next)
1961 		folder_item_destroy(FOLDER_ITEM(cur->data));
1962 	g_slist_free(item_list);
1963 
1964 	return 0;
1965 }
1966 
imap_scan_tree_recursive(IMAPSession * session,FolderItem * item,GSList * item_list)1967 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item,
1968 				     GSList *item_list)
1969 {
1970 	Folder *folder;
1971 	FolderItem *new_item;
1972 	GSList *part_list, *cur;
1973 	GNode *node;
1974 
1975 	g_return_val_if_fail(item != NULL, -1);
1976 	g_return_val_if_fail(item->folder != NULL, -1);
1977 	g_return_val_if_fail(item->no_sub == FALSE, -1);
1978 
1979 	folder = item->folder;
1980 
1981 	part_list = imap_get_part_folder_list(item_list, item);
1982 
1983 	node = item->node->children;
1984 	while (node != NULL) {
1985 		FolderItem *old_item = FOLDER_ITEM(node->data);
1986 		GNode *next = node->next;
1987 
1988 		new_item = NULL;
1989 
1990 		for (cur = part_list; cur != NULL; cur = cur->next) {
1991 			FolderItem *cur_item = FOLDER_ITEM(cur->data);
1992 			if (!strcmp2(old_item->path, cur_item->path)) {
1993 				new_item = cur_item;
1994 				break;
1995 			}
1996 		}
1997 		if (!new_item) {
1998 			if (old_item->stype != F_VIRTUAL) {
1999 				debug_print("folder '%s' not found. removing...\n", old_item->path);
2000 				folder_item_remove(old_item);
2001 			}
2002 		} else if (old_item->stype == F_VIRTUAL) {
2003 				debug_print("IMAP4 folder found at the location of virtual folder '%s'. removing virtual folder...\n", old_item->path);
2004 				virtual_get_class()->remove_folder
2005 					(folder, old_item);
2006 		} else {
2007 			old_item->no_sub = new_item->no_sub;
2008 			old_item->no_select = new_item->no_select;
2009 			if (old_item->no_select == TRUE)
2010 				old_item->new = old_item->unread =
2011 					old_item->total = 0;
2012 			if (old_item->no_sub == TRUE && node->children) {
2013 				debug_print("folder '%s' doesn't have "
2014 					    "subfolders. removing...\n",
2015 					    old_item->path);
2016 				folder_item_remove_children(old_item);
2017 			}
2018 		}
2019 
2020 		node = next;
2021 	}
2022 
2023 	for (cur = part_list; cur != NULL; cur = cur->next) {
2024 		FolderItem *cur_item = FOLDER_ITEM(cur->data);
2025 		new_item = NULL;
2026 		for (node = item->node->children; node != NULL;
2027 		     node = node->next) {
2028 			if (!strcmp2(FOLDER_ITEM(node->data)->path,
2029 				     cur_item->path)) {
2030 				new_item = FOLDER_ITEM(node->data);
2031 				cur_item = NULL;
2032 				break;
2033 			}
2034 		}
2035 		if (!new_item) {
2036 			new_item = folder_item_copy(cur_item);
2037 			debug_print("new folder '%s' found.\n", new_item->path);
2038 			folder_item_append(item, new_item);
2039 		}
2040 
2041 		if (!g_ascii_strcasecmp(new_item->path, "INBOX")) {
2042 			new_item->stype = F_INBOX;
2043 			folder->inbox = new_item;
2044 		} else if (!item->parent || item->stype == F_INBOX) {
2045 			const gchar *base;
2046 
2047 			base = g_basename(new_item->path);
2048 
2049 			if (!folder->outbox &&
2050 			    !g_ascii_strcasecmp(base, "Sent")) {
2051 				new_item->stype = F_OUTBOX;
2052 				folder->outbox = new_item;
2053 			} else if (!folder->draft &&
2054 				   !g_ascii_strcasecmp(base, "Drafts")) {
2055 				new_item->stype = F_DRAFT;
2056 				folder->draft = new_item;
2057 			} else if (!folder->queue &&
2058 				   !g_ascii_strcasecmp(base, "Queue")) {
2059 				new_item->stype = F_QUEUE;
2060 				folder->queue = new_item;
2061 			} else if (!folder->trash &&
2062 				   !g_ascii_strcasecmp(base, "Trash")) {
2063 				new_item->stype = F_TRASH;
2064 				folder->trash = new_item;
2065 			}
2066 		}
2067 
2068 #if 0
2069 		if (new_item->no_select == FALSE)
2070 			imap_scan_folder(folder, new_item);
2071 #endif
2072 		if (new_item->no_sub == FALSE)
2073 			imap_scan_tree_recursive(session, new_item, item_list);
2074 	}
2075 
2076 	g_slist_free(part_list);
2077 
2078 	return IMAP_SUCCESS;
2079 }
2080 
imap_get_folder_list(IMAPSession * session,FolderItem * item)2081 static GSList *imap_get_folder_list(IMAPSession *session, FolderItem *item)
2082 {
2083 	Folder *folder;
2084 	IMAPFolder *imapfolder;
2085 	gchar *real_path;
2086 	gchar *wildcard_path;
2087 	gchar separator;
2088 	GSList *item_list = NULL;
2089 
2090 	folder = item->folder;
2091 	imapfolder = IMAP_FOLDER(folder);
2092 
2093 	separator = imap_get_path_separator(imapfolder, item->path);
2094 
2095 	if (folder->ui_func)
2096 		folder->ui_func(folder, item, folder->ui_func_data);
2097 
2098 	if (item->path) {
2099 		real_path = imap_get_real_path(imapfolder, item->path);
2100 		strtailchomp(real_path, separator);
2101 		wildcard_path = g_strdup_printf("%s%c*", real_path, separator);
2102 	} else {
2103 		real_path = g_strdup("");
2104 		wildcard_path = g_strdup("*");
2105 	}
2106 
2107 	if (imap_cmd_gen_send(session, "LIST \"\" \"%s\"", wildcard_path) == IMAP_SUCCESS) {
2108 		item_list = imap_parse_list(session, real_path, NULL);
2109 		item_list = imap_add_inter_folders(item_list, item->path);
2110 	}
2111 	g_free(real_path);
2112 	g_free(wildcard_path);
2113 
2114 	return item_list;
2115 }
2116 
imap_parse_list(IMAPSession * session,const gchar * real_path,gchar * separator)2117 static GSList *imap_parse_list(IMAPSession *session, const gchar *real_path,
2118 			       gchar *separator)
2119 {
2120 	gchar buf[IMAPBUFSIZE];
2121 	gchar flags[256];
2122 	gchar separator_str[16];
2123 	gchar *p;
2124 	const gchar *name;
2125 	gchar *loc_name, *loc_path;
2126 	GSList *item_list = NULL;
2127 	GString *str;
2128 	FolderItem *new_item;
2129 
2130 	debug_print("getting list of %s ...\n",
2131 		    *real_path ? real_path : "\"\"");
2132 
2133 	str = g_string_new(NULL);
2134 
2135 	for (;;) {
2136 		if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
2137 			log_warning(_("error occurred while getting LIST.\n"));
2138 			break;
2139 		}
2140 		strretchomp(buf);
2141 		if (buf[0] != '*' || buf[1] != ' ') {
2142 			log_print("IMAP4< %s\n", buf);
2143 			if (sscanf(buf, "%*d %16s", buf) < 1 ||
2144 			    strcmp(buf, "OK") != 0)
2145 				log_warning(_("error occurred while getting LIST.\n"));
2146 
2147 			break;
2148 		}
2149 		debug_print("IMAP4< %s\n", buf);
2150 
2151 		g_string_assign(str, buf);
2152 		p = str->str + 2;
2153 		if (strncmp(p, "LIST ", 5) != 0) continue;
2154 		p += 5;
2155 
2156 		if (*p != '(') continue;
2157 		p++;
2158 		p = strchr_cpy(p, ')', flags, sizeof(flags));
2159 		if (!p) continue;
2160 		while (*p == ' ') p++;
2161 
2162 		p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
2163 		if (!p) continue;
2164 		extract_quote(separator_str, '"');
2165 		if (!strcmp(separator_str, "NIL"))
2166 			separator_str[0] = '\0';
2167 		if (separator)
2168 			*separator = separator_str[0];
2169 
2170 		buf[0] = '\0';
2171 		while (*p == ' ') p++;
2172 		if ((*p == '~' && *(p + 1) == '{') || *p == '{' || *p == '"')
2173 			p = imap_parse_atom(session, p, buf, sizeof(buf), str);
2174 		else
2175 			strncpy2(buf, p, sizeof(buf));
2176 		strtailchomp(buf, separator_str[0]);
2177 		if (buf[0] == '\0') continue;
2178 		if (!strcmp(buf, real_path)) continue;
2179 
2180 		if (separator_str[0] != '\0')
2181 			subst_char(buf, separator_str[0], '/');
2182 		name = g_basename(buf);
2183 		if (name[0] == '.') continue;
2184 
2185 		loc_name = imap_modified_utf7_to_utf8(name);
2186 		loc_path = imap_modified_utf7_to_utf8(buf);
2187 		new_item = folder_item_new(loc_name, loc_path);
2188 		if (strcasestr(flags, "\\Noinferiors") != NULL)
2189 			new_item->no_sub = TRUE;
2190 		if (g_ascii_strcasecmp(buf, "INBOX") != 0 &&
2191 		    strcasestr(flags, "\\Noselect") != NULL)
2192 			new_item->no_select = TRUE;
2193 
2194 		item_list = g_slist_prepend(item_list, new_item);
2195 
2196 		debug_print("folder '%s' found.\n", loc_path);
2197 		g_free(loc_path);
2198 		g_free(loc_name);
2199 	}
2200 
2201 	g_string_free(str, TRUE);
2202 
2203 	item_list = g_slist_reverse(item_list);
2204 	return item_list;
2205 }
2206 
imap_add_inter_folders(GSList * item_list,const gchar * root_path)2207 static GSList *imap_add_inter_folders(GSList *item_list, const gchar *root_path)
2208 {
2209 	FolderItem *item;
2210 	GSList *cur;
2211 	GSList *add_list = NULL;
2212 	GHashTable *exist;
2213 	const gchar *p;
2214 	gint root_path_len = 0;
2215 
2216 	if (root_path)
2217 		root_path_len = strlen(root_path);
2218 
2219 	exist = g_hash_table_new(g_str_hash, g_str_equal);
2220 
2221 	for (cur = item_list; cur != NULL; cur = cur->next) {
2222 		item = FOLDER_ITEM(cur->data);
2223 
2224 		if (root_path_len > 0 &&
2225 		    strncmp(root_path, item->path, root_path_len) != 0)
2226 			continue;
2227 		p = item->path + root_path_len;
2228 		if (root_path_len > 0 && *p != '/') continue;
2229 		while (*p == '/') p++;
2230 		if (*p == '\0') continue;
2231 		g_hash_table_insert(exist, (gpointer)p, GINT_TO_POINTER(1));
2232 	}
2233 
2234 	for (cur = item_list; cur != NULL; cur = cur->next) {
2235 		const gchar *q, *r;
2236 		gchar *parent, *full_parent;
2237 		FolderItem *new_item;
2238 
2239 		item = FOLDER_ITEM(cur->data);
2240 
2241 		if (root_path_len > 0 &&
2242 		    strncmp(root_path, item->path, root_path_len) != 0)
2243 			continue;
2244 		p = item->path + root_path_len;
2245 		if (root_path_len > 0 && *p != '/') continue;
2246 		while (*p == '/') p++;
2247 		if (*p == '\0') continue;
2248 
2249 		q = p;
2250 		while ((q = strchr(q, '/')) != NULL) {
2251 			parent = g_strndup(p, q - p);
2252 			if (!g_hash_table_lookup(exist, parent)) {
2253 				if (root_path_len > 0)
2254 					full_parent = g_strconcat
2255 						(root_path, "/", parent, NULL);
2256 				else
2257 					full_parent = g_strdup(parent);
2258 				new_item = folder_item_new(g_basename(parent),
2259 							   full_parent);
2260 				new_item->no_select = TRUE;
2261 				add_list = g_slist_prepend(add_list, new_item);
2262 				r = new_item->path + root_path_len;
2263 				while (*r == '/') r++;
2264 				g_hash_table_insert(exist, (gpointer)r,
2265 						    GINT_TO_POINTER(1));
2266 				debug_print("intermediate folder '%s' added\n",
2267 					    full_parent);
2268 				g_free(full_parent);
2269 			}
2270 			g_free(parent);
2271 			while (*q == '/') q++;
2272 		}
2273 	}
2274 
2275 	g_hash_table_destroy(exist);
2276 
2277 	add_list = g_slist_reverse(add_list);
2278 	item_list = g_slist_concat(item_list, add_list);
2279 
2280 	return item_list;
2281 }
2282 
imap_get_part_folder_list(GSList * item_list,FolderItem * item)2283 static GSList *imap_get_part_folder_list(GSList *item_list, FolderItem *item)
2284 {
2285 	FolderItem *cur_item;
2286 	GSList *part_list = NULL, *cur;
2287 	gint len;
2288 
2289 	if (!item->path) {
2290 		debug_print("imap_get_part_folder_list(): get root folders\n");
2291 		for (cur = item_list; cur != NULL; cur = cur->next) {
2292 			cur_item = FOLDER_ITEM(cur->data);
2293 
2294 			if (!strchr(cur_item->path, '/')) {
2295 				part_list = g_slist_prepend(part_list,
2296 							    cur_item);
2297 				debug_print("append '%s'\n", cur_item->path);
2298 			}
2299 		}
2300 		part_list = g_slist_reverse(part_list);
2301 		return part_list;
2302 	}
2303 
2304 	len = strlen(item->path);
2305 	debug_print("imap_get_part_folder_list(): get folders under '%s'\n",
2306 		    item->path);
2307 
2308 	for (cur = item_list; cur != NULL; cur = cur->next) {
2309 		cur_item = FOLDER_ITEM(cur->data);
2310 
2311 		if (!strncmp(cur_item->path, item->path, len) &&
2312 		    cur_item->path[len] == '/' &&
2313 		    !strchr(cur_item->path + len + 1, '/')) {
2314 			part_list = g_slist_prepend(part_list, cur_item);
2315 			debug_print("append '%s'\n", cur_item->path);
2316 		}
2317 	}
2318 
2319 	part_list = g_slist_reverse(part_list);
2320 	return part_list;
2321 }
2322 
imap_create_tree(Folder * folder)2323 static gint imap_create_tree(Folder *folder)
2324 {
2325 	g_return_val_if_fail(folder != NULL, -1);
2326 	g_return_val_if_fail(folder->node != NULL, -1);
2327 	g_return_val_if_fail(folder->node->data != NULL, -1);
2328 	g_return_val_if_fail(folder->account != NULL, -1);
2329 
2330 	if (imap_scan_tree(folder) < 0)
2331 		return -1;
2332 	imap_create_missing_folders(folder);
2333 
2334 	return 0;
2335 }
2336 
imap_create_missing_folders(Folder * folder)2337 static void imap_create_missing_folders(Folder *folder)
2338 {
2339 	g_return_if_fail(folder != NULL);
2340 
2341 	if (!folder->inbox)
2342 		folder->inbox = imap_create_special_folder
2343 			(folder, F_INBOX, "INBOX");
2344 #if 0
2345 	if (!folder->outbox)
2346 		folder->outbox = imap_create_special_folder
2347 			(folder, F_OUTBOX, "Sent");
2348 	if (!folder->draft)
2349 		folder->draft = imap_create_special_folder
2350 			(folder, F_DRAFT, "Drafts");
2351 	if (!folder->queue)
2352 		folder->queue = imap_create_special_folder
2353 			(folder, F_QUEUE, "Queue");
2354 #endif
2355 	if (!folder->trash)
2356 		folder->trash = imap_create_special_folder
2357 			(folder, F_TRASH, "Trash");
2358 }
2359 
imap_create_special_folder(Folder * folder,SpecialFolderItemType stype,const gchar * name)2360 static FolderItem *imap_create_special_folder(Folder *folder,
2361 					      SpecialFolderItemType stype,
2362 					      const gchar *name)
2363 {
2364 	FolderItem *item;
2365 	FolderItem *new_item;
2366 
2367 	g_return_val_if_fail(folder != NULL, NULL);
2368 	g_return_val_if_fail(folder->node != NULL, NULL);
2369 	g_return_val_if_fail(folder->node->data != NULL, NULL);
2370 	g_return_val_if_fail(folder->account != NULL, NULL);
2371 	g_return_val_if_fail(name != NULL, NULL);
2372 
2373 	item = FOLDER_ITEM(folder->node->data);
2374 	new_item = imap_create_folder(folder, item, name);
2375 
2376 	if (!new_item) {
2377 		g_warning(_("Can't create '%s'\n"), name);
2378 		if (!folder->inbox) return NULL;
2379 
2380 		new_item = imap_create_folder(folder, folder->inbox, name);
2381 		if (!new_item)
2382 			g_warning(_("Can't create '%s' under INBOX\n"), name);
2383 		else
2384 			new_item->stype = stype;
2385 	} else
2386 		new_item->stype = stype;
2387 
2388 	return new_item;
2389 }
2390 
imap_create_folder(Folder * folder,FolderItem * parent,const gchar * name)2391 static FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
2392 				      const gchar *name)
2393 {
2394 	gchar *dirpath, *imap_path;
2395 	IMAPSession *session;
2396 	FolderItem *new_item;
2397 	gchar separator;
2398 	gchar *new_name;
2399 	const gchar *p;
2400 	gboolean no_sub = FALSE, no_select = FALSE;
2401 	gint ok;
2402 
2403 	g_return_val_if_fail(folder != NULL, NULL);
2404 	g_return_val_if_fail(folder->account != NULL, NULL);
2405 	g_return_val_if_fail(parent != NULL, NULL);
2406 	g_return_val_if_fail(name != NULL, NULL);
2407 
2408 	session = imap_session_get(folder);
2409 	if (!session) return NULL;
2410 
2411 	if (!parent->parent && g_ascii_strcasecmp(name, "INBOX") == 0)
2412 		dirpath = g_strdup(name);
2413 	else if (parent->path)
2414 		dirpath = g_strconcat(parent->path, "/", name, NULL);
2415 	else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
2416 		dirpath = g_strdup(name);
2417 	else if (folder->account->imap_dir && *folder->account->imap_dir) {
2418 		gchar *imap_dir;
2419 
2420 		Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
2421 		strtailchomp(imap_dir, '/');
2422 		dirpath = g_strconcat(imap_dir, "/", name, NULL);
2423 	} else
2424 		dirpath = g_strdup(name);
2425 
2426 	/* keep trailing directory separator to create a folder that contains
2427 	   sub folder */
2428 	imap_path = imap_utf8_to_modified_utf7(dirpath);
2429 	strtailchomp(dirpath, '/');
2430 	Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
2431 	strtailchomp(new_name, '/');
2432 	separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
2433 	imap_path_separator_subst(imap_path, separator);
2434 	subst_char(new_name, '/', separator);
2435 
2436 	if (g_ascii_strcasecmp(name, "INBOX") != 0) {
2437 		GPtrArray *argbuf;
2438 		gint i;
2439 		gboolean exist = FALSE;
2440 
2441 		argbuf = g_ptr_array_new();
2442 		ok = imap_cmd_list(session, NULL, imap_path, argbuf);
2443 		if (ok != IMAP_SUCCESS) {
2444 			log_warning(_("can't create mailbox: LIST failed\n"));
2445 			g_free(imap_path);
2446 			g_free(dirpath);
2447 			g_ptr_array_free(argbuf, TRUE);
2448 			return NULL;
2449 		}
2450 
2451 		for (i = 0; i < argbuf->len; i++) {
2452 			gchar *str;
2453 			str = g_ptr_array_index(argbuf, i);
2454 			if (!strncmp(str, "LIST ", 5)) {
2455 				exist = TRUE;
2456 				if (strcasestr(str + 5, "\\Noinferiors"))
2457 					no_sub = TRUE;
2458 				if (strcasestr(str + 5, "\\Noselect"))
2459 					no_select = TRUE;
2460 				break;
2461 			}
2462 		}
2463 
2464 		g_ptr_array_free(argbuf, TRUE);
2465 		argbuf = NULL;
2466 
2467 		if (!exist) {
2468 			ok = imap_cmd_create(session, imap_path);
2469 			if (ok != IMAP_SUCCESS) {
2470 				log_warning(_("can't create mailbox\n"));
2471 				g_free(imap_path);
2472 				g_free(dirpath);
2473 				return NULL;
2474 			}
2475 
2476 			ok = imap_cmd_subscribe(session, imap_path);
2477 			if (ok != IMAP_SUCCESS) {
2478 				log_warning(_("can't subscribe mailbox\n"));
2479 			}
2480 
2481 			argbuf = g_ptr_array_new();
2482 			ok = imap_cmd_list(session, NULL, imap_path, argbuf);
2483 			if (ok != IMAP_SUCCESS) {
2484 				log_warning("LIST failed\n");
2485 				g_free(imap_path);
2486 				g_free(dirpath);
2487 				g_ptr_array_free(argbuf, TRUE);
2488 				return NULL;
2489 			}
2490 
2491 			for (i = 0; i < argbuf->len; i++) {
2492 				gchar *str;
2493 				str = g_ptr_array_index(argbuf, i);
2494 				if (!strncmp(str, "LIST ", 5)) {
2495 					if (strcasestr(str + 5, "\\Noinferiors"))
2496 						no_sub = TRUE;
2497 					if (strcasestr(str + 5, "\\Noselect"))
2498 						no_select = TRUE;
2499 					break;
2500 				}
2501 			}
2502 			g_ptr_array_free(argbuf, TRUE);
2503 		}
2504 
2505 	}
2506 
2507 	new_item = folder_item_new(new_name, dirpath);
2508 	new_item->no_sub = no_sub;
2509 	new_item->no_select = no_select;
2510 	folder_item_append(parent, new_item);
2511 	g_free(imap_path);
2512 	g_free(dirpath);
2513 
2514 	dirpath = folder_item_get_path(new_item);
2515 	if (!is_dir_exist(dirpath))
2516 		make_dir_hier(dirpath);
2517 	g_free(dirpath);
2518 
2519 	return new_item;
2520 }
2521 
imap_rename_folder_real(Folder * folder,FolderItem * item,FolderItem * new_parent,const gchar * name)2522 static gint imap_rename_folder_real(Folder *folder, FolderItem *item,
2523 				    FolderItem *new_parent, const gchar *name)
2524 {
2525 	gchar *newpath;
2526 	gchar *real_oldpath;
2527 	gchar *real_newpath;
2528 	gchar *paths[2];
2529 	gchar *old_cache_dir;
2530 	gchar *new_cache_dir;
2531 	IMAPSession *session;
2532 	gchar separator;
2533 	gint ok;
2534 	gint exists, recent, unseen;
2535 	guint32 uid_validity;
2536 	gchar *old_id, *new_id;
2537 
2538 	g_return_val_if_fail(folder != NULL, -1);
2539 	g_return_val_if_fail(item != NULL, -1);
2540 	g_return_val_if_fail(folder == item->folder, -1);
2541 	g_return_val_if_fail(item->path != NULL, -1);
2542 	g_return_val_if_fail(new_parent != NULL || name != NULL, -1);
2543 	if (new_parent) {
2544 		g_return_val_if_fail(item != new_parent, -1);
2545 		g_return_val_if_fail(item->parent != new_parent, -1);
2546 		g_return_val_if_fail(item->folder == new_parent->folder, -1);
2547 		if (g_node_is_ancestor(item->node, new_parent->node)) {
2548 			g_warning("folder to be moved is ancestor of new parent\n");
2549 			return -1;
2550 		}
2551 	}
2552 
2553 	session = imap_session_get(folder);
2554 	if (!session) return -1;
2555 
2556 	real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2557 
2558 	g_free(session->mbox);
2559 	session->mbox = NULL;
2560 	ok = imap_cmd_examine(session, "INBOX",
2561 			      &exists, &recent, &unseen, &uid_validity);
2562 	if (ok != IMAP_SUCCESS) {
2563 		g_free(real_oldpath);
2564 		return -1;
2565 	}
2566 
2567 	separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
2568 	if (new_parent) {
2569 		if (name) {
2570 			if (new_parent->path)
2571 				newpath = g_strconcat(new_parent->path,
2572 						      "/", name, NULL);
2573 			else
2574 				newpath = g_strdup(name);
2575 		} else {
2576 			gchar *name_;
2577 
2578 			name_ = g_path_get_basename(item->path);
2579 			if (new_parent->path)
2580 				newpath = g_strconcat(new_parent->path,
2581 						      "/", name_, NULL);
2582 			else
2583 				newpath = g_strdup(name_);
2584 			AUTORELEASE_STR(name_, );
2585 			name = name_;
2586 		}
2587 	} else {
2588 		if (strchr(item->path, '/')) {
2589 			gchar *dirpath;
2590 
2591 			dirpath = g_dirname(item->path);
2592 			newpath = g_strconcat(dirpath, "/", name, NULL);
2593 			g_free(dirpath);
2594 		} else
2595 			newpath = g_strdup(name);
2596 	}
2597 
2598 	real_newpath = imap_utf8_to_modified_utf7(newpath);
2599 	imap_path_separator_subst(real_newpath, separator);
2600 
2601 	ok = imap_cmd_rename(session, real_oldpath, real_newpath);
2602 	if (ok != IMAP_SUCCESS) {
2603 		log_warning(_("can't rename mailbox: %s to %s\n"),
2604 			    real_oldpath, real_newpath);
2605 		g_free(real_oldpath);
2606 		g_free(newpath);
2607 		g_free(real_newpath);
2608 		return -1;
2609 	}
2610 
2611 	old_id = folder_item_get_identifier(item);
2612 
2613 	if (new_parent) {
2614 		g_node_unlink(item->node);
2615 		g_node_append(new_parent->node, item->node);
2616 		item->parent = new_parent;
2617 	}
2618 
2619 	g_free(item->name);
2620 	item->name = g_strdup(name);
2621 
2622 	old_cache_dir = folder_item_get_path(item);
2623 
2624 	paths[0] = g_strdup(item->path);
2625 	paths[1] = newpath;
2626 	g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
2627 			imap_rename_folder_func, paths);
2628 
2629 	if (is_dir_exist(old_cache_dir)) {
2630 		new_cache_dir = folder_item_get_path(item);
2631 		if (g_rename(old_cache_dir, new_cache_dir) < 0) {
2632 			FILE_OP_ERROR(old_cache_dir, "rename");
2633 		}
2634 		g_free(new_cache_dir);
2635 	}
2636 
2637 	g_free(old_cache_dir);
2638 	g_free(paths[0]);
2639 	g_free(newpath);
2640 	g_free(real_oldpath);
2641 	g_free(real_newpath);
2642 
2643 	new_id = folder_item_get_identifier(item);
2644 	if (syl_app_get())
2645 		g_signal_emit_by_name(syl_app_get(), "move-folder", item,
2646 				      old_id, new_id);
2647 	g_free(new_id);
2648 	g_free(old_id);
2649 
2650 	return 0;
2651 }
2652 
imap_rename_folder(Folder * folder,FolderItem * item,const gchar * name)2653 static gint imap_rename_folder(Folder *folder, FolderItem *item,
2654 			       const gchar *name)
2655 {
2656 	return imap_rename_folder_real(folder, item, NULL, name);
2657 }
2658 
imap_move_folder(Folder * folder,FolderItem * item,FolderItem * new_parent)2659 static gint imap_move_folder(Folder *folder, FolderItem *item,
2660 			     FolderItem *new_parent)
2661 {
2662 	return imap_rename_folder_real(folder, item, new_parent, NULL);
2663 }
2664 
imap_remove_folder(Folder * folder,FolderItem * item)2665 static gint imap_remove_folder(Folder *folder, FolderItem *item)
2666 {
2667 	gint ok;
2668 	IMAPSession *session;
2669 	gchar *path;
2670 	gchar *cache_dir;
2671 	gint exists, recent, unseen;
2672 	guint32 uid_validity;
2673 
2674 	g_return_val_if_fail(folder != NULL, -1);
2675 	g_return_val_if_fail(item != NULL, -1);
2676 	g_return_val_if_fail(item->path != NULL, -1);
2677 
2678 	session = imap_session_get(folder);
2679 	if (!session) return -1;
2680 
2681 	path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
2682 
2683 	ok = imap_cmd_examine(session, "INBOX",
2684 			      &exists, &recent, &unseen, &uid_validity);
2685 	if (ok != IMAP_SUCCESS) {
2686 		g_free(path);
2687 		return -1;
2688 	}
2689 
2690 	ok = imap_cmd_delete(session, path);
2691 	if (ok != IMAP_SUCCESS) {
2692 		log_warning(_("can't delete mailbox\n"));
2693 		g_free(path);
2694 		return -1;
2695 	}
2696 
2697 	g_free(path);
2698 	cache_dir = folder_item_get_path(item);
2699 	if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
2700 		g_warning("can't remove directory '%s'\n", cache_dir);
2701 	g_free(cache_dir);
2702 
2703 	if (syl_app_get())
2704 		g_signal_emit_by_name(syl_app_get(), "remove-folder", item);
2705 	folder_item_remove(item);
2706 
2707 	return 0;
2708 }
2709 
2710 typedef struct _IMAPGetData
2711 {
2712 	FolderItem *item;
2713 	gint exists;
2714 	gboolean update_count;
2715 	GSList *newlist;
2716 } IMAPGetData;
2717 
imap_get_uncached_messages_progress_func(IMAPSession * session,gint count,gint total,gpointer data)2718 static gint imap_get_uncached_messages_progress_func(IMAPSession *session,
2719 						     gint count, gint total,
2720 						     gpointer data)
2721 {
2722 	status_print(_("Getting message headers (%d / %d)"), count, total);
2723 	progress_show(count, total);
2724 #ifndef USE_THREADS
2725 	ui_update();
2726 #endif
2727 	return 0;
2728 }
2729 
imap_get_uncached_messages_func(IMAPSession * session,gpointer data)2730 static gint imap_get_uncached_messages_func(IMAPSession *session, gpointer data)
2731 {
2732 	gchar *tmp;
2733 	GSList *newlist = NULL;
2734 	GSList *llast = NULL;
2735 	GString *str;
2736 	MsgInfo *msginfo;
2737 	gint count = 1;
2738 	GTimeVal tv_prev, tv_cur;
2739 	IMAPGetData *get_data = (IMAPGetData *)data;
2740 	FolderItem *item = get_data->item;
2741 	gint exists = get_data->exists;
2742 	gboolean update_count = get_data->update_count;
2743 
2744 	g_get_current_time(&tv_prev);
2745 #ifndef USE_THREADS
2746 	ui_update();
2747 #endif
2748 
2749 #if USE_THREADS
2750 	((IMAPRealSession *)session)->prog_total = exists;
2751 #endif
2752 
2753 	str = g_string_new(NULL);
2754 
2755 	for (;;) {
2756 		if (exists > 0 && count <= exists) {
2757 			g_get_current_time(&tv_cur);
2758 			if (tv_cur.tv_sec > tv_prev.tv_sec ||
2759 			    tv_cur.tv_usec - tv_prev.tv_usec >
2760 			    PROGRESS_UPDATE_INTERVAL * 1000) {
2761 #if USE_THREADS
2762 				((IMAPRealSession *)session)->prog_count = count;
2763 				g_main_context_wakeup(NULL);
2764 #else
2765 				imap_get_uncached_messages_progress_func
2766 					(session, count, exists, data);
2767 #endif
2768 				tv_prev = tv_cur;
2769 			}
2770 		}
2771 		++count;
2772 
2773 		if (sock_getline(SESSION(session)->sock, &tmp) < 0) {
2774 			log_warning(_("error occurred while getting envelope.\n"));
2775 			g_string_free(str, TRUE);
2776 			return IMAP_SOCKET;
2777 		}
2778 		strretchomp(tmp);
2779 		if (tmp[0] != '*' || tmp[1] != ' ') {
2780 			log_print("IMAP4< %s\n", tmp);
2781 			g_free(tmp);
2782 			break;
2783 		}
2784 		if (strstr(tmp, "FETCH") == NULL) {
2785 			log_print("IMAP4< %s\n", tmp);
2786 			g_free(tmp);
2787 			continue;
2788 		}
2789 		log_print("IMAP4< %s\n", tmp);
2790 		g_string_assign(str, tmp);
2791 		g_free(tmp);
2792 
2793 		msginfo = imap_parse_envelope(session, item, str);
2794 		if (!msginfo) {
2795 			log_warning(_("can't parse envelope: %s\n"), str->str);
2796 			continue;
2797 		}
2798 		if (update_count) {
2799 			if (MSG_IS_NEW(msginfo->flags))
2800 				item->new++;
2801 			if (MSG_IS_UNREAD(msginfo->flags))
2802 				item->unread++;
2803 		}
2804 		if (item->stype == F_QUEUE) {
2805 			MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
2806 		} else if (item->stype == F_DRAFT) {
2807 			MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
2808 		}
2809 
2810 		msginfo->folder = item;
2811 
2812 		if (!newlist)
2813 			llast = newlist = g_slist_append(newlist, msginfo);
2814 		else {
2815 			llast = g_slist_append(llast, msginfo);
2816 			llast = llast->next;
2817 		}
2818 
2819 		if (update_count)
2820 			item->total++;
2821 	}
2822 
2823 	g_string_free(str, TRUE);
2824 
2825 	session_set_access_time(SESSION(session));
2826 
2827 	get_data->newlist = newlist;
2828 	return IMAP_SUCCESS;
2829 }
2830 
imap_get_uncached_messages(IMAPSession * session,FolderItem * item,guint32 first_uid,guint32 last_uid,gint exists,gboolean update_count)2831 static GSList *imap_get_uncached_messages(IMAPSession *session,
2832 					  FolderItem *item,
2833 					  guint32 first_uid, guint32 last_uid,
2834 					  gint exists, gboolean update_count)
2835 {
2836 	IMAPGetData get_data = {item, exists, update_count, NULL};
2837 	gchar seq_set[22];
2838 	gint ok;
2839 
2840 	g_return_val_if_fail(session != NULL, NULL);
2841 	g_return_val_if_fail(item != NULL, NULL);
2842 	g_return_val_if_fail(item->folder != NULL, NULL);
2843 	g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, NULL);
2844 	g_return_val_if_fail(first_uid <= last_uid, NULL);
2845 
2846 	if (first_uid == 0 && last_uid == 0)
2847 		strcpy(seq_set, "1:*");
2848 	else
2849 		g_snprintf(seq_set, sizeof(seq_set), "%u:%u",
2850 			   first_uid, last_uid);
2851 	if (imap_cmd_envelope(session, seq_set) != IMAP_SUCCESS) {
2852 		log_warning(_("can't get envelope\n"));
2853 		return NULL;
2854 	}
2855 
2856 #if USE_THREADS
2857 	ok = imap_thread_run_progress(session, imap_get_uncached_messages_func,
2858 				      imap_get_uncached_messages_progress_func,
2859 				      &get_data);
2860 #else
2861 	ok = imap_get_uncached_messages_func(session, &get_data);
2862 #endif
2863 
2864 	progress_show(0, 0);
2865 	return get_data.newlist;
2866 }
2867 
imap_delete_cached_message(FolderItem * item,guint32 uid)2868 static void imap_delete_cached_message(FolderItem *item, guint32 uid)
2869 {
2870 	gchar *dir;
2871 	gchar *file;
2872 
2873 	g_return_if_fail(item != NULL);
2874 	g_return_if_fail(item->folder != NULL);
2875 	g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2876 
2877 	dir = folder_item_get_path(item);
2878 	file = g_strdup_printf("%s%c%u", dir, G_DIR_SEPARATOR, uid);
2879 
2880 	debug_print("Deleting cached message: %s\n", file);
2881 
2882 	g_unlink(file);
2883 
2884 	g_free(file);
2885 	g_free(dir);
2886 }
2887 
imap_delete_cached_messages(GSList * mlist,FolderItem * item,guint32 first_uid,guint32 last_uid)2888 static GSList *imap_delete_cached_messages(GSList *mlist, FolderItem *item,
2889 					   guint32 first_uid, guint32 last_uid)
2890 {
2891 	GSList *cur, *next;
2892 	MsgInfo *msginfo;
2893 	gchar *dir;
2894 
2895 	g_return_val_if_fail(item != NULL, mlist);
2896 	g_return_val_if_fail(item->folder != NULL, mlist);
2897 	g_return_val_if_fail(FOLDER_TYPE(item->folder) == F_IMAP, mlist);
2898 
2899 	if (first_uid == 0 && last_uid == 0)
2900 		return mlist;
2901 
2902 	debug_print("Deleting cached messages %u - %u ... ",
2903 		    first_uid, last_uid);
2904 
2905 	dir = folder_item_get_path(item);
2906 	if (is_dir_exist(dir))
2907 		remove_numbered_files(dir, first_uid, last_uid);
2908 	g_free(dir);
2909 
2910 	for (cur = mlist; cur != NULL; ) {
2911 		next = cur->next;
2912 
2913 		msginfo = (MsgInfo *)cur->data;
2914 		if (msginfo != NULL && first_uid <= msginfo->msgnum &&
2915 		    msginfo->msgnum <= last_uid) {
2916 			procmsg_msginfo_free(msginfo);
2917 			mlist = g_slist_remove(mlist, msginfo);
2918 		}
2919 
2920 		cur = next;
2921 	}
2922 
2923 	debug_print("done.\n");
2924 
2925 	return mlist;
2926 }
2927 
imap_delete_all_cached_messages(FolderItem * item)2928 static void imap_delete_all_cached_messages(FolderItem *item)
2929 {
2930 	gchar *dir;
2931 
2932 	g_return_if_fail(item != NULL);
2933 	g_return_if_fail(item->folder != NULL);
2934 	g_return_if_fail(FOLDER_TYPE(item->folder) == F_IMAP);
2935 
2936 	debug_print("Deleting all cached messages... ");
2937 
2938 	dir = folder_item_get_path(item);
2939 	if (is_dir_exist(dir))
2940 		remove_all_numbered_files(dir);
2941 	g_free(dir);
2942 
2943 	debug_print("done.\n");
2944 }
2945 
2946 #if USE_SSL
imap_open(const gchar * server,gushort port,SocksInfo * socks_info,SSLType ssl_type)2947 static SockInfo *imap_open(const gchar *server, gushort port,
2948 			   SocksInfo *socks_info, SSLType ssl_type)
2949 #else
2950 static SockInfo *imap_open(const gchar *server, gushort port,
2951 			   SocksInfo *socks_info)
2952 #endif
2953 {
2954 	SockInfo *sock = NULL;
2955 	const gchar *server_;
2956 	gushort port_;
2957 #if USE_THREADS
2958 	gint conn_id;
2959 #endif
2960 
2961 	if (socks_info) {
2962 		server_ = socks_info->proxy_host;
2963 		port_ = socks_info->proxy_port;
2964 	} else {
2965 		server_ = server;
2966 		port_ = port;
2967 	}
2968 
2969 #if USE_THREADS
2970 	if ((conn_id = sock_connect_async_thread(server_, port_)) < 0 ||
2971 	    sock_connect_async_thread_wait(conn_id, &sock) < 0) {
2972 		log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2973 			    server, port);
2974 		return NULL;
2975 	}
2976 #else
2977 	if ((sock = sock_connect(server_, port_)) == NULL) {
2978 		log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
2979 			    server, port);
2980 		return NULL;
2981 	}
2982 #endif
2983 
2984 	if (socks_info) {
2985 		if (socks_connect(sock, server, port, socks_info) < 0) {
2986 			log_warning("Can't establish SOCKS connection: %s:%d\n",
2987 				    server, port);
2988 			sock_close(sock);
2989 			return NULL;
2990 		}
2991 	}
2992 
2993 #if USE_SSL
2994 	if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
2995 		log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
2996 			    server, port);
2997 		sock_close(sock);
2998 		return NULL;
2999 	}
3000 #endif
3001 
3002 	return sock;
3003 }
3004 
imap_parse_namespace_str(gchar * str)3005 static GList *imap_parse_namespace_str(gchar *str)
3006 {
3007 	gchar *p = str;
3008 	gchar *name;
3009 	gchar *separator;
3010 	IMAPNameSpace *namespace;
3011 	GList *ns_list = NULL;
3012 
3013 	while (*p != '\0') {
3014 		/* parse ("#foo" "/") */
3015 
3016 		while (*p && *p != '(') p++;
3017 		if (*p == '\0') break;
3018 		p++;
3019 
3020 		while (*p && *p != '"') p++;
3021 		if (*p == '\0') break;
3022 		p++;
3023 		name = p;
3024 
3025 		while (*p && *p != '"') p++;
3026 		if (*p == '\0') break;
3027 		*p = '\0';
3028 		p++;
3029 
3030 		while (*p && g_ascii_isspace(*p)) p++;
3031 		if (*p == '\0') break;
3032 		if (strncmp(p, "NIL", 3) == 0)
3033 			separator = NULL;
3034 		else if (*p == '"') {
3035 			p++;
3036 			separator = p;
3037 			while (*p && *p != '"') p++;
3038 			if (*p == '\0') break;
3039 			*p = '\0';
3040 			p++;
3041 		} else break;
3042 
3043 		while (*p && *p != ')') p++;
3044 		if (*p == '\0') break;
3045 		p++;
3046 
3047 		namespace = g_new(IMAPNameSpace, 1);
3048 		namespace->name = g_strdup(name);
3049 		namespace->separator = separator ? separator[0] : '\0';
3050 		ns_list = g_list_append(ns_list, namespace);
3051 	}
3052 
3053 	return ns_list;
3054 }
3055 
imap_parse_namespace(IMAPSession * session,IMAPFolder * folder)3056 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
3057 {
3058 	gchar *ns_str = NULL;
3059 	gchar **str_array;
3060 
3061 	g_return_if_fail(session != NULL);
3062 	g_return_if_fail(folder != NULL);
3063 
3064 	if (folder->ns_personal != NULL ||
3065 	    folder->ns_others   != NULL ||
3066 	    folder->ns_shared   != NULL)
3067 		return;
3068 
3069 	if (imap_cmd_namespace(session, &ns_str) != IMAP_SUCCESS) {
3070 		log_warning(_("can't get namespace\n"));
3071 		imap_get_namespace_by_list(session, folder);
3072 		return;
3073 	}
3074 
3075 	str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
3076 	if (str_array[0])
3077 		folder->ns_personal = imap_parse_namespace_str(str_array[0]);
3078 	if (str_array[0] && str_array[1])
3079 		folder->ns_others = imap_parse_namespace_str(str_array[1]);
3080 	if (str_array[0] && str_array[1] && str_array[2])
3081 		folder->ns_shared = imap_parse_namespace_str(str_array[2]);
3082 	g_strfreev(str_array);
3083 	g_free(ns_str);
3084 }
3085 
imap_get_namespace_by_list(IMAPSession * session,IMAPFolder * folder)3086 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
3087 {
3088 	GSList *item_list, *cur;
3089 	gchar separator = '\0';
3090 	IMAPNameSpace *namespace;
3091 
3092 	g_return_if_fail(session != NULL);
3093 	g_return_if_fail(folder != NULL);
3094 
3095 	if (folder->ns_personal != NULL ||
3096 	    folder->ns_others   != NULL ||
3097 	    folder->ns_shared   != NULL)
3098 		return;
3099 
3100 	if (imap_cmd_gen_send(session, "LIST \"\" \"\"") != IMAP_SUCCESS)
3101 		return;
3102 	item_list = imap_parse_list(session, "", &separator);
3103 	for (cur = item_list; cur != NULL; cur = cur->next)
3104 		folder_item_destroy(FOLDER_ITEM(cur->data));
3105 	g_slist_free(item_list);
3106 
3107 	namespace = g_new(IMAPNameSpace, 1);
3108 	namespace->name = g_strdup("");
3109 	namespace->separator = separator;
3110 	folder->ns_personal = g_list_append(NULL, namespace);
3111 }
3112 
imap_find_namespace_from_list(GList * ns_list,const gchar * path)3113 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
3114 						    const gchar *path)
3115 {
3116 	IMAPNameSpace *namespace = NULL;
3117 	gchar *tmp_path, *name;
3118 
3119 	if (!path) path = "";
3120 
3121 	for (; ns_list != NULL; ns_list = ns_list->next) {
3122 		IMAPNameSpace *tmp_ns = ns_list->data;
3123 
3124 		Xstrcat_a(tmp_path, path, "/", return namespace);
3125 		Xstrdup_a(name, tmp_ns->name, return namespace);
3126 		if (tmp_ns->separator && tmp_ns->separator != '/') {
3127 			subst_char(tmp_path, tmp_ns->separator, '/');
3128 			subst_char(name, tmp_ns->separator, '/');
3129 		}
3130 		if (strncmp(tmp_path, name, strlen(name)) == 0)
3131 			namespace = tmp_ns;
3132 	}
3133 
3134 	return namespace;
3135 }
3136 
imap_find_namespace(IMAPFolder * folder,const gchar * path)3137 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
3138 					  const gchar *path)
3139 {
3140 	IMAPNameSpace *namespace;
3141 
3142 	g_return_val_if_fail(folder != NULL, NULL);
3143 
3144 	namespace = imap_find_namespace_from_list(folder->ns_personal, path);
3145 	if (namespace) return namespace;
3146 	namespace = imap_find_namespace_from_list(folder->ns_others, path);
3147 	if (namespace) return namespace;
3148 	namespace = imap_find_namespace_from_list(folder->ns_shared, path);
3149 	if (namespace) return namespace;
3150 
3151 	return NULL;
3152 }
3153 
imap_get_path_separator(IMAPFolder * folder,const gchar * path)3154 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
3155 {
3156 	IMAPNameSpace *namespace;
3157 	gchar separator = '/';
3158 
3159 	namespace = imap_find_namespace(folder, path);
3160 	if (namespace && namespace->separator)
3161 		separator = namespace->separator;
3162 
3163 	return separator;
3164 }
3165 
imap_get_real_path(IMAPFolder * folder,const gchar * path)3166 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
3167 {
3168 	gchar *real_path;
3169 	gchar separator;
3170 
3171 	g_return_val_if_fail(folder != NULL, NULL);
3172 	g_return_val_if_fail(path != NULL, NULL);
3173 
3174 	real_path = imap_utf8_to_modified_utf7(path);
3175 	separator = imap_get_path_separator(folder, path);
3176 	imap_path_separator_subst(real_path, separator);
3177 
3178 	return real_path;
3179 }
3180 
imap_parse_atom(IMAPSession * session,gchar * src,gchar * dest,gint dest_len,GString * str)3181 static gchar *imap_parse_atom(IMAPSession *session, gchar *src,
3182 			      gchar *dest, gint dest_len, GString *str)
3183 {
3184 	gchar *cur_pos = src;
3185 	gchar *nextline;
3186 
3187 	g_return_val_if_fail(str != NULL, cur_pos);
3188 
3189 	/* read the next line if the current response buffer is empty */
3190 	while (g_ascii_isspace(*cur_pos)) cur_pos++;
3191 	while (*cur_pos == '\0') {
3192 		if (sock_getline(SESSION(session)->sock, &nextline) < 0)
3193 			return cur_pos;
3194 		g_string_assign(str, nextline);
3195 		cur_pos = str->str;
3196 		strretchomp(nextline);
3197 		/* log_print("IMAP4< %s\n", nextline); */
3198 		debug_print("IMAP4< %s\n", nextline);
3199 		g_free(nextline);
3200 
3201 		while (g_ascii_isspace(*cur_pos)) cur_pos++;
3202 	}
3203 
3204 	if (*cur_pos == '~' && *(cur_pos + 1) == '{')
3205 		cur_pos++;
3206 
3207 	if (!strncmp(cur_pos, "NIL", 3)) {
3208 		*dest = '\0';
3209 		cur_pos += 3;
3210 	} else if (*cur_pos == '\"') {
3211 		gchar *p;
3212 
3213 		p = get_quoted(cur_pos, '\"', dest, dest_len);
3214 		cur_pos = p ? p : cur_pos + 2;
3215 	} else if (*cur_pos == '{') {
3216 		gchar buf[32];
3217 		gint len;
3218 		gint block_len = 0;
3219 
3220 		cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
3221 		len = atoi(buf);
3222 		g_return_val_if_fail(len >= 0, cur_pos);
3223 
3224 		g_string_truncate(str, 0);
3225 		cur_pos = str->str;
3226 
3227 		do {
3228 			gint cur_len;
3229 
3230 			cur_len = sock_getline(SESSION(session)->sock,
3231 					       &nextline);
3232 			if (cur_len < 0)
3233 				return cur_pos;
3234 			block_len += cur_len;
3235 			subst_null(nextline, cur_len, ' ');
3236 			g_string_append(str, nextline);
3237 			cur_pos = str->str;
3238 			strretchomp(nextline);
3239 			/* log_print("IMAP4< %s\n", nextline); */
3240 			debug_print("IMAP4< %s\n", nextline);
3241 			g_free(nextline);
3242 		} while (block_len < len);
3243 
3244 		memcpy(dest, cur_pos, MIN(len, dest_len - 1));
3245 		dest[MIN(len, dest_len - 1)] = '\0';
3246 		cur_pos += len;
3247 	}
3248 
3249 	return cur_pos;
3250 }
3251 
imap_get_header(IMAPSession * session,gchar * cur_pos,gchar ** headers,GString * str)3252 static gchar *imap_get_header(IMAPSession *session, gchar *cur_pos,
3253 			      gchar **headers, GString *str)
3254 {
3255 	gchar *nextline;
3256 	gchar buf[IMAPBUFSIZE];
3257 	gint len;
3258 	gint block_len = 0;
3259 
3260 	*headers = NULL;
3261 
3262 	g_return_val_if_fail(str != NULL, cur_pos);
3263 
3264 	while (g_ascii_isspace(*cur_pos)) cur_pos++;
3265 	if (*cur_pos == '~' && *(cur_pos + 1) == '{')
3266 		cur_pos++;
3267 
3268 	if (*cur_pos == '"') {
3269 		cur_pos = strchr_cpy(cur_pos + 1, '"', buf, sizeof(buf));
3270 		if (!cur_pos)
3271 			return NULL;
3272 		len = strlen(buf);
3273 		*headers = g_strdup(buf);
3274 		while (g_ascii_isspace(*cur_pos)) cur_pos++;
3275 		return cur_pos;
3276 	}
3277 
3278 	g_return_val_if_fail(*cur_pos == '{', cur_pos);
3279 
3280 	cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
3281 	len = atoi(buf);
3282 	g_return_val_if_fail(len >= 0, cur_pos);
3283 
3284 	g_string_truncate(str, 0);
3285 	cur_pos = str->str;
3286 
3287 	do {
3288 		gint cur_len;
3289 
3290 		cur_len = sock_getline(SESSION(session)->sock, &nextline);
3291 		if (cur_len < 0)
3292 			return cur_pos;
3293 		block_len += cur_len;
3294 		subst_null(nextline, cur_len, ' ');
3295 		g_string_append(str, nextline);
3296 		cur_pos = str->str;
3297 		/* strretchomp(nextline); */
3298 		/* debug_print("IMAP4< %s\n", nextline); */
3299 		g_free(nextline);
3300 	} while (block_len < len);
3301 
3302 	debug_print("IMAP4< [contents of RFC822.HEADER]\n");
3303 
3304 	*headers = g_strndup(cur_pos, len);
3305 	cur_pos += len;
3306 
3307 	while (g_ascii_isspace(*cur_pos)) cur_pos++;
3308 	while (*cur_pos == '\0') {
3309 		if (sock_getline(SESSION(session)->sock, &nextline) < 0)
3310 			return cur_pos;
3311 		g_string_assign(str, nextline);
3312 		cur_pos = str->str;
3313 		strretchomp(nextline);
3314 		debug_print("IMAP4< %s\n", nextline);
3315 		g_free(nextline);
3316 
3317 		while (g_ascii_isspace(*cur_pos)) cur_pos++;
3318 	}
3319 
3320 	return cur_pos;
3321 }
3322 
imap_parse_flags(const gchar * flag_str)3323 static MsgFlags imap_parse_flags(const gchar *flag_str)
3324 {
3325 	const gchar *p = flag_str;
3326 	MsgFlags flags = {0, 0};
3327 
3328 	flags.perm_flags = MSG_UNREAD;
3329 
3330 	while (*p != '\0') {
3331 		if (g_ascii_strncasecmp(p, "\\Recent", 7) == 0 &&
3332 		    MSG_IS_UNREAD(flags)) {
3333 			MSG_SET_PERM_FLAGS(flags, MSG_NEW);
3334 		} else if (g_ascii_strncasecmp(p, "\\Seen", 5) == 0) {
3335 			MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
3336 		} else if (g_ascii_strncasecmp(p, "\\Deleted", 8) == 0) {
3337 			MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
3338 		} else if (g_ascii_strncasecmp(p, "\\Flagged", 8) == 0) {
3339 			MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
3340 		} else if (g_ascii_strncasecmp(p, "\\Answered", 9) == 0) {
3341 			MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
3342 		} else if (g_ascii_strncasecmp(p, "$label", 6) == 0) {
3343 			/* color labels */
3344 			if (*(p + 6) >= '1' && *(p + 6) <= '7') {
3345 				guint color = *(p + 6) - '1' + 1;
3346 				MSG_UNSET_PERM_FLAGS(flags,
3347 						     MSG_CLABEL_FLAG_MASK);
3348 				MSG_SET_COLORLABEL_VALUE(flags, color);
3349 			}
3350 		}
3351 
3352 		while (*p && !g_ascii_isspace(*p)) p++;
3353 		while (g_ascii_isspace(*p)) p++;
3354 	}
3355 
3356 	return flags;
3357 }
3358 
imap_parse_imap_flags(const gchar * flag_str)3359 static IMAPFlags imap_parse_imap_flags(const gchar *flag_str)
3360 {
3361 	const gchar *p = flag_str;
3362 	IMAPFlags flags = 0;
3363 
3364 	while (*p != '\0') {
3365 		if (g_ascii_strncasecmp(p, "\\Seen", 5) == 0) {
3366 			flags |= IMAP_FLAG_SEEN;
3367 		} else if (g_ascii_strncasecmp(p, "\\Deleted", 8) == 0) {
3368 			flags |= IMAP_FLAG_DELETED;
3369 		} else if (g_ascii_strncasecmp(p, "\\Flagged", 8) == 0) {
3370 			flags |= IMAP_FLAG_FLAGGED;
3371 		} else if (g_ascii_strncasecmp(p, "\\Answered", 9) == 0) {
3372 			flags |= IMAP_FLAG_ANSWERED;
3373 		} else if (g_ascii_strncasecmp(p, "$label", 6) == 0) {
3374 			/* color labels */
3375 			if (*(p + 6) >= '1' && *(p + 6) <= '7') {
3376 				guint color = *(p + 6) - '1' + 1;
3377 				MSG_UNSET_FLAGS(flags, MSG_CLABEL_FLAG_MASK);
3378 				IMAP_SET_COLORLABEL_VALUE(flags, color);
3379 			}
3380 		}
3381 
3382 		while (*p && !g_ascii_isspace(*p)) p++;
3383 		while (g_ascii_isspace(*p)) p++;
3384 	}
3385 
3386 	return flags;
3387 }
3388 
imap_parse_envelope(IMAPSession * session,FolderItem * item,GString * line_str)3389 static MsgInfo *imap_parse_envelope(IMAPSession *session, FolderItem *item,
3390 				    GString *line_str)
3391 {
3392 	gchar buf[IMAPBUFSIZE];
3393 	MsgInfo *msginfo = NULL;
3394 	gchar *cur_pos;
3395 	gint msgnum;
3396 	guint32 uid = 0;
3397 	size_t size = 0;
3398 	MsgFlags flags = {0, 0}, imap_flags = {0, 0};
3399 
3400 	g_return_val_if_fail(line_str != NULL, NULL);
3401 	g_return_val_if_fail(line_str->str[0] == '*' &&
3402 			     line_str->str[1] == ' ', NULL);
3403 
3404 	MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
3405 	if (item->stype == F_QUEUE) {
3406 		MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3407 	} else if (item->stype == F_DRAFT) {
3408 		MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3409 	}
3410 
3411 	cur_pos = line_str->str + 2;
3412 
3413 #define PARSE_ONE_ELEMENT(ch)					\
3414 {								\
3415 	cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf));	\
3416 	if (cur_pos == NULL) {					\
3417 		g_warning("cur_pos == NULL\n");			\
3418 		procmsg_msginfo_free(msginfo);			\
3419 		return NULL;					\
3420 	}							\
3421 }
3422 
3423 	PARSE_ONE_ELEMENT(' ');
3424 	msgnum = atoi(buf);
3425 
3426 	PARSE_ONE_ELEMENT(' ');
3427 	g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
3428 
3429 	g_return_val_if_fail(*cur_pos == '(', NULL);
3430 	cur_pos++;
3431 
3432 	while (*cur_pos != '\0' && *cur_pos != ')') {
3433 		while (*cur_pos == ' ') cur_pos++;
3434 
3435 		if (!strncmp(cur_pos, "UID ", 4)) {
3436 			cur_pos += 4;
3437 			uid = strtoul(cur_pos, &cur_pos, 10);
3438 		} else if (!strncmp(cur_pos, "FLAGS ", 6)) {
3439 			cur_pos += 6;
3440 			if (*cur_pos != '(') {
3441 				g_warning("FLAGS: *cur_pos != '('\n");
3442 				procmsg_msginfo_free(msginfo);
3443 				return NULL;
3444 			}
3445 			cur_pos++;
3446 			PARSE_ONE_ELEMENT(')');
3447 			imap_flags = imap_parse_flags(buf);
3448 		} else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
3449 			cur_pos += 12;
3450 			size = strtol(cur_pos, &cur_pos, 10);
3451 		} else if (!strncmp(cur_pos, "RFC822.HEADER", 13)) {
3452 			gchar *headers;
3453 
3454 			cur_pos += 13;
3455 			cur_pos = imap_get_header(session, cur_pos, &headers,
3456 						  line_str);
3457 			if (cur_pos == NULL) {
3458 				g_warning("RFC822.HEADER: cur_pos == NULL\n");
3459 				procmsg_msginfo_free(msginfo);
3460 				return NULL;
3461 			}
3462 			if (!msginfo)
3463 				msginfo = procheader_parse_str(headers, flags, FALSE);
3464 			g_free(headers);
3465 		} else {
3466 			g_warning("invalid FETCH response: %s\n", cur_pos);
3467 			break;
3468 		}
3469 	}
3470 
3471 #undef PARSE_ONE_ELEMENT
3472 
3473 	if (msginfo) {
3474 		msginfo->msgnum = uid;
3475 		msginfo->size = size;
3476 		msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
3477 		msginfo->flags.perm_flags = imap_flags.perm_flags;
3478 	}
3479 
3480 	return msginfo;
3481 }
3482 
imap_msg_list_change_perm_flags(GSList * msglist,MsgPermFlags flags,gboolean is_set)3483 static gint imap_msg_list_change_perm_flags(GSList *msglist, MsgPermFlags flags,
3484 					    gboolean is_set)
3485 {
3486 	Folder *folder;
3487 	IMAPSession *session;
3488 	IMAPFlags iflags = 0;
3489 	MsgInfo *msginfo;
3490 	GSList *seq_list, *cur;
3491 	gint ok = IMAP_SUCCESS;
3492 
3493 	if (msglist == NULL) return IMAP_SUCCESS;
3494 
3495 	msginfo = (MsgInfo *)msglist->data;
3496 	g_return_val_if_fail(msginfo != NULL, -1);
3497 
3498 	g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
3499 	g_return_val_if_fail(msginfo->folder != NULL, -1);
3500 	g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
3501 
3502 	folder = msginfo->folder->folder;
3503 	g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
3504 
3505 	session = imap_session_get(folder);
3506 	if (!session) return -1;
3507 
3508 	ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3509 			 NULL, NULL, NULL, NULL);
3510 	if (ok != IMAP_SUCCESS)
3511 		return ok;
3512 
3513 	seq_list = imap_get_seq_set_from_msglist(msglist, 0);
3514 
3515 	if (flags & MSG_MARKED)  iflags |= IMAP_FLAG_FLAGGED;
3516 	if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
3517 
3518 	for (cur = seq_list; cur != NULL; cur = cur->next) {
3519 		gchar *seq_set = (gchar *)cur->data;
3520 
3521 		if (iflags) {
3522 			ok = imap_set_message_flags(session, seq_set, iflags,
3523 						    is_set);
3524 			if (ok != IMAP_SUCCESS) break;
3525 		}
3526 
3527 		if (flags & MSG_UNREAD) {
3528 			ok = imap_set_message_flags(session, seq_set,
3529 						    IMAP_FLAG_SEEN, !is_set);
3530 			if (ok != IMAP_SUCCESS) break;
3531 		}
3532 	}
3533 
3534 	imap_seq_set_free(seq_list);
3535 
3536 	return ok;
3537 }
3538 
imap_msg_set_perm_flags(MsgInfo * msginfo,MsgPermFlags flags)3539 gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
3540 {
3541 	GSList msglist;
3542 
3543 	msglist.data = msginfo;
3544 	msglist.next = NULL;
3545 
3546 	return imap_msg_list_change_perm_flags(&msglist, flags, TRUE);
3547 }
3548 
imap_msg_unset_perm_flags(MsgInfo * msginfo,MsgPermFlags flags)3549 gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
3550 {
3551 	GSList msglist;
3552 
3553 	msglist.data = msginfo;
3554 	msglist.next = NULL;
3555 
3556 	return imap_msg_list_change_perm_flags(&msglist, flags, FALSE);
3557 }
3558 
imap_msg_list_set_perm_flags(GSList * msglist,MsgPermFlags flags)3559 gint imap_msg_list_set_perm_flags(GSList *msglist, MsgPermFlags flags)
3560 {
3561 	return imap_msg_list_change_perm_flags(msglist, flags, TRUE);
3562 }
3563 
imap_msg_list_unset_perm_flags(GSList * msglist,MsgPermFlags flags)3564 gint imap_msg_list_unset_perm_flags(GSList *msglist, MsgPermFlags flags)
3565 {
3566 	return imap_msg_list_change_perm_flags(msglist, flags, FALSE);
3567 }
3568 
imap_msg_list_set_colorlabel_flags(GSList * msglist,guint color)3569 gint imap_msg_list_set_colorlabel_flags(GSList *msglist, guint color)
3570 {
3571 	Folder *folder;
3572 	IMAPSession *session;
3573 	IMAPFlags iflags = 0;
3574 	MsgInfo *msginfo;
3575 	GSList *seq_list, *cur;
3576 	gint ok = IMAP_SUCCESS;
3577 
3578 	if (msglist == NULL) return IMAP_SUCCESS;
3579 
3580 	msginfo = (MsgInfo *)msglist->data;
3581 	g_return_val_if_fail(msginfo != NULL, -1);
3582 
3583 	g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
3584 	g_return_val_if_fail(msginfo->folder != NULL, -1);
3585 	g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
3586 
3587 	folder = msginfo->folder->folder;
3588 	g_return_val_if_fail(FOLDER_TYPE(folder) == F_IMAP, -1);
3589 
3590 	session = imap_session_get(folder);
3591 	if (!session) return -1;
3592 
3593 	ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
3594 			 NULL, NULL, NULL, NULL);
3595 	if (ok != IMAP_SUCCESS)
3596 		return ok;
3597 
3598 	seq_list = imap_get_seq_set_from_msglist(msglist, 0);
3599 
3600 	IMAP_SET_COLORLABEL_VALUE(iflags, color);
3601 
3602 	for (cur = seq_list; cur != NULL; cur = cur->next) {
3603 		gchar *seq_set = (gchar *)cur->data;
3604 
3605 		ok = imap_cmd_store(session, seq_set,
3606 				    "-FLAGS.SILENT ($label1 $label2 $label3 $label4 $label5 $label6 $label7)");
3607 		if (ok != IMAP_SUCCESS) break;
3608 
3609 		if (iflags) {
3610 			ok = imap_set_message_flags(session, seq_set, iflags,
3611 						    TRUE);
3612 			if (ok != IMAP_SUCCESS) break;
3613 		}
3614 	}
3615 
3616 	imap_seq_set_free(seq_list);
3617 
3618 	return ok;
3619 }
3620 
imap_get_flag_str(IMAPFlags flags)3621 static gchar *imap_get_flag_str(IMAPFlags flags)
3622 {
3623 	GString *str;
3624 	gchar *ret;
3625 	guint color;
3626 
3627 	str = g_string_new(NULL);
3628 
3629 	if (IMAP_IS_SEEN(flags))	g_string_append(str, "\\Seen ");
3630 	if (IMAP_IS_ANSWERED(flags))	g_string_append(str, "\\Answered ");
3631 	if (IMAP_IS_FLAGGED(flags))	g_string_append(str, "\\Flagged ");
3632 	if (IMAP_IS_DELETED(flags))	g_string_append(str, "\\Deleted ");
3633 	if (IMAP_IS_DRAFT(flags))	g_string_append(str, "\\Draft ");
3634 
3635 	if ((color = IMAP_GET_COLORLABEL_VALUE(flags)) != 0) {
3636 		g_string_append_printf(str, "$label%u", color);
3637 	}
3638 
3639 	if (str->len > 0 && str->str[str->len - 1] == ' ')
3640 		g_string_truncate(str, str->len - 1);
3641 
3642 	ret = str->str;
3643 	g_string_free(str, FALSE);
3644 
3645 	return ret;
3646 }
3647 
imap_set_message_flags(IMAPSession * session,const gchar * seq_set,IMAPFlags flags,gboolean is_set)3648 static gint imap_set_message_flags(IMAPSession *session,
3649 				   const gchar *seq_set,
3650 				   IMAPFlags flags,
3651 				   gboolean is_set)
3652 {
3653 	gchar *cmd;
3654 	gchar *flag_str;
3655 	gint ok;
3656 
3657 	flag_str = imap_get_flag_str(flags);
3658 	cmd = g_strconcat(is_set ? "+FLAGS.SILENT (" : "-FLAGS.SILENT (",
3659 			  flag_str, ")", NULL);
3660 	g_free(flag_str);
3661 
3662 	ok = imap_cmd_store(session, seq_set, cmd);
3663 	g_free(cmd);
3664 
3665 	return ok;
3666 }
3667 
imap_select(IMAPSession * session,IMAPFolder * folder,const gchar * path,gint * exists,gint * recent,gint * unseen,guint32 * uid_validity)3668 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
3669 			const gchar *path,
3670 			gint *exists, gint *recent, gint *unseen,
3671 			guint32 *uid_validity)
3672 {
3673 	gchar *real_path;
3674 	gint ok;
3675 	gint exists_, recent_, unseen_;
3676 	guint32 uid_validity_;
3677 
3678 	if (!exists || !recent || !unseen || !uid_validity) {
3679 		if (session->mbox && strcmp(session->mbox, path) == 0)
3680 			return IMAP_SUCCESS;
3681 		exists = &exists_;
3682 		recent = &recent_;
3683 		unseen = &unseen_;
3684 		uid_validity = &uid_validity_;
3685 	}
3686 
3687 	g_free(session->mbox);
3688 	session->mbox = NULL;
3689 
3690 	real_path = imap_get_real_path(folder, path);
3691 	ok = imap_cmd_select(session, real_path,
3692 			     exists, recent, unseen, uid_validity);
3693 	if (ok != IMAP_SUCCESS)
3694 		log_warning(_("can't select folder: %s\n"), real_path);
3695 	else
3696 		session->mbox = g_strdup(path);
3697 	g_free(real_path);
3698 
3699 	return ok;
3700 }
3701 
3702 #define THROW(err) { ok = err; goto catch; }
3703 
imap_status(IMAPSession * session,IMAPFolder * folder,const gchar * path,gint * messages,gint * recent,guint32 * uid_next,guint32 * uid_validity,gint * unseen)3704 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
3705 			const gchar *path,
3706 			gint *messages, gint *recent,
3707 			guint32 *uid_next, guint32 *uid_validity,
3708 			gint *unseen)
3709 {
3710 	gchar *real_path;
3711 	gchar *real_path_;
3712 	gint ok;
3713 	GPtrArray *argbuf = NULL;
3714 	gchar *str;
3715 
3716 	if (messages && recent && uid_next && uid_validity && unseen) {
3717 		*messages = *recent = *uid_next = *uid_validity = *unseen = 0;
3718 		argbuf = g_ptr_array_new();
3719 	}
3720 
3721 	real_path = imap_get_real_path(folder, path);
3722 	QUOTE_IF_REQUIRED(real_path_, real_path);
3723 	ok = imap_cmd_gen_send(session, "STATUS %s "
3724 			       "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
3725 			       real_path_);
3726 	if (ok != IMAP_SUCCESS) {
3727 		log_warning("error on sending imap command: STATUS\n");
3728 		THROW(ok);
3729 	}
3730 	ok = imap_cmd_ok(session, argbuf);
3731 	if (ok != IMAP_SUCCESS)
3732 		log_warning(_("error on imap command: STATUS\n"));
3733 	if (ok != IMAP_SUCCESS || !argbuf) THROW(ok);
3734 
3735 	str = search_array_str(argbuf, "STATUS");
3736 	if (!str) THROW(IMAP_ERROR);
3737 
3738 	str = strrchr_with_skip_quote(str, '"', '(');
3739 	if (!str) THROW(IMAP_ERROR);
3740 	str++;
3741 	while (*str != '\0' && *str != ')') {
3742 		while (*str == ' ') str++;
3743 
3744 		if (!strncmp(str, "MESSAGES ", 9)) {
3745 			str += 9;
3746 			*messages = strtol(str, &str, 10);
3747 		} else if (!strncmp(str, "RECENT ", 7)) {
3748 			str += 7;
3749 			*recent = strtol(str, &str, 10);
3750 		} else if (!strncmp(str, "UIDNEXT ", 8)) {
3751 			str += 8;
3752 			*uid_next = strtoul(str, &str, 10);
3753 		} else if (!strncmp(str, "UIDVALIDITY ", 12)) {
3754 			str += 12;
3755 			*uid_validity = strtoul(str, &str, 10);
3756 		} else if (!strncmp(str, "UNSEEN ", 7)) {
3757 			str += 7;
3758 			*unseen = strtol(str, &str, 10);
3759 		} else {
3760 			g_warning("invalid STATUS response: %s\n", str);
3761 			break;
3762 		}
3763 	}
3764 
3765 catch:
3766 	g_free(real_path);
3767 	if (argbuf) {
3768 		ptr_array_free_strings(argbuf);
3769 		g_ptr_array_free(argbuf, TRUE);
3770 	}
3771 
3772 	return ok;
3773 }
3774 
3775 #undef THROW
3776 
imap_has_capability(IMAPSession * session,const gchar * capability)3777 static gboolean imap_has_capability(IMAPSession	*session,
3778 				    const gchar *capability)
3779 {
3780 	gchar **p;
3781 
3782 	for (p = session->capability; *p != NULL; ++p) {
3783 		if (!g_ascii_strcasecmp(*p, capability))
3784 			return TRUE;
3785 	}
3786 
3787 	return FALSE;
3788 }
3789 
imap_capability_free(IMAPSession * session)3790 static void imap_capability_free(IMAPSession *session)
3791 {
3792 	if (session->capability) {
3793 		g_strfreev(session->capability);
3794 		session->capability = NULL;
3795 	}
3796 }
3797 
3798 
3799 /* low-level IMAP4rev1 commands */
3800 
3801 #define THROW(err) { ok = err; goto catch; }
3802 
imap_cmd_capability(IMAPSession * session)3803 static gint imap_cmd_capability(IMAPSession *session)
3804 {
3805 	gint ok;
3806 	GPtrArray *argbuf;
3807 	gchar *capability;
3808 
3809 	argbuf = g_ptr_array_new();
3810 
3811 	if ((ok = imap_cmd_gen_send(session, "CAPABILITY")) != IMAP_SUCCESS)
3812 		THROW(ok);
3813 	if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3814 
3815 	capability = search_array_str(argbuf, "CAPABILITY ");
3816 	if (!capability) THROW(IMAP_ERROR);
3817 
3818 	capability += strlen("CAPABILITY ");
3819 
3820 	imap_capability_free(session);
3821 	session->capability = g_strsplit(capability, " ", -1);
3822 
3823 catch:
3824 	ptr_array_free_strings(argbuf);
3825 	g_ptr_array_free(argbuf, TRUE);
3826 
3827 	return ok;
3828 }
3829 
3830 #undef THROW
3831 
imap_cmd_auth_plain(IMAPSession * session,const gchar * user,const gchar * pass)3832 static gint imap_cmd_auth_plain(IMAPSession *session, const gchar *user,
3833 				const gchar *pass)
3834 {
3835 	gchar *p;
3836 	gchar *response;
3837 	gchar *response64;
3838 	gint ok;
3839 
3840 	p = response = g_malloc(strlen(user) * 2 + 2 + strlen(pass) + 1);
3841 	strcpy(p, user);
3842 	p += strlen(user) + 1;
3843 	strcpy(p, user);
3844 	p += strlen(user) + 1;
3845 	strcpy(p, pass);
3846 	p += strlen(pass);
3847 
3848 	response64 = g_malloc((p - response) * 2 + 1);
3849 	base64_encode(response64, (guchar *)response, p - response);
3850 	g_free(response);
3851 
3852 	log_print("IMAP4> ****************\n");
3853 	sock_puts(SESSION(session)->sock, response64);
3854 	ok = imap_cmd_ok(session, NULL);
3855 	if (ok != IMAP_SUCCESS)
3856 		log_warning(_("IMAP4 authentication failed.\n"));
3857 	g_free(response64);
3858 
3859 	return ok;
3860 }
3861 
imap_cmd_auth_cram_md5(IMAPSession * session,const gchar * user,const gchar * pass,const gchar * challenge64)3862 static gint imap_cmd_auth_cram_md5(IMAPSession *session, const gchar *user,
3863 				   const gchar *pass, const gchar *challenge64)
3864 {
3865 	gchar *challenge;
3866 	gint challenge_len;
3867 	gchar hexdigest[33];
3868 	gchar *response;
3869 	gchar *response64;
3870 	gint ok;
3871 
3872 	challenge = g_malloc(strlen(challenge64 + 2) + 1);
3873 	challenge_len = base64_decode((guchar *)challenge, challenge64 + 2, -1);
3874 	challenge[challenge_len] = '\0';
3875 	log_print("IMAP< [Decoded: %s]\n", challenge);
3876 
3877 	md5_hex_hmac(hexdigest, (guchar *)challenge, challenge_len,
3878 		     (guchar *)pass, strlen(pass));
3879 	g_free(challenge);
3880 
3881 	response = g_strdup_printf("%s %s", user, hexdigest);
3882 	log_print("IMAP> [Encoded: %s]\n", response);
3883 	response64 = g_malloc((strlen(response) + 3) * 2 + 1);
3884 	base64_encode(response64, (guchar *)response, strlen(response));
3885 	g_free(response);
3886 
3887 	log_print("IMAP> %s\n", response64);
3888 	sock_puts(SESSION(session)->sock, response64);
3889 	ok = imap_cmd_ok(session, NULL);
3890 	if (ok != IMAP_SUCCESS)
3891 		log_warning(_("IMAP4 authentication failed.\n"));
3892 
3893 	return ok;
3894 }
3895 
imap_cmd_authenticate(IMAPSession * session,const gchar * user,const gchar * pass,IMAPAuthType type)3896 static gint imap_cmd_authenticate(IMAPSession *session, const gchar *user,
3897 				  const gchar *pass, IMAPAuthType type)
3898 {
3899 	gchar *auth_type;
3900 	gint ok;
3901 	gchar *buf = NULL;
3902 
3903 	g_return_val_if_fail((type == 0 || type == IMAP_AUTH_CRAM_MD5 ||
3904 			      type == IMAP_AUTH_PLAIN), IMAP_ERROR);
3905 
3906 	if (type == IMAP_AUTH_PLAIN)
3907 		auth_type = "PLAIN";
3908 	else
3909 		auth_type = "CRAM-MD5";
3910 
3911 	ok = imap_cmd_gen_send(session, "AUTHENTICATE %s", auth_type);
3912 	if (ok != IMAP_SUCCESS) {
3913 		g_free(buf);
3914 		return ok;
3915 	}
3916 	ok = imap_cmd_gen_recv(session, &buf);
3917 	if (ok != IMAP_SUCCESS || buf[0] != '+') {
3918 		g_free(buf);
3919 		return IMAP_ERROR;
3920 	}
3921 
3922 	if (type == IMAP_AUTH_PLAIN)
3923 		ok = imap_cmd_auth_plain(session, user, pass);
3924 	else
3925 		ok = imap_cmd_auth_cram_md5(session, user, pass, buf);
3926 
3927 	g_free(buf);
3928 
3929 	return ok;
3930 }
3931 
imap_cmd_login(IMAPSession * session,const gchar * user,const gchar * pass)3932 static gint imap_cmd_login(IMAPSession *session,
3933 			   const gchar *user, const gchar *pass)
3934 {
3935 	gchar *user_, *pass_;
3936 	gint ok;
3937 
3938 	QUOTE_IF_REQUIRED(user_, user);
3939 	QUOTE_IF_REQUIRED(pass_, pass);
3940 	ok = imap_cmd_gen_send(session, "LOGIN %s %s", user_, pass_);
3941 	if (ok == IMAP_SUCCESS)
3942 		ok = imap_cmd_ok(session, NULL);
3943 	if (ok != IMAP_SUCCESS)
3944 		log_warning(_("IMAP4 login failed.\n"));
3945 
3946 	return ok;
3947 }
3948 
imap_cmd_logout(IMAPSession * session)3949 static gint imap_cmd_logout(IMAPSession *session)
3950 {
3951 	if (imap_cmd_gen_send(session, "LOGOUT") != IMAP_SUCCESS)
3952 		return IMAP_ERROR;
3953 	return imap_cmd_ok(session, NULL);
3954 }
3955 
imap_cmd_noop(IMAPSession * session)3956 static gint imap_cmd_noop(IMAPSession *session)
3957 {
3958 	gint ret;
3959 
3960 	ret = imap_cmd_gen_send(session, "NOOP");
3961 	if (ret != IMAP_SUCCESS)
3962 		return ret;
3963 	return imap_cmd_ok(session, NULL);
3964 }
3965 
3966 #if USE_SSL
imap_cmd_starttls(IMAPSession * session)3967 static gint imap_cmd_starttls(IMAPSession *session)
3968 {
3969 	if (imap_cmd_gen_send(session, "STARTTLS") != IMAP_SUCCESS)
3970 		return IMAP_ERROR;
3971 	return imap_cmd_ok(session, NULL);
3972 }
3973 #endif
3974 
3975 #define THROW(err) { ok = err; goto catch; }
3976 
imap_cmd_namespace(IMAPSession * session,gchar ** ns_str)3977 static gint imap_cmd_namespace(IMAPSession *session, gchar **ns_str)
3978 {
3979 	gint ok;
3980 	GPtrArray *argbuf;
3981 	gchar *str;
3982 
3983 	argbuf = g_ptr_array_new();
3984 
3985 	if ((ok = imap_cmd_gen_send(session, "NAMESPACE")) != IMAP_SUCCESS)
3986 		THROW(ok);
3987 	if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
3988 
3989 	str = search_array_str(argbuf, "NAMESPACE");
3990 	if (!str) THROW(IMAP_ERROR);
3991 
3992 	*ns_str = g_strdup(str);
3993 
3994 catch:
3995 	ptr_array_free_strings(argbuf);
3996 	g_ptr_array_free(argbuf, TRUE);
3997 
3998 	return ok;
3999 }
4000 
4001 #undef THROW
4002 
imap_cmd_list(IMAPSession * session,const gchar * ref,const gchar * mailbox,GPtrArray * argbuf)4003 static gint imap_cmd_list(IMAPSession *session, const gchar *ref,
4004 			  const gchar *mailbox, GPtrArray *argbuf)
4005 {
4006 	gchar *ref_, *mailbox_;
4007 
4008 	if (!ref) ref = "";
4009 	if (!mailbox) mailbox = "";
4010 
4011 	QUOTE_IF_REQUIRED(ref_, ref);
4012 	QUOTE_IF_REQUIRED(mailbox_, mailbox);
4013 	if (imap_cmd_gen_send(session, "LIST %s %s", ref_, mailbox_) != IMAP_SUCCESS)
4014 		return IMAP_ERROR;
4015 
4016 	return imap_cmd_ok(session, argbuf);
4017 }
4018 
4019 #define THROW goto catch
4020 
imap_cmd_do_select(IMAPSession * session,const gchar * folder,gboolean examine,gint * exists,gint * recent,gint * unseen,guint32 * uid_validity)4021 static gint imap_cmd_do_select(IMAPSession *session, const gchar *folder,
4022 			       gboolean examine,
4023 			       gint *exists, gint *recent, gint *unseen,
4024 			       guint32 *uid_validity)
4025 {
4026 	gint ok;
4027 	gchar *resp_str;
4028 	GPtrArray *argbuf;
4029 	gchar *select_cmd;
4030 	gchar *folder_;
4031 	guint uid_validity_;
4032 
4033 	*exists = *recent = *unseen = *uid_validity = 0;
4034 	argbuf = g_ptr_array_new();
4035 
4036 	if (examine)
4037 		select_cmd = "EXAMINE";
4038 	else
4039 		select_cmd = "SELECT";
4040 
4041 	QUOTE_IF_REQUIRED(folder_, folder);
4042 	if ((ok = imap_cmd_gen_send(session, "%s %s", select_cmd, folder_)) != IMAP_SUCCESS)
4043 		THROW;
4044 
4045 	if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW;
4046 
4047 	resp_str = search_array_contain_str(argbuf, "EXISTS");
4048 	if (resp_str) {
4049 		if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
4050 			g_warning("imap_cmd_select(): invalid EXISTS line.\n");
4051 			THROW;
4052 		}
4053 	}
4054 
4055 	resp_str = search_array_contain_str(argbuf, "RECENT");
4056 	if (resp_str) {
4057 		if (sscanf(resp_str, "%d RECENT", recent) != 1) {
4058 			g_warning("imap_cmd_select(): invalid RECENT line.\n");
4059 			THROW;
4060 		}
4061 	}
4062 
4063 	resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
4064 	if (resp_str) {
4065 		if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", &uid_validity_)
4066 		    != 1) {
4067 			g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
4068 			THROW;
4069 		}
4070 		*uid_validity = uid_validity_;
4071 	}
4072 
4073 	resp_str = search_array_contain_str(argbuf, "UNSEEN");
4074 	if (resp_str) {
4075 		if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
4076 			g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
4077 			THROW;
4078 		}
4079 	}
4080 
4081 catch:
4082 	ptr_array_free_strings(argbuf);
4083 	g_ptr_array_free(argbuf, TRUE);
4084 
4085 	return ok;
4086 }
4087 
imap_cmd_select(IMAPSession * session,const gchar * folder,gint * exists,gint * recent,gint * unseen,guint32 * uid_validity)4088 static gint imap_cmd_select(IMAPSession *session, const gchar *folder,
4089 			    gint *exists, gint *recent, gint *unseen,
4090 			    guint32 *uid_validity)
4091 {
4092 	return imap_cmd_do_select(session, folder, FALSE,
4093 				  exists, recent, unseen, uid_validity);
4094 }
4095 
imap_cmd_examine(IMAPSession * session,const gchar * folder,gint * exists,gint * recent,gint * unseen,guint32 * uid_validity)4096 static gint imap_cmd_examine(IMAPSession *session, const gchar *folder,
4097 			     gint *exists, gint *recent, gint *unseen,
4098 			     guint32 *uid_validity)
4099 {
4100 	return imap_cmd_do_select(session, folder, TRUE,
4101 				  exists, recent, unseen, uid_validity);
4102 }
4103 
4104 #undef THROW
4105 
imap_cmd_create(IMAPSession * session,const gchar * folder)4106 static gint imap_cmd_create(IMAPSession *session, const gchar *folder)
4107 {
4108 	gchar *folder_;
4109 
4110 	QUOTE_IF_REQUIRED(folder_, folder);
4111 	if (imap_cmd_gen_send(session, "CREATE %s", folder_) != IMAP_SUCCESS)
4112 		return IMAP_ERROR;
4113 
4114 	return imap_cmd_ok(session, NULL);
4115 }
4116 
imap_cmd_rename(IMAPSession * session,const gchar * old_folder,const gchar * new_folder)4117 static gint imap_cmd_rename(IMAPSession *session, const gchar *old_folder,
4118 			    const gchar *new_folder)
4119 {
4120 	gchar *old_folder_, *new_folder_;
4121 
4122 	QUOTE_IF_REQUIRED(old_folder_, old_folder);
4123 	QUOTE_IF_REQUIRED(new_folder_, new_folder);
4124 	if (imap_cmd_gen_send(session, "RENAME %s %s", old_folder_, new_folder_) != IMAP_SUCCESS)
4125 		return IMAP_ERROR;
4126 
4127 	return imap_cmd_ok(session, NULL);
4128 }
4129 
imap_cmd_delete(IMAPSession * session,const gchar * folder)4130 static gint imap_cmd_delete(IMAPSession *session, const gchar *folder)
4131 {
4132 	gchar *folder_;
4133 
4134 	QUOTE_IF_REQUIRED(folder_, folder);
4135 	if (imap_cmd_gen_send(session, "DELETE %s", folder_) != IMAP_SUCCESS)
4136 		return IMAP_ERROR;
4137 
4138 	return imap_cmd_ok(session, NULL);
4139 }
4140 
imap_cmd_subscribe(IMAPSession * session,const gchar * folder)4141 static gint imap_cmd_subscribe(IMAPSession *session, const gchar *folder)
4142 {
4143 	gchar *folder_;
4144 
4145 	QUOTE_IF_REQUIRED(folder_, folder);
4146 	if (imap_cmd_gen_send(session, "SUBSCRIBE %s", folder_) != IMAP_SUCCESS)
4147 		return IMAP_ERROR;
4148 
4149 	return imap_cmd_ok(session, NULL);
4150 }
4151 
4152 #define THROW(err) { ok = err; goto catch; }
4153 
imap_cmd_search(IMAPSession * session,const gchar * criteria,GArray ** result)4154 static gint imap_cmd_search(IMAPSession *session, const gchar *criteria,
4155 			    GArray **result)
4156 {
4157 	gint ok;
4158 	GPtrArray *argbuf;
4159 	GArray *array;
4160 	gchar *str;
4161 	gchar *p, *ep;
4162 	gint i;
4163 	guint32 uid;
4164 
4165 	g_return_val_if_fail(criteria != NULL, IMAP_ERROR);
4166 	g_return_val_if_fail(result != NULL, IMAP_ERROR);
4167 
4168 	argbuf = g_ptr_array_new();
4169 
4170 	if ((ok = imap_cmd_gen_send(session, "UID SEARCH %s", criteria)) != IMAP_SUCCESS)
4171 		THROW(ok);
4172 	if ((ok = imap_cmd_ok(session, argbuf)) != IMAP_SUCCESS) THROW(ok);
4173 
4174 	array = g_array_new(FALSE, FALSE, sizeof(guint32));
4175 
4176 	for (i = 0; i < argbuf->len; i++) {
4177 		str = g_ptr_array_index(argbuf, i);
4178 		if (strncmp(str, "SEARCH", 6) != 0)
4179 			continue;
4180 
4181 		p = str + 6;
4182 		while (*p != '\0') {
4183 			uid = strtoul(p, &ep, 10);
4184 			if (p < ep && uid > 0) {
4185 				g_array_append_val(array, uid);
4186 				p = ep;
4187 			} else
4188 				break;
4189 		}
4190 	}
4191 
4192 	*result = array;
4193 
4194 catch:
4195 	ptr_array_free_strings(argbuf);
4196 	g_ptr_array_free(argbuf, TRUE);
4197 
4198 	return ok;
4199 }
4200 
4201 typedef struct _IMAPCmdFetchData
4202 {
4203 	guint32 uid;
4204 	const gchar *filename;
4205 } IMAPCmdFetchData;
4206 
4207 #define THROW(err) { ok = err; goto catch; }
4208 
imap_cmd_fetch_func(IMAPSession * session,gpointer data)4209 static gint imap_cmd_fetch_func(IMAPSession *session, gpointer data)
4210 {
4211 	const gchar *filename = ((IMAPCmdFetchData *)data)->filename;
4212 	gint ok;
4213 	gchar *buf;
4214 	gchar *cur_pos;
4215 	gchar size_str[32];
4216 	glong size_num;
4217 	gint ret;
4218 
4219 	while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) {
4220 		if (buf[0] != '*' || buf[1] != ' ') {
4221 			g_free(buf);
4222 			return IMAP_ERROR;
4223 		}
4224 		if (strstr(buf, "FETCH") != NULL && strstr(buf, "BODY") != NULL)
4225 			break;
4226 		g_free(buf);
4227 	}
4228 	if (ok != IMAP_SUCCESS)
4229 		THROW(ok);
4230 
4231 #define RETURN_ERROR_IF_FAIL(cond)			\
4232 	if (!(cond)) {					\
4233 		g_free(buf);				\
4234 		ok = imap_cmd_ok_real(session, NULL);	\
4235 		THROW(IMAP_ERROR);			\
4236 	}
4237 
4238 	cur_pos = strchr(buf, '{');
4239 	RETURN_ERROR_IF_FAIL(cur_pos != NULL);
4240 	cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
4241 	RETURN_ERROR_IF_FAIL(cur_pos != NULL);
4242 	size_num = atol(size_str);
4243 	RETURN_ERROR_IF_FAIL(size_num >= 0);
4244 
4245 	RETURN_ERROR_IF_FAIL(*cur_pos == '\0');
4246 
4247 #undef RETURN_ERROR_IF_FAIL
4248 
4249 	g_free(buf);
4250 
4251 	if ((ret = recv_bytes_write_to_file(SESSION(session)->sock,
4252 					    size_num, filename)) != 0) {
4253 		if (ret == -2)
4254 			THROW(IMAP_SOCKET);
4255 	}
4256 
4257 	if (imap_cmd_gen_recv(session, &buf) != IMAP_SUCCESS)
4258 		THROW(IMAP_ERROR);
4259 
4260 	if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')') {
4261 		g_free(buf);
4262 		THROW(IMAP_ERROR);
4263 	}
4264 	g_free(buf);
4265 
4266 	ok = imap_cmd_ok_real(session, NULL);
4267 
4268 	if (ret != 0)
4269 		THROW(IMAP_ERROR);
4270 
4271 catch:
4272 	return ok;
4273 }
4274 
4275 #undef THROW
4276 
imap_cmd_fetch(IMAPSession * session,guint32 uid,const gchar * filename)4277 static gint imap_cmd_fetch(IMAPSession *session, guint32 uid,
4278 			   const gchar *filename)
4279 {
4280 	gint ok;
4281 	IMAPCmdFetchData fetch_data = {uid, filename};
4282 
4283 	g_return_val_if_fail(filename != NULL, IMAP_ERROR);
4284 
4285 	ok = imap_cmd_gen_send(session, "UID FETCH %u BODY.PEEK[]", uid);
4286 	if (ok != IMAP_SUCCESS)
4287 		return ok;
4288 
4289 #if USE_THREADS
4290 	ok = imap_thread_run(session, imap_cmd_fetch_func, &fetch_data);
4291 #else
4292 	ok = imap_cmd_fetch_func(session, &fetch_data);
4293 #endif
4294 
4295 	return ok;
4296 }
4297 
imap_get_date_time(gchar * buf,size_t len,stime_t timer)4298 static void imap_get_date_time(gchar *buf, size_t len, stime_t timer)
4299 {
4300 	static gchar monthstr[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
4301 	struct tm *lt;
4302 	gchar date_time[64];
4303 	gchar tz[6];
4304 	time_t timer_ = timer;
4305 
4306 	lt = localtime(&timer_);
4307 	if (lt && lt->tm_mon >= 0 && lt->tm_mon < 12) {
4308 		strftime(date_time, sizeof(date_time), "%d-___-%Y %H:%M:%S",
4309 			 lt);
4310 		tzoffset_buf(tz, &timer);
4311 		memcpy(date_time + 3, monthstr + lt->tm_mon * 3, 3);
4312 		g_snprintf(buf, len, "%s %s", date_time, tz);
4313 	}
4314 }
4315 
imap_cmd_append(IMAPSession * session,const gchar * destfolder,const gchar * file,IMAPFlags flags,guint32 * new_uid)4316 static gint imap_cmd_append(IMAPSession *session, const gchar *destfolder,
4317 			    const gchar *file, IMAPFlags flags,
4318 			    guint32 *new_uid)
4319 {
4320 	gint ok;
4321 	MsgInfo *msginfo;
4322 	MsgFlags flags_ = {0, 0};
4323 	gchar date_time[64] = "";
4324 	gint size;
4325 	gchar *destfolder_;
4326 	gchar *flag_str;
4327 	guint new_uid_;
4328 	gchar *ret = NULL;
4329 	gchar buf[BUFFSIZE];
4330 	FILE *fp;
4331 	FILE *tmp;
4332 	size_t read_len;
4333 	GPtrArray *argbuf;
4334 	gchar *resp_str;
4335 
4336 	g_return_val_if_fail(file != NULL, IMAP_ERROR);
4337 
4338 	if ((fp = g_fopen(file, "rb")) == NULL) {
4339 		FILE_OP_ERROR(file, "fopen");
4340 		return -1;
4341 	}
4342 
4343 	/* use Date: header as received date */
4344 	msginfo = procheader_parse_stream(fp, flags_, FALSE);
4345 	imap_get_date_time(date_time, sizeof(date_time), msginfo->date_t);
4346 	procmsg_msginfo_free(msginfo);
4347 
4348 	rewind(fp);
4349 	tmp = canonicalize_file_stream(fp, &size);
4350 	fclose(fp);
4351 	if (!tmp)
4352 		return -1;
4353 
4354 	QUOTE_IF_REQUIRED(destfolder_, destfolder);
4355 	flag_str = imap_get_flag_str(flags);
4356 	if (date_time[0])
4357 		ok = imap_cmd_gen_send(session, "APPEND %s (%s) \"%s\" {%d}",
4358 				       destfolder_, flag_str, date_time, size);
4359 	else
4360 		ok = imap_cmd_gen_send(session, "APPEND %s (%s) {%d}",
4361 				       destfolder_, flag_str, size);
4362 	g_free(flag_str);
4363 	if (ok != IMAP_SUCCESS) {
4364 		log_warning(_("can't append %s to %s\n"), file, destfolder_);
4365 		fclose(tmp);
4366 		return ok;
4367 	}
4368 
4369 	ok = imap_cmd_gen_recv(session, &ret);
4370 	if (ok != IMAP_SUCCESS || ret[0] != '+') {
4371 		log_warning(_("can't append %s to %s\n"), file, destfolder_);
4372 		g_free(ret);
4373 		fclose(tmp);
4374 		return IMAP_ERROR;
4375 	}
4376 	g_free(ret);
4377 
4378 	log_print("IMAP4> %s\n", _("(sending file...)"));
4379 
4380 	while ((read_len = fread(buf, 1, sizeof(buf), tmp)) > 0) {
4381 		if (read_len < sizeof(buf) && ferror(tmp))
4382 			break;
4383 		if (sock_write_all(SESSION(session)->sock, buf, read_len) < 0) {
4384 			fclose(tmp);
4385 			return -1;
4386 		}
4387 	}
4388 
4389 	if (ferror(tmp)) {
4390 		FILE_OP_ERROR(file, "fread");
4391 		fclose(tmp);
4392 		return -1;
4393 	}
4394 
4395 	sock_puts(SESSION(session)->sock, "");
4396 
4397 	fclose(tmp);
4398 
4399 	if (new_uid != NULL)
4400 		*new_uid = 0;
4401 
4402 	if (new_uid != NULL && session->uidplus) {
4403 		argbuf = g_ptr_array_new();
4404 
4405 		ok = imap_cmd_ok(session, argbuf);
4406 		if (ok != IMAP_SUCCESS)
4407 			log_warning(_("can't append message to %s\n"),
4408 				    destfolder_);
4409 		else if (argbuf->len > 0) {
4410 			resp_str = g_ptr_array_index(argbuf, argbuf->len - 1);
4411 			if (resp_str &&
4412 			    sscanf(resp_str, "%*u OK [APPENDUID %*u %u]",
4413 				   &new_uid_) == 1) {
4414 				*new_uid = new_uid_;
4415 			}
4416 		}
4417 
4418 		ptr_array_free_strings(argbuf);
4419 		g_ptr_array_free(argbuf, TRUE);
4420 	} else
4421 		ok = imap_cmd_ok(session, NULL);
4422 
4423 	return ok;
4424 }
4425 
imap_cmd_copy(IMAPSession * session,const gchar * seq_set,const gchar * destfolder)4426 static gint imap_cmd_copy(IMAPSession *session, const gchar *seq_set,
4427 			  const gchar *destfolder)
4428 {
4429 	gint ok;
4430 	gchar *destfolder_;
4431 
4432 	g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
4433 
4434 	QUOTE_IF_REQUIRED(destfolder_, destfolder);
4435 	ok = imap_cmd_gen_send(session, "UID COPY %s %s", seq_set, destfolder_);
4436 	if (ok == IMAP_SUCCESS)
4437 		ok = imap_cmd_ok(session, NULL);
4438 	if (ok != IMAP_SUCCESS) {
4439 		log_warning(_("can't copy %s to %s\n"), seq_set, destfolder_);
4440 		return -1;
4441 	}
4442 
4443 	return ok;
4444 }
4445 
imap_cmd_envelope(IMAPSession * session,const gchar * seq_set)4446 gint imap_cmd_envelope(IMAPSession *session, const gchar *seq_set)
4447 {
4448 	return imap_cmd_gen_send
4449 		(session, "UID FETCH %s (UID FLAGS RFC822.SIZE RFC822.HEADER)",
4450 		 seq_set);
4451 }
4452 
imap_cmd_store(IMAPSession * session,const gchar * seq_set,const gchar * sub_cmd)4453 static gint imap_cmd_store(IMAPSession *session, const gchar *seq_set,
4454 			   const gchar *sub_cmd)
4455 {
4456 	gint ok;
4457 
4458 	ok = imap_cmd_gen_send(session, "UID STORE %s %s", seq_set, sub_cmd);
4459 	if (ok == IMAP_SUCCESS)
4460 		ok = imap_cmd_ok(session, NULL);
4461 	if (ok != IMAP_SUCCESS) {
4462 		log_warning(_("error while imap command: STORE %s %s\n"),
4463 			    seq_set, sub_cmd);
4464 		return ok;
4465 	}
4466 
4467 	return IMAP_SUCCESS;
4468 }
4469 
imap_cmd_expunge(IMAPSession * session)4470 static gint imap_cmd_expunge(IMAPSession *session)
4471 {
4472 	gint ok;
4473 
4474 	ok = imap_cmd_gen_send(session, "EXPUNGE");
4475 	if (ok == IMAP_SUCCESS)
4476 		ok = imap_cmd_ok(session, NULL);
4477 	if (ok != IMAP_SUCCESS) {
4478 		log_warning(_("error while imap command: EXPUNGE\n"));
4479 		return ok;
4480 	}
4481 
4482 	return IMAP_SUCCESS;
4483 }
4484 
imap_cmd_close(IMAPSession * session)4485 static gint imap_cmd_close(IMAPSession *session)
4486 {
4487 	gint ok;
4488 
4489 	ok = imap_cmd_gen_send(session, "CLOSE");
4490 	if (ok == IMAP_SUCCESS)
4491 		ok = imap_cmd_ok(session, NULL);
4492 	if (ok != IMAP_SUCCESS)
4493 		log_warning(_("error while imap command: CLOSE\n"));
4494 
4495 	return ok;
4496 }
4497 
imap_cmd_ok_real(IMAPSession * session,GPtrArray * argbuf)4498 static gint imap_cmd_ok_real(IMAPSession *session, GPtrArray *argbuf)
4499 {
4500 	gint ok;
4501 	gchar *buf;
4502 	gint cmd_num;
4503 	gchar cmd_status[IMAPBUFSIZE + 1];
4504 	GString *str;
4505 	gchar *p;
4506 	gchar obuf[32];
4507 	gint len;
4508 	gchar *literal;
4509 
4510 	str = g_string_sized_new(256);
4511 
4512 	//g_usleep(800000);
4513 	while ((ok = imap_cmd_gen_recv(session, &buf)) == IMAP_SUCCESS) {
4514 		g_string_append(str, buf);
4515 
4516 		if ((p = strrchr_with_skip_quote(buf, '"', '{'))) {
4517 			/* literal */
4518 			p = strchr_cpy(p + 1, '}', obuf, sizeof(obuf));
4519 			len = atoi(obuf);
4520 			if (len < 0 || p == NULL || *p != '\0') {
4521 				g_free(buf);
4522 				ok = IMAP_ERROR;
4523 				break;
4524 			}
4525 
4526 			literal = recv_bytes(SESSION(session)->sock, len);
4527 			if (!literal) {
4528 				g_free(buf);
4529 				ok = IMAP_SOCKET;
4530 				break;
4531 			}
4532 			if (memchr(literal, '\n', len))
4533 				log_print("IMAP4< (literal: %d bytes)\n", len);
4534 			else
4535 				log_print("IMAP4< %s\n", literal);
4536 
4537 			g_string_append(str, "\r\n");
4538 			g_string_append_len(str, literal, len);
4539 			g_free(literal);
4540 			g_free(buf);
4541 			continue;
4542 		}
4543 
4544 		g_free(buf);
4545 
4546 		if (str->str[0] == '*' && str->str[1] == ' ') {
4547 			if (argbuf)
4548 				g_ptr_array_add(argbuf, g_strdup(str->str + 2));
4549 
4550 			g_string_truncate(str, 0);
4551 			continue;
4552 		} else if (sscanf(str->str, "%d %" Xstr(IMAPBUFSIZE) "s",
4553 			   &cmd_num, cmd_status) < 2) {
4554 			ok = IMAP_ERROR;
4555 		} else if (cmd_num == session->cmd_count &&
4556 			   !strcmp(cmd_status, "OK")) {
4557 			if (argbuf)
4558 				g_ptr_array_add(argbuf, g_strdup(str->str));
4559 		} else {
4560 			ok = IMAP_ERROR;
4561 		}
4562 
4563 		break;
4564 	}
4565 
4566 	g_string_free(str, TRUE);
4567 	return ok;
4568 }
4569 
4570 #if USE_THREADS
imap_cmd_ok_func(IMAPSession * session,gpointer data)4571 static gint imap_cmd_ok_func(IMAPSession *session, gpointer data)
4572 {
4573 	GPtrArray *argbuf = (GPtrArray *)data;
4574 	gint ok;
4575 
4576 	ok = imap_cmd_ok_real(session, argbuf);
4577 	return ok;
4578 }
4579 #endif
4580 
imap_cmd_ok(IMAPSession * session,GPtrArray * argbuf)4581 static gint imap_cmd_ok(IMAPSession *session, GPtrArray *argbuf)
4582 {
4583 #if USE_THREADS
4584 	return imap_thread_run(session, imap_cmd_ok_func, argbuf);
4585 #else
4586 	return imap_cmd_ok_real(session, argbuf);
4587 #endif
4588 }
4589 
imap_cmd_gen_send(IMAPSession * session,const gchar * format,...)4590 static gint imap_cmd_gen_send(IMAPSession *session, const gchar *format, ...)
4591 {
4592 	IMAPRealSession *real = (IMAPRealSession *)session;
4593 	gchar buf[IMAPBUFSIZE];
4594 	gchar tmp[IMAPBUFSIZE];
4595 	gchar *p;
4596 	va_list args;
4597 
4598 	va_start(args, format);
4599 	g_vsnprintf(tmp, sizeof(tmp), format, args);
4600 	va_end(args);
4601 
4602 #if USE_THREADS
4603 	if (real->is_running) {
4604 		g_warning("imap_cmd_gen_send: cannot send command because another command is already running.");
4605 		return IMAP_EAGAIN;
4606 	}
4607 #endif
4608 
4609 	session->cmd_count++;
4610 
4611 	g_snprintf(buf, sizeof(buf), "%d %s\r\n", session->cmd_count, tmp);
4612 	if (!g_ascii_strncasecmp(tmp, "LOGIN ", 6) &&
4613 	    (p = strchr(tmp + 6, ' '))) {
4614 		*p = '\0';
4615 		log_print("IMAP4> %d %s ********\n", session->cmd_count, tmp);
4616 	} else
4617 		log_print("IMAP4> %d %s\n", session->cmd_count, tmp);
4618 
4619 	sock_write_all(SESSION(session)->sock, buf, strlen(buf));
4620 
4621 	return IMAP_SUCCESS;
4622 }
4623 
imap_cmd_gen_recv(IMAPSession * session,gchar ** ret)4624 static gint imap_cmd_gen_recv(IMAPSession *session, gchar **ret)
4625 {
4626 	gint len;
4627 
4628 	if ((len = sock_getline(SESSION(session)->sock, ret)) < 0)
4629 		return IMAP_SOCKET;
4630 
4631 	strretchomp(*ret);
4632 
4633 	if (len > 1000) {
4634 		gchar *str;
4635 
4636 		str = trim_string(*ret, 1000);
4637 		log_print("IMAP4< %s\n", str);
4638 		g_free(str);
4639 	} else
4640 		log_print("IMAP4< %s\n", *ret);
4641 
4642 	session_set_access_time(SESSION(session));
4643 
4644 	return IMAP_SUCCESS;
4645 }
4646 
imap_cmd_gen_recv_silent(IMAPSession * session,gchar ** ret)4647 static gint imap_cmd_gen_recv_silent(IMAPSession *session, gchar **ret)
4648 {
4649 	gint len;
4650 
4651 	if ((len = sock_getline(SESSION(session)->sock, ret)) < 0)
4652 		return IMAP_SOCKET;
4653 
4654 	strretchomp(*ret);
4655 
4656 	session_set_access_time(SESSION(session));
4657 
4658 	return IMAP_SUCCESS;
4659 }
4660 
4661 
4662 /* misc utility functions */
4663 
strchr_cpy(const gchar * src,gchar ch,gchar * dest,gint len)4664 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
4665 {
4666 	gchar *tmp;
4667 
4668 	dest[0] = '\0';
4669 	tmp = strchr(src, ch);
4670 	if (!tmp)
4671 		return NULL;
4672 
4673 	memcpy(dest, src, MIN(tmp - src, len - 1));
4674 	dest[MIN(tmp - src, len - 1)] = '\0';
4675 
4676 	return tmp + 1;
4677 }
4678 
get_quoted(const gchar * src,gchar ch,gchar * dest,gint len)4679 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
4680 {
4681 	const gchar *p = src;
4682 	gint n = 0;
4683 
4684 	g_return_val_if_fail(*p == ch, NULL);
4685 
4686 	*dest = '\0';
4687 	p++;
4688 
4689 	while (*p != '\0' && *p != ch) {
4690 		if (n < len - 1) {
4691 			if (*p == '\\' && *(p + 1) != '\0')
4692 				p++;
4693 			*dest++ = *p++;
4694 		} else
4695 			p++;
4696 		n++;
4697 	}
4698 
4699 	*dest = '\0';
4700 	return (gchar *)(*p == ch ? p + 1 : p);
4701 }
4702 
search_array_contain_str(GPtrArray * array,gchar * str)4703 static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
4704 {
4705 	gint i;
4706 
4707 	for (i = 0; i < array->len; i++) {
4708 		gchar *tmp;
4709 
4710 		tmp = g_ptr_array_index(array, i);
4711 		if (strstr(tmp, str) != NULL)
4712 			return tmp;
4713 	}
4714 
4715 	return NULL;
4716 }
4717 
search_array_str(GPtrArray * array,gchar * str)4718 static gchar *search_array_str(GPtrArray *array, gchar *str)
4719 {
4720 	gint i;
4721 	gint len;
4722 
4723 	len = strlen(str);
4724 
4725 	for (i = 0; i < array->len; i++) {
4726 		gchar *tmp;
4727 
4728 		tmp = g_ptr_array_index(array, i);
4729 		if (!strncmp(tmp, str, len))
4730 			return tmp;
4731 	}
4732 
4733 	return NULL;
4734 }
4735 
imap_path_separator_subst(gchar * str,gchar separator)4736 static void imap_path_separator_subst(gchar *str, gchar separator)
4737 {
4738 	gchar *p;
4739 	gboolean in_escape = FALSE;
4740 
4741 	if (!separator || separator == '/') return;
4742 
4743 	for (p = str; *p != '\0'; p++) {
4744 		if (*p == '/' && !in_escape)
4745 			*p = separator;
4746 		else if (*p == '&' && *(p + 1) != '-' && !in_escape)
4747 			in_escape = TRUE;
4748 		else if (*p == '-' && in_escape)
4749 			in_escape = FALSE;
4750 	}
4751 }
4752 
imap_modified_utf7_to_utf8(const gchar * mutf7_str)4753 static gchar *imap_modified_utf7_to_utf8(const gchar *mutf7_str)
4754 {
4755 	static iconv_t cd = (iconv_t)-1;
4756 	static gboolean iconv_ok = TRUE;
4757 	GString *norm_utf7;
4758 	gchar *norm_utf7_p;
4759 	size_t norm_utf7_len;
4760 	const gchar *p;
4761 	gchar *to_str, *to_p;
4762 	size_t to_len;
4763 	gboolean in_escape = FALSE;
4764 
4765 	if (!iconv_ok) return g_strdup(mutf7_str);
4766 
4767 	if (cd == (iconv_t)-1) {
4768 		cd = iconv_open(CS_INTERNAL, CS_UTF_7);
4769 		if (cd == (iconv_t)-1) {
4770 			g_warning("iconv cannot convert UTF-7 to %s\n",
4771 				  CS_INTERNAL);
4772 			iconv_ok = FALSE;
4773 			return g_strdup(mutf7_str);
4774 		}
4775 	}
4776 
4777 	/* modified UTF-7 to normal UTF-7 conversion */
4778 	norm_utf7 = g_string_new(NULL);
4779 
4780 	for (p = mutf7_str; *p != '\0'; p++) {
4781 		/* replace: '&'  -> '+',
4782 			    "&-" -> '&',
4783 			    "+"  -> "+-",
4784 			    escaped ','  -> '/' */
4785 		if (!in_escape && *p == '&') {
4786 			if (*(p + 1) != '-') {
4787 				g_string_append_c(norm_utf7, '+');
4788 				in_escape = TRUE;
4789 			} else {
4790 				g_string_append_c(norm_utf7, '&');
4791 				p++;
4792 			}
4793 		} else if (!in_escape && *p == '+') {
4794 			g_string_append(norm_utf7, "+-");
4795 		} else if (in_escape && *p == ',') {
4796 			g_string_append_c(norm_utf7, '/');
4797 		} else if (in_escape && *p == '-') {
4798 			g_string_append_c(norm_utf7, '-');
4799 			in_escape = FALSE;
4800 		} else {
4801 			g_string_append_c(norm_utf7, *p);
4802 		}
4803 	}
4804 
4805 	/* somehow iconv() returns error when the last of the string is "+-" */
4806 	g_string_append_c(norm_utf7, '\n');
4807 	norm_utf7_p = norm_utf7->str;
4808 	norm_utf7_len = norm_utf7->len;
4809 	to_len = strlen(mutf7_str) * 5;
4810 	to_p = to_str = g_malloc(to_len + 1);
4811 
4812 	if (iconv(cd, (ICONV_CONST gchar **)&norm_utf7_p, &norm_utf7_len,
4813 		  &to_p, &to_len) == -1) {
4814 		g_warning(_("iconv cannot convert UTF-7 to %s\n"), CS_INTERNAL);
4815 		g_string_free(norm_utf7, TRUE);
4816 		g_free(to_str);
4817 		return g_strdup(mutf7_str);
4818 	}
4819 
4820 	/* second iconv() call for flushing */
4821 	iconv(cd, NULL, NULL, &to_p, &to_len);
4822 	g_string_free(norm_utf7, TRUE);
4823 	*to_p = '\0';
4824 	strretchomp(to_str);
4825 
4826 	return to_str;
4827 }
4828 
imap_utf8_to_modified_utf7(const gchar * from)4829 static gchar *imap_utf8_to_modified_utf7(const gchar *from)
4830 {
4831 	static iconv_t cd = (iconv_t)-1;
4832 	static gboolean iconv_ok = TRUE;
4833 	gchar *norm_utf7, *norm_utf7_p;
4834 	size_t from_len, norm_utf7_len;
4835 	GString *to_str;
4836 	const gchar *from_tmp;
4837 	const gchar *p;
4838 	gchar *to;
4839 	gboolean in_escape = FALSE;
4840 
4841 	if (!iconv_ok) return g_strdup(from);
4842 
4843 	if (cd == (iconv_t)-1) {
4844 		cd = iconv_open(CS_UTF_7, CS_INTERNAL);
4845 		if (cd == (iconv_t)-1) {
4846 			g_warning(_("iconv cannot convert %s to UTF-7\n"),
4847 				  CS_INTERNAL);
4848 			iconv_ok = FALSE;
4849 			return g_strdup(from);
4850 		}
4851 	}
4852 
4853 	/* UTF-8 to normal UTF-7 conversion */
4854 	from_tmp = from;
4855 	from_len = strlen(from);
4856 	norm_utf7_len = from_len * 5;
4857 	norm_utf7 = g_malloc(norm_utf7_len + 1);
4858 	norm_utf7_p = norm_utf7;
4859 
4860 	while (from_len > 0) {
4861 		if (*from_tmp == '+') {
4862 			*norm_utf7_p++ = '+';
4863 			*norm_utf7_p++ = '-';
4864 			norm_utf7_len -= 2;
4865 			from_tmp++;
4866 			from_len--;
4867 		} else if (g_ascii_isprint(*from_tmp)) {
4868 			/* printable ascii char */
4869 			*norm_utf7_p = *from_tmp;
4870 			norm_utf7_p++;
4871 			norm_utf7_len--;
4872 			from_tmp++;
4873 			from_len--;
4874 		} else {
4875 			size_t conv_len = 0;
4876 
4877 			/* unprintable char: convert to UTF-7 */
4878 			p = from_tmp;
4879 			while (!g_ascii_isprint(*p) && conv_len < from_len) {
4880 				conv_len += g_utf8_skip[*(guchar *)p];
4881 				p += g_utf8_skip[*(guchar *)p];
4882 			}
4883 
4884 			from_len -= conv_len;
4885 			if (iconv(cd, (ICONV_CONST gchar **)&from_tmp,
4886 				  &conv_len,
4887 				  &norm_utf7_p, &norm_utf7_len) == -1) {
4888 				g_warning("iconv cannot convert %s to UTF-7\n",
4889 					  CS_INTERNAL);
4890 				g_free(norm_utf7);
4891 				return g_strdup(from);
4892 			}
4893 
4894 			/* second iconv() call for flushing */
4895 			iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
4896 		}
4897 	}
4898 
4899 	*norm_utf7_p = '\0';
4900 	to_str = g_string_new(NULL);
4901 	for (p = norm_utf7; p < norm_utf7_p; p++) {
4902 		/* replace: '&' -> "&-",
4903 			    '+' -> '&',
4904 			    "+-" -> '+',
4905 			    BASE64 '/' -> ',' */
4906 		if (!in_escape && *p == '&') {
4907 			g_string_append(to_str, "&-");
4908 		} else if (!in_escape && *p == '+') {
4909 			if (*(p + 1) == '-') {
4910 				g_string_append_c(to_str, '+');
4911 				p++;
4912 			} else {
4913 				g_string_append_c(to_str, '&');
4914 				in_escape = TRUE;
4915 			}
4916 		} else if (in_escape && *p == '/') {
4917 			g_string_append_c(to_str, ',');
4918 		} else if (in_escape && *p == '-') {
4919 			g_string_append_c(to_str, '-');
4920 			in_escape = FALSE;
4921 		} else {
4922 			g_string_append_c(to_str, *p);
4923 		}
4924 	}
4925 
4926 	if (in_escape) {
4927 		in_escape = FALSE;
4928 		g_string_append_c(to_str, '-');
4929 	}
4930 
4931 	to = g_string_free(to_str, FALSE);
4932 	g_free(norm_utf7);
4933 
4934 	return to;
4935 }
4936 
imap_get_seq_set_from_msglist(GSList * msglist,gint limit)4937 static GSList *imap_get_seq_set_from_msglist(GSList *msglist, gint limit)
4938 {
4939 	GString *str;
4940 	GSList *sorted_list, *cur;
4941 	guint first, last, next;
4942 	gchar *ret_str;
4943 	GSList *ret_list = NULL;
4944 	gint count = 0;
4945 
4946 	if (msglist == NULL)
4947 		return NULL;
4948 
4949 	str = g_string_sized_new(256);
4950 
4951 	sorted_list = g_slist_copy(msglist);
4952 	sorted_list = procmsg_sort_msg_list(sorted_list, SORT_BY_NUMBER,
4953 					    SORT_ASCENDING);
4954 
4955 	first = ((MsgInfo *)sorted_list->data)->msgnum;
4956 
4957 	for (cur = sorted_list; cur != NULL; cur = cur->next) {
4958 		++count;
4959 		last = ((MsgInfo *)cur->data)->msgnum;
4960 		if (cur->next)
4961 			next = ((MsgInfo *)cur->next->data)->msgnum;
4962 		else
4963 			next = 0;
4964 
4965 		if (limit > 0 && count >= limit) {
4966 			if (str->len > 0)
4967 				g_string_append_c(str, ',');
4968 			if (first == last)
4969 				g_string_sprintfa(str, "%u", first);
4970 			else
4971 				g_string_sprintfa(str, "%u:%u", first, last);
4972 
4973 			first = next;
4974 
4975 			ret_str = g_strdup(str->str);
4976 			ret_list = g_slist_append(ret_list, ret_str);
4977 			g_string_truncate(str, 0);
4978 			count = 0;
4979 			continue;
4980 		}
4981 
4982 		if (last + 1 != next || next == 0) {
4983 			if (str->len > 0)
4984 				g_string_append_c(str, ',');
4985 			if (first == last)
4986 				g_string_sprintfa(str, "%u", first);
4987 			else
4988 				g_string_sprintfa(str, "%u:%u", first, last);
4989 
4990 			first = next;
4991 
4992 			if (str->len > IMAP_CMD_LIMIT) {
4993 				ret_str = g_strdup(str->str);
4994 				ret_list = g_slist_append(ret_list, ret_str);
4995 				g_string_truncate(str, 0);
4996 			}
4997 		}
4998 	}
4999 
5000 	if (str->len > 0) {
5001 		ret_str = g_strdup(str->str);
5002 		ret_list = g_slist_append(ret_list, ret_str);
5003 	}
5004 
5005 	g_slist_free(sorted_list);
5006 	g_string_free(str, TRUE);
5007 
5008 	return ret_list;
5009 }
5010 
imap_seq_set_get_count(const gchar * seq_set)5011 static gint imap_seq_set_get_count(const gchar *seq_set)
5012 {
5013 	gint count = 0;
5014 	guint first, last;
5015 	gchar *tmp, *p, *q;
5016 
5017 	p = q = tmp = g_strdup(seq_set);
5018 
5019 	while (*p) {
5020 		if (*p == ',') {
5021 			*p = '\0';
5022 			if (sscanf(q, "%u:%u", &first, &last) == 2)
5023 				count += last - first + 1;
5024 			else if (sscanf(q, "%u", &first) == 1)
5025 				count++;
5026 			q = ++p;
5027 		} else
5028 			++p;
5029 	}
5030 	if (q != p) {
5031 		if (sscanf(q, "%u:%u", &first, &last) == 2)
5032 			count += last - first + 1;
5033 		else if (sscanf(q, "%u", &first) == 1)
5034 			count++;
5035 	}
5036 
5037 	g_free(tmp);
5038 
5039 	return count;
5040 }
5041 
imap_seq_set_free(GSList * seq_list)5042 static void imap_seq_set_free(GSList *seq_list)
5043 {
5044 	slist_free_strings(seq_list);
5045 	g_slist_free(seq_list);
5046 }
5047 
imap_get_uid_table(GArray * array)5048 static GHashTable *imap_get_uid_table(GArray *array)
5049 {
5050 	GHashTable *table;
5051 	gint i;
5052 	guint32 uid;
5053 
5054 	g_return_val_if_fail(array != NULL, NULL);
5055 
5056 	table = g_hash_table_new(NULL, g_direct_equal);
5057 
5058 	for (i = 0; i < array->len; i++) {
5059 		uid = g_array_index(array, guint32, i);
5060 		g_hash_table_insert(table, GUINT_TO_POINTER(uid),
5061 				    GINT_TO_POINTER(i + 1));
5062 	}
5063 
5064 	return table;
5065 }
5066 
imap_rename_folder_func(GNode * node,gpointer data)5067 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
5068 {
5069 	FolderItem *item = node->data;
5070 	gchar **paths = data;
5071 	const gchar *oldpath = paths[0];
5072 	const gchar *newpath = paths[1];
5073 	gchar *base;
5074 	gchar *new_itempath;
5075 	gint oldpathlen;
5076 
5077 	oldpathlen = strlen(oldpath);
5078 	if (strncmp(oldpath, item->path, oldpathlen) != 0) {
5079 		g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
5080 		return TRUE;
5081 	}
5082 
5083 	base = item->path + oldpathlen;
5084 	while (*base == '/') base++;
5085 	if (*base == '\0')
5086 		new_itempath = g_strdup(newpath);
5087 	else
5088 		new_itempath = g_strconcat(newpath, "/", base, NULL);
5089 	g_free(item->path);
5090 	item->path = new_itempath;
5091 
5092 	return FALSE;
5093 }
5094 
5095 #if USE_THREADS
imap_thread_run_proxy(gpointer push_data,gpointer data)5096 static void imap_thread_run_proxy(gpointer push_data, gpointer data)
5097 {
5098 	IMAPRealSession *real = (IMAPRealSession *)data;
5099 
5100 	debug_print("imap_thread_run_proxy (%p): calling thread_func\n", g_thread_self());
5101 	real->retval = real->thread_func(IMAP_SESSION(real), real->thread_data);
5102 	g_atomic_int_set(&real->flag, 1);
5103 	g_main_context_wakeup(NULL);
5104 	debug_print("imap_thread_run_proxy (%p): thread_func done\n", g_thread_self());
5105 }
5106 
imap_thread_run(IMAPSession * session,IMAPThreadFunc func,gpointer data)5107 static gint imap_thread_run(IMAPSession	*session, IMAPThreadFunc func,
5108 			    gpointer data)
5109 {
5110 	IMAPRealSession *real = (IMAPRealSession *)session;
5111 	gint ret;
5112 
5113 	if (real->is_running) {
5114 		g_warning("imap_thread_run: thread is already running");
5115 		return IMAP_ERROR;
5116 	}
5117 
5118 	if (!real->pool) {
5119 		real->pool = g_thread_pool_new(imap_thread_run_proxy, real,
5120 					       -1, FALSE, NULL);
5121 		if (!real->pool)
5122 			return IMAP_ERROR;
5123 	}
5124 
5125 	real->is_running = TRUE;
5126 	real->thread_func = func;
5127 	real->thread_data = data;
5128 	real->flag = 0;
5129 	real->retval = 0;
5130 
5131 	g_thread_pool_push(real->pool, real, NULL);
5132 
5133 	while (g_atomic_int_get(&real->flag) == 0)
5134 		event_loop_iterate();
5135 
5136 	real->is_running = FALSE;
5137 	real->thread_func = NULL;
5138 	real->thread_data = NULL;
5139 	real->flag = 0;
5140 	ret = real->retval;
5141 	real->retval = 0;
5142 	log_flush();
5143 
5144 	return ret;
5145 }
5146 
imap_thread_run_progress(IMAPSession * session,IMAPThreadFunc func,IMAPProgressFunc progress_func,gpointer data)5147 static gint imap_thread_run_progress(IMAPSession *session, IMAPThreadFunc func,
5148 				     IMAPProgressFunc progress_func,
5149 				     gpointer data)
5150 {
5151 	IMAPRealSession *real = (IMAPRealSession *)session;
5152 	gint prev_count = 0;
5153 	gint ret;
5154 
5155 	if (real->is_running) {
5156 		g_warning("imap_thread_run: thread is already running");
5157 		return IMAP_ERROR;
5158 	}
5159 
5160 	if (!real->pool) {
5161 		real->pool = g_thread_pool_new(imap_thread_run_proxy, real,
5162 					       -1, FALSE, NULL);
5163 		if (!real->pool)
5164 			return IMAP_ERROR;
5165 	}
5166 
5167 	real->is_running = TRUE;
5168 	real->thread_func = func;
5169 	real->thread_data = data;
5170 	real->flag = 0;
5171 	real->retval = 0;
5172 	real->prog_count = 0;
5173 	real->prog_total = 0;
5174 
5175 	g_thread_pool_push(real->pool, real, NULL);
5176 
5177 	while (g_atomic_int_get(&real->flag) == 0) {
5178 		event_loop_iterate();
5179 		if (prev_count != real->prog_count && real->prog_total > 0) {
5180 			progress_func(session, real->prog_count,
5181 				      real->prog_total, data);
5182 			prev_count = real->prog_count;
5183 		}
5184 	}
5185 
5186 	real->is_running = FALSE;
5187 	real->thread_func = NULL;
5188 	real->thread_data = NULL;
5189 	real->flag = 0;
5190 	ret = real->retval;
5191 	real->retval = 0;
5192 	real->prog_count = 0;
5193 	real->prog_total = 0;
5194 	log_flush();
5195 
5196 	return ret;
5197 }
5198 #endif /* USE_THREADS */
5199 
imap_is_session_active(IMAPFolder * folder)5200 gboolean imap_is_session_active(IMAPFolder *folder)
5201 {
5202 #if USE_THREADS
5203 	IMAPRealSession *real;
5204 
5205 	g_return_val_if_fail(folder != NULL, FALSE);
5206 
5207 	real = (IMAPRealSession *)(REMOTE_FOLDER(folder)->session);
5208 	if (!real)
5209 		return FALSE;
5210 
5211 	return real->is_running;
5212 #else
5213 	return FALSE;
5214 #endif
5215 }
5216