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