This commit was manufactured by cvs2svn to create tag 'LAST_STABLE'.
[claws.git] / src / imap.c
blob823d3a76f6db8618dedbc35c7ce8afd1ce517b26
1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2002 Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
24 #include "defs.h"
26 #include <glib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <dirent.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <time.h>
34 #if HAVE_LIBJCONV
35 # include <jconv.h>
36 #endif
38 #include "intl.h"
39 #include "imap.h"
40 #include "socket.h"
41 #include "ssl.h"
42 #include "recv.h"
43 #include "procmsg.h"
44 #include "procheader.h"
45 #include "folder.h"
46 #include "prefs_account.h"
47 #include "codeconv.h"
48 #include "utils.h"
49 #include "inputdialog.h"
50 #include "log.h"
52 #define IMAP4_PORT 143
53 #if USE_OPENSSL
54 #define IMAPS_PORT 993
55 #endif
57 #define QUOTE_IF_REQUIRED(out, str) \
58 { \
59 if (*str != '"' && strpbrk(str, " \t(){}%*") != NULL) { \
60 gchar *__tmp; \
61 gint len; \
63 len = strlen(str) + 3; \
64 Xalloca(__tmp, len, return IMAP_ERROR); \
65 g_snprintf(__tmp, len, "\"%s\"", str); \
66 out = __tmp; \
67 } else { \
68 Xstrdup_a(out, str, return IMAP_ERROR); \
69 } \
72 struct _IMAPFolderItem
74 FolderItem item;
76 guint lastuid;
77 GSList *uid_list;
80 static GList *session_list = NULL;
82 static gint imap_cmd_count = 0;
84 static void imap_folder_init (Folder *folder,
85 const gchar *name,
86 const gchar *path);
88 static FolderItem *imap_folder_item_new (Folder *folder);
89 static void imap_folder_item_destroy (Folder *folder,
90 FolderItem *item);
92 static IMAPSession *imap_session_get (Folder *folder);
94 static gint imap_scan_tree_recursive (IMAPSession *session,
95 FolderItem *item);
96 static GSList *imap_parse_list (Folder *folder,
97 IMAPSession *session,
98 const gchar *real_path,
99 gchar *separator);
101 static void imap_create_missing_folders (Folder *folder);
102 static FolderItem *imap_create_special_folder
103 (Folder *folder,
104 SpecialFolderItemType stype,
105 const gchar *name);
107 static gint imap_do_copy (Folder *folder,
108 FolderItem *dest,
109 MsgInfo *msginfo,
110 gboolean remove_source);
111 static gint imap_do_copy_msgs_with_dest (Folder *folder,
112 FolderItem *dest,
113 GSList *msglist,
114 gboolean remove_source);
116 static GSList *imap_get_uncached_messages (IMAPSession *session,
117 FolderItem *item,
118 guint32 first_uid,
119 guint32 last_uid);
120 static GSList *imap_delete_cached_messages (GSList *mlist,
121 FolderItem *item,
122 guint32 first_uid,
123 guint32 last_uid);
124 static void imap_delete_all_cached_messages (FolderItem *item);
126 #if USE_OPENSSL
127 static SockInfo *imap_open (const gchar *server,
128 gushort port,
129 SSLType ssl_type);
130 #else
131 static SockInfo *imap_open (const gchar *server,
132 gushort port);
133 #endif
135 #if USE_OPENSSL
136 static SockInfo *imap_open_tunnel(const gchar *server,
137 const gchar *tunnelcmd,
138 SSLType ssl_type);
139 #else
140 static SockInfo *imap_open_tunnel(const gchar *server,
141 const gchar *tunnelcmd);
142 #endif
144 #if USE_OPENSSL
145 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type);
146 #else
147 static SockInfo *imap_init_sock(SockInfo *sock);
148 #endif
150 static gint imap_set_message_flags (IMAPSession *session,
151 guint32 first_uid,
152 guint32 last_uid,
153 IMAPFlags flag,
154 gboolean is_set);
155 static gint imap_select (IMAPSession *session,
156 IMAPFolder *folder,
157 const gchar *path,
158 gint *exists,
159 gint *recent,
160 gint *unseen,
161 guint32 *uid_validity);
162 static gint imap_get_uid (IMAPSession *session,
163 gint msgnum,
164 guint32 *uid);
165 static gint imap_status (IMAPSession *session,
166 IMAPFolder *folder,
167 const gchar *path,
168 gint *messages,
169 gint *recent,
170 guint32 *uid_next,
171 guint32 *uid_validity,
172 gint *unseen);
174 static void imap_parse_namespace (IMAPSession *session,
175 IMAPFolder *folder);
176 static void imap_get_namespace_by_list (IMAPSession *session,
177 IMAPFolder *folder);
178 static IMAPNameSpace *imap_find_namespace (IMAPFolder *folder,
179 const gchar *path);
180 static gchar imap_get_path_separator (IMAPFolder *folder,
181 const gchar *path);
182 static gchar *imap_get_real_path (IMAPFolder *folder,
183 const gchar *path);
185 static gchar *imap_parse_atom (SockInfo *sock,
186 gchar *src,
187 gchar *dest,
188 gint dest_len,
189 GString *str);
190 static MsgFlags imap_parse_flags (const gchar *flag_str);
191 static MsgInfo *imap_parse_envelope (SockInfo *sock,
192 FolderItem *item,
193 GString *line_str);
194 static gint imap_greeting (SockInfo *sock,
195 gboolean *is_preauth);
197 /* low-level IMAP4rev1 commands */
198 static gint imap_cmd_login (SockInfo *sock,
199 const gchar *user,
200 const gchar *pass);
201 static gint imap_cmd_logout (SockInfo *sock);
202 static gint imap_cmd_noop (SockInfo *sock);
203 static gint imap_cmd_starttls (SockInfo *sock);
204 static gint imap_cmd_namespace (SockInfo *sock,
205 gchar **ns_str);
206 static gint imap_cmd_list (SockInfo *sock,
207 const gchar *ref,
208 const gchar *mailbox,
209 GPtrArray *argbuf);
210 static gint imap_cmd_do_select (SockInfo *sock,
211 const gchar *folder,
212 gboolean examine,
213 gint *exists,
214 gint *recent,
215 gint *unseen,
216 guint32 *uid_validity);
217 static gint imap_cmd_select (SockInfo *sock,
218 const gchar *folder,
219 gint *exists,
220 gint *recent,
221 gint *unseen,
222 guint32 *uid_validity);
223 static gint imap_cmd_examine (SockInfo *sock,
224 const gchar *folder,
225 gint *exists,
226 gint *recent,
227 gint *unseen,
228 guint32 *uid_validity);
229 static gint imap_cmd_create (SockInfo *sock,
230 const gchar *folder);
231 static gint imap_cmd_rename (SockInfo *sock,
232 const gchar *oldfolder,
233 const gchar *newfolder);
234 static gint imap_cmd_delete (SockInfo *sock,
235 const gchar *folder);
236 static gint imap_cmd_envelope (SockInfo *sock,
237 guint32 first_uid,
238 guint32 last_uid);
239 #if 0
240 static gint imap_cmd_search (SockInfo *sock,
241 GSList *numlist);
242 #endif
243 static gint imap_cmd_fetch (SockInfo *sock,
244 guint32 uid,
245 const gchar *filename);
246 static gint imap_cmd_append (SockInfo *sock,
247 const gchar *destfolder,
248 const gchar *file);
249 static gint imap_cmd_copy (SockInfo *sock,
250 guint32 uid,
251 const gchar *destfolder);
252 static gint imap_cmd_store (SockInfo *sock,
253 guint32 first_uid,
254 guint32 last_uid,
255 gchar *sub_cmd);
256 static gint imap_cmd_expunge (SockInfo *sock);
258 static gint imap_cmd_ok (SockInfo *sock,
259 GPtrArray *argbuf);
260 static void imap_cmd_gen_send (SockInfo *sock,
261 const gchar *format, ...);
262 static gint imap_cmd_gen_recv (SockInfo *sock,
263 gchar *buf,
264 gint size);
266 /* misc utility functions */
267 static gchar *strchr_cpy (const gchar *src,
268 gchar ch,
269 gchar *dest,
270 gint len);
271 static gchar *get_quoted (const gchar *src,
272 gchar ch,
273 gchar *dest,
274 gint len);
275 static gchar *search_array_contain_str (GPtrArray *array,
276 gchar *str);
277 static gchar *search_array_str (GPtrArray *array,
278 gchar *str);
279 static gchar *search_array_starts (GPtrArray *array,
280 const gchar *str);
281 static void imap_path_separator_subst (gchar *str,
282 gchar separator);
284 static gchar *imap_modified_utf7_to_locale (const gchar *mutf7_str);
285 static gchar *imap_locale_to_modified_utf7 (const gchar *from);
287 static gboolean imap_rename_folder_func (GNode *node,
288 gpointer data);
289 gint imap_get_num_list (Folder *folder,
290 FolderItem *item,
291 GSList **list);
292 MsgInfo *imap_get_msginfo (Folder *folder,
293 FolderItem *item,
294 gint num);
295 gboolean imap_check_msgnum_validity (Folder *folder,
296 FolderItem *item);
298 Folder *imap_folder_new(const gchar *name, const gchar *path)
300 Folder *folder;
302 folder = (Folder *)g_new0(IMAPFolder, 1);
303 imap_folder_init(folder, name, path);
305 return folder;
308 void imap_folder_destroy(Folder *folder)
310 gchar *dir;
312 dir = folder_get_path(folder);
313 if (is_dir_exist(dir))
314 remove_dir_recursive(dir);
315 g_free(dir);
317 folder_remote_folder_destroy(REMOTE_FOLDER(folder));
320 static void imap_folder_init(Folder *folder, const gchar *name,
321 const gchar *path)
323 folder->type = F_IMAP;
325 folder_remote_folder_init((Folder *)folder, name, path);
328 folder->get_msg_list = imap_get_msg_list;
330 folder->item_new = imap_folder_item_new;
331 folder->item_destroy = imap_folder_item_destroy;
332 folder->fetch_msg = imap_fetch_msg;
333 folder->add_msg = imap_add_msg;
334 folder->move_msg = imap_move_msg;
335 folder->move_msgs_with_dest = imap_move_msgs_with_dest;
336 folder->copy_msg = imap_copy_msg;
337 folder->copy_msgs_with_dest = imap_copy_msgs_with_dest;
338 folder->remove_msg = imap_remove_msg;
339 folder->remove_msgs = imap_remove_msgs;
340 folder->remove_all_msg = imap_remove_all_msg;
341 folder->is_msg_changed = imap_is_msg_changed;
343 folder->scan = imap_scan_folder;
345 folder->scan_tree = imap_scan_tree;
346 folder->create_tree = imap_create_tree;
347 folder->create_folder = imap_create_folder;
348 folder->rename_folder = imap_rename_folder;
349 folder->remove_folder = imap_remove_folder;
350 folder->destroy = imap_folder_destroy;
351 folder->check_msgnum_validity = imap_check_msgnum_validity;
353 folder->get_num_list = imap_get_num_list;
354 folder->get_msginfo = imap_get_msginfo;
357 static FolderItem *imap_folder_item_new(Folder *folder)
359 IMAPFolderItem *item;
361 item = g_new0(IMAPFolderItem, 1);
362 item->lastuid = 0;
363 item->uid_list = NULL;
365 return (FolderItem *)item;
368 static void imap_folder_item_destroy(Folder *folder, FolderItem *_item)
370 IMAPFolderItem *item = (IMAPFolderItem *)_item;
372 g_return_if_fail(item != NULL);
373 g_slist_free(item->uid_list);
375 g_free(_item);
378 static gboolean imap_reset_uid_lists_func(GNode *node, gpointer data)
380 IMAPFolderItem *item = (IMAPFolderItem *)node->data;
382 item->lastuid = 0;
383 g_slist_free(item->uid_list);
384 item->uid_list = NULL;
386 return FALSE;
389 static void imap_reset_uid_lists(Folder *folder)
391 if(folder->node == NULL)
392 return;
394 /* Destroy all uid lists and rest last uid */
395 g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, imap_reset_uid_lists_func, NULL);
398 static IMAPSession *imap_session_get(Folder *folder)
400 RemoteFolder *rfolder = REMOTE_FOLDER(folder);
401 Session *session = NULL;
402 gushort port;
404 g_return_val_if_fail(folder != NULL, NULL);
405 g_return_val_if_fail(folder->type == F_IMAP, NULL);
406 g_return_val_if_fail(folder->account != NULL, NULL);
408 #if USE_OPENSSL
409 port = folder->account->set_imapport ? folder->account->imapport
410 : folder->account->ssl_imap == SSL_TUNNEL
411 ? IMAPS_PORT : IMAP4_PORT;
412 #else
413 port = folder->account->set_imapport ? folder->account->imapport
414 : IMAP4_PORT;
415 #endif
417 /* Make sure we have a session */
418 if (rfolder->session != NULL) {
419 session = rfolder->session;
420 } else {
421 imap_reset_uid_lists(folder);
422 session = imap_session_new(folder->account);
423 session->last_access_time = time(NULL);
425 if(!session) {
426 return NULL;
429 /* Make sure session is authenticated */
430 if (!IMAP_SESSION(session)->authenticated)
431 imap_session_authenticate(IMAP_SESSION(session), folder->account);
432 if (!IMAP_SESSION(session)->authenticated) {
433 session_destroy(session);
434 rfolder->session = NULL;
435 return NULL;
438 /* Make sure we have parsed the IMAP namespace */
439 imap_parse_namespace(IMAP_SESSION(session),
440 IMAP_FOLDER(folder));
442 /* I think the point of this code is to avoid sending a
443 * keepalive if we've used the session recently and therefore
444 * think it's still alive. Unfortunately, most of the code
445 * does not yet check for errors on the socket, and so if the
446 * connection drops we don't notice until the timeout expires.
447 * A better solution than sending a NOOP every time would be
448 * for every command to be prepared to retry until it is
449 * successfully sent. -- mbp */
450 if (time(NULL) - session->last_access_time > SESSION_TIMEOUT) {
451 /* verify that the session is still alive */
452 if (imap_cmd_noop(session->sock) != IMAP_SUCCESS) {
453 /* Check if this is the first try to establish a
454 connection, if yes we don't try to reconnect */
455 if (rfolder->session == NULL) {
456 log_warning(_("Connecting %s:%d failed"),
457 folder->account->recv_server, port);
458 session_destroy(session);
459 session = NULL;
460 } else {
461 log_warning(_("IMAP4 connection to %s:%d has been"
462 " disconnected. Reconnecting...\n"),
463 folder->account->recv_server, port);
464 session_destroy(session);
465 /* Clear folders session to make imap_session_get create
466 a new session, because of rfolder->session == NULL
467 it will not try to reconnect again and so avoid an
468 endless loop */
469 rfolder->session = NULL;
470 session = SESSION(imap_session_get(folder));
475 rfolder->session = session;
476 if (session) {
477 session->last_access_time = time(NULL);
479 return IMAP_SESSION(session);
482 Session *imap_session_new(const PrefsAccount *account)
484 IMAPSession *session;
485 SockInfo *imap_sock;
486 gushort port;
487 gboolean is_preauth;
489 #ifdef USE_OPENSSL
490 /* FIXME: IMAP over SSL only... */
491 SSLType ssl_type;
493 port = account->set_imapport ? account->imapport
494 : account->ssl_imap ? IMAPS_PORT : IMAP4_PORT;
495 ssl_type = account->ssl_imap ? TRUE : FALSE;
496 #else
497 port = account->set_imapport ? account->imapport
498 : IMAP4_PORT;
499 #endif
501 if (account->set_tunnelcmd) {
502 log_message(_("creating tunneled IMAP4 connection\n"));
503 #if USE_OPENSSL
504 if ((imap_sock = imap_open_tunnel(account->recv_server,
505 account->tunnelcmd,
506 ssl_type)) == NULL)
507 #else
508 if ((imap_sock = imap_open_tunnel(account->recv_server,
509 account->tunnelcmd)) == NULL)
510 #endif
511 return NULL;
512 } else {
513 g_return_val_if_fail(account->recv_server != NULL, NULL);
515 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
516 account->recv_server, port);
518 #if USE_OPENSSL
519 if ((imap_sock = imap_open(account->recv_server, port,
520 ssl_type)) == NULL)
521 #else
522 if ((imap_sock = imap_open(account->recv_server, port)) == NULL)
523 #endif
524 return NULL;
527 /* Only need to log in if the connection was not PREAUTH */
528 if (imap_greeting(imap_sock, &is_preauth) != IMAP_SUCCESS) {
529 sock_close(imap_sock);
530 return NULL;
532 log_message("IMAP connection is %s-authenticated\n",
533 (is_preauth) ? "pre" : "un");
535 session = g_new(IMAPSession, 1);
536 SESSION(session)->type = SESSION_IMAP;
537 SESSION(session)->server = g_strdup(account->recv_server);
538 SESSION(session)->sock = imap_sock;
539 SESSION(session)->connected = TRUE;
540 SESSION(session)->phase = SESSION_READY;
541 SESSION(session)->last_access_time = time(NULL);
542 SESSION(session)->data = NULL;
544 SESSION(session)->destroy = imap_session_destroy;
546 session->mbox = NULL;
547 session->authenticated = is_preauth;
549 session_list = g_list_append(session_list, session);
551 return SESSION(session);
554 void imap_session_authenticate(IMAPSession *session, const PrefsAccount *account)
556 gchar *pass;
558 g_return_if_fail(account->userid != NULL);
560 pass = account->passwd;
561 if (!pass) {
562 gchar *tmp_pass;
563 tmp_pass = input_dialog_query_password(account->recv_server, account->userid);
564 if (!tmp_pass)
565 return;
566 Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return;});
567 g_free(tmp_pass);
570 if (imap_cmd_login(SESSION(session)->sock, account->userid, pass) != IMAP_SUCCESS) {
571 imap_cmd_logout(SESSION(session)->sock);
572 return;
575 session->authenticated = TRUE;
578 void imap_session_destroy(Session *session)
580 sock_close(session->sock);
581 session->sock = NULL;
583 g_free(IMAP_SESSION(session)->mbox);
585 session_list = g_list_remove(session_list, session);
588 void imap_session_destroy_all(void)
590 while (session_list != NULL) {
591 IMAPSession *session = (IMAPSession *)session_list->data;
593 imap_cmd_logout(SESSION(session)->sock);
594 session_destroy(SESSION(session));
598 #define THROW goto catch
600 GSList *imap_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
602 GSList *mlist = NULL;
603 IMAPSession *session;
604 gint ok, exists = 0, recent = 0, unseen = 0;
605 guint32 uid_validity = 0;
606 guint32 first_uid = 0, last_uid = 0, begin;
608 g_return_val_if_fail(folder != NULL, NULL);
609 g_return_val_if_fail(item != NULL, NULL);
610 g_return_val_if_fail(folder->type == F_IMAP, NULL);
611 g_return_val_if_fail(folder->account != NULL, NULL);
613 session = imap_session_get(folder);
615 if (!session) {
616 mlist = procmsg_read_cache(item, FALSE);
617 item->last_num = procmsg_get_last_num_in_msg_list(mlist);
618 procmsg_set_flags(mlist, item);
619 return mlist;
622 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
623 &exists, &recent, &unseen, &uid_validity);
624 if (ok != IMAP_SUCCESS) THROW;
625 if (exists > 0) {
626 ok = imap_get_uid(session, 1, &first_uid);
627 if (ok != IMAP_SUCCESS) THROW;
628 if (1 != exists) {
629 ok = imap_get_uid(session, exists, &last_uid);
630 if (ok != IMAP_SUCCESS) THROW;
631 } else
632 last_uid = first_uid;
633 } else {
634 imap_delete_all_cached_messages(item);
635 return NULL;
638 if (use_cache) {
639 guint32 cache_last;
641 mlist = procmsg_read_cache(item, FALSE);
642 procmsg_set_flags(mlist, item);
643 cache_last = procmsg_get_last_num_in_msg_list(mlist);
645 /* calculating the range of envelope to get */
646 if (item->mtime != uid_validity) {
647 /* mailbox is changed (get all) */
648 begin = first_uid;
649 } else if (last_uid < cache_last) {
650 /* mailbox is changed (get all) */
651 begin = first_uid;
652 } else if (last_uid == cache_last) {
653 /* mailbox unchanged (get none)*/
654 begin = 0;
655 } else {
656 begin = cache_last + 1;
659 item->mtime = uid_validity;
661 if (first_uid > 0 && last_uid > 0) {
662 mlist = imap_delete_cached_messages(mlist, item,
663 0, first_uid - 1);
664 mlist = imap_delete_cached_messages(mlist, item,
665 last_uid + 1,
666 UINT_MAX);
668 if (begin > 0)
669 mlist = imap_delete_cached_messages(mlist, item,
670 begin, UINT_MAX);
671 } else {
672 imap_delete_all_cached_messages(item);
673 begin = first_uid;
676 if (begin > 0 && begin <= last_uid) {
677 GSList *newlist;
678 newlist = imap_get_uncached_messages(session, item,
679 begin, last_uid);
680 mlist = g_slist_concat(mlist, newlist);
683 item->last_num = last_uid;
685 catch:
686 return mlist;
689 #undef THROW
691 gchar *imap_fetch_msg(Folder *folder, FolderItem *item, gint uid)
693 gchar *path, *filename;
694 IMAPSession *session;
695 gint ok;
697 g_return_val_if_fail(folder != NULL, NULL);
698 g_return_val_if_fail(item != NULL, NULL);
700 path = folder_item_get_path(item);
701 if (!is_dir_exist(path))
702 make_dir_hier(path);
703 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(uid), NULL);
704 g_free(path);
706 if (is_file_exist(filename)) {
707 debug_print("message %d has been already cached.\n", uid);
708 return filename;
711 session = imap_session_get(folder);
712 if (!session) {
713 g_free(filename);
714 return NULL;
717 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
718 NULL, NULL, NULL, NULL);
719 if (ok != IMAP_SUCCESS) {
720 g_warning("can't select mailbox %s\n", item->path);
721 g_free(filename);
722 return NULL;
725 debug_print("getting message %d...\n", uid);
726 ok = imap_cmd_fetch(SESSION(session)->sock, (guint32)uid, filename);
728 if (ok != IMAP_SUCCESS) {
729 g_warning("can't fetch message %d\n", uid);
730 g_free(filename);
731 return NULL;
734 return filename;
737 gint imap_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
738 gboolean remove_source)
740 gchar *destdir;
741 IMAPSession *session;
742 gint messages, recent, unseen;
743 guint32 uid_next, uid_validity;
744 gint ok;
746 g_return_val_if_fail(folder != NULL, -1);
747 g_return_val_if_fail(dest != NULL, -1);
748 g_return_val_if_fail(file != NULL, -1);
750 session = imap_session_get(folder);
751 if (!session) return -1;
753 ok = imap_status(session, IMAP_FOLDER(folder), dest->path,
754 &messages, &recent, &uid_next, &uid_validity, &unseen);
755 if (ok != IMAP_SUCCESS) {
756 g_warning("can't append message %s\n", file);
757 return -1;
760 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
761 ok = imap_cmd_append(SESSION(session)->sock, destdir, file);
762 g_free(destdir);
764 if (ok != IMAP_SUCCESS) {
765 g_warning("can't append message %s\n", file);
766 return -1;
769 if (remove_source) {
770 if (unlink(file) < 0)
771 FILE_OP_ERROR(file, "unlink");
774 return uid_next;
777 static gint imap_do_copy(Folder *folder, FolderItem *dest, MsgInfo *msginfo,
778 gboolean remove_source)
780 gchar *destdir;
781 IMAPSession *session;
782 gint messages, recent, unseen;
783 guint32 uid_next, uid_validity;
784 gint ok;
786 g_return_val_if_fail(folder != NULL, -1);
787 g_return_val_if_fail(folder->type == F_IMAP, -1);
788 g_return_val_if_fail(dest != NULL, -1);
789 g_return_val_if_fail(msginfo != NULL, -1);
791 session = imap_session_get(folder);
792 if (!session) return -1;
794 if (msginfo->folder == dest) {
795 g_warning("the src folder is identical to the dest.\n");
796 return -1;
799 ok = imap_status(session, IMAP_FOLDER(folder), dest->path,
800 &messages, &recent, &uid_next, &uid_validity, &unseen);
801 if (ok != IMAP_SUCCESS) {
802 g_warning("can't copy message\n");
803 return -1;
806 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
808 /* ensure source folder selected */
809 ok = imap_select(session, IMAP_FOLDER(folder), msginfo->folder->path,
810 NULL, NULL, NULL, NULL);
811 if (ok != IMAP_SUCCESS)
812 return -1;
814 if (remove_source)
815 debug_print("Moving message %s%c%d to %s ...\n",
816 msginfo->folder->path, G_DIR_SEPARATOR,
817 msginfo->msgnum, destdir);
818 else
819 debug_print("Copying message %s%c%d to %s ...\n",
820 msginfo->folder->path, G_DIR_SEPARATOR,
821 msginfo->msgnum, destdir);
823 ok = imap_cmd_copy(SESSION(session)->sock, msginfo->msgnum, destdir);
825 if (ok == IMAP_SUCCESS && remove_source) {
826 imap_set_message_flags(session, msginfo->msgnum, msginfo->msgnum,
827 IMAP_FLAG_DELETED, TRUE);
828 ok = imap_cmd_expunge(SESSION(session)->sock);
831 g_free(destdir);
833 if (ok == IMAP_SUCCESS)
834 return uid_next;
835 else
836 return -1;
839 static gint imap_do_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
840 GSList *msglist,
841 gboolean remove_source)
843 gchar *destdir;
844 GSList *cur;
845 MsgInfo *msginfo;
846 IMAPSession *session;
847 gint ok = IMAP_SUCCESS;
849 g_return_val_if_fail(folder != NULL, -1);
850 g_return_val_if_fail(dest != NULL, -1);
851 g_return_val_if_fail(msglist != NULL, -1);
853 session = imap_session_get(folder);
854 if (!session) return -1;
856 destdir = imap_get_real_path(IMAP_FOLDER(folder), dest->path);
858 for (cur = msglist; cur != NULL; cur = cur->next) {
859 msginfo = (MsgInfo *)cur->data;
861 if (msginfo->folder == dest) {
862 g_warning("the src folder is identical to the dest.\n");
863 continue;
866 /* ensure source folder selected */
867 ok = imap_select(session, IMAP_FOLDER(folder),
868 msginfo->folder->path, NULL, NULL, NULL, NULL);
870 if (remove_source)
871 debug_print("Moving message %s%c%d to %s ...\n",
872 msginfo->folder->path, G_DIR_SEPARATOR,
873 msginfo->msgnum, destdir);
874 else
875 debug_print("Copying message %s%c%d to %s ...\n",
876 msginfo->folder->path, G_DIR_SEPARATOR,
877 msginfo->msgnum, destdir);
879 ok = imap_cmd_copy(SESSION(session)->sock, msginfo->msgnum,
880 destdir);
882 if (ok == IMAP_SUCCESS && remove_source) {
883 imap_set_message_flags
884 (session, msginfo->msgnum, msginfo->msgnum,
885 IMAP_FLAG_DELETED, TRUE);
889 if (remove_source)
890 ok = imap_cmd_expunge(SESSION(session)->sock);
892 g_free(destdir);
894 if (ok == IMAP_SUCCESS)
895 return 0;
896 else
897 return -1;
900 gint imap_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
902 gchar *srcfile;
903 gint ret = 0;
905 g_return_val_if_fail(folder != NULL, -1);
906 g_return_val_if_fail(dest != NULL, -1);
907 g_return_val_if_fail(msginfo != NULL, -1);
908 g_return_val_if_fail(msginfo->folder != NULL, -1);
910 if (folder == msginfo->folder->folder)
911 return imap_do_copy(folder, dest, msginfo, TRUE);
913 srcfile = procmsg_get_message_file(msginfo);
914 if (!srcfile) return -1;
916 ret = imap_add_msg(folder, dest, srcfile, FALSE);
917 g_free(srcfile);
919 if (ret != -1) {
920 if(folder_item_remove_msg(msginfo->folder, msginfo->msgnum)) {
921 ret = -1;
925 return ret;
928 gint imap_move_msgs_with_dest(Folder *folder, FolderItem *dest,
929 GSList *msglist)
931 MsgInfo *msginfo;
932 GSList *cur;
933 gint ret = 0;
935 g_return_val_if_fail(folder != NULL, -1);
936 g_return_val_if_fail(dest != NULL, -1);
937 g_return_val_if_fail(msglist != NULL, -1);
939 msginfo = (MsgInfo *)msglist->data;
940 if (folder == msginfo->folder->folder)
941 return imap_do_copy_msgs_with_dest(folder, dest, msglist, TRUE);
943 for (cur = msglist; cur != NULL; cur = cur->next) {
944 msginfo = (MsgInfo *)cur->data;
945 ret = imap_move_msg(folder, dest, msginfo);
946 if (ret == -1) break;
949 return ret;
952 gint imap_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
954 gchar *srcfile;
955 gint ret = 0;
957 g_return_val_if_fail(folder != NULL, -1);
958 g_return_val_if_fail(dest != NULL, -1);
959 g_return_val_if_fail(msginfo != NULL, -1);
960 g_return_val_if_fail(msginfo->folder != NULL, -1);
962 if (folder == msginfo->folder->folder)
963 return imap_do_copy(folder, dest, msginfo, FALSE);
965 srcfile = procmsg_get_message_file(msginfo);
966 if (!srcfile) return -1;
968 ret = imap_add_msg(folder, dest, srcfile, FALSE);
970 g_free(srcfile);
972 return ret;
975 gint imap_copy_msgs_with_dest(Folder *folder, FolderItem *dest,
976 GSList *msglist)
978 MsgInfo *msginfo;
979 GSList *cur;
980 gint ret = 0;
982 g_return_val_if_fail(folder != NULL, -1);
983 g_return_val_if_fail(dest != NULL, -1);
984 g_return_val_if_fail(msglist != NULL, -1);
986 msginfo = (MsgInfo *)msglist->data;
987 if (folder == msginfo->folder->folder)
988 return imap_do_copy_msgs_with_dest
989 (folder, dest, msglist, FALSE);
991 for (cur = msglist; cur != NULL; cur = cur->next) {
992 msginfo = (MsgInfo *)cur->data;
993 ret = imap_copy_msg(folder, dest, msginfo);
994 if (ret == -1) break;
997 return ret;
1000 gint imap_remove_msg(Folder *folder, FolderItem *item, gint uid)
1002 gint ok;
1003 IMAPSession *session;
1004 gchar *dir;
1006 g_return_val_if_fail(folder != NULL, -1);
1007 g_return_val_if_fail(folder->type == F_IMAP, -1);
1008 g_return_val_if_fail(item != NULL, -1);
1010 session = imap_session_get(folder);
1011 if (!session) return -1;
1013 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1014 NULL, NULL, NULL, NULL);
1015 if (ok != IMAP_SUCCESS)
1016 return ok;
1018 ok = imap_set_message_flags
1019 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1020 (guint32)uid, (guint32)uid, IMAP_FLAG_DELETED, TRUE);
1021 if (ok != IMAP_SUCCESS) {
1022 log_warning(_("can't set deleted flags: %d\n"), uid);
1023 return ok;
1026 ok = imap_cmd_expunge(SESSION(session)->sock);
1027 if (ok != IMAP_SUCCESS) {
1028 log_warning(_("can't expunge\n"));
1029 return ok;
1032 dir = folder_item_get_path(item);
1033 if (is_dir_exist(dir))
1034 remove_numbered_files(dir, uid, uid);
1035 g_free(dir);
1037 return IMAP_SUCCESS;
1040 gint imap_remove_msgs(Folder *folder, FolderItem *item, GSList *msglist)
1042 gint ok;
1043 IMAPSession *session;
1044 gchar *dir;
1045 MsgInfo *msginfo;
1046 GSList *cur;
1047 guint32 uid;
1049 g_return_val_if_fail(folder != NULL, -1);
1050 g_return_val_if_fail(folder->type == F_IMAP, -1);
1051 g_return_val_if_fail(item != NULL, -1);
1052 g_return_val_if_fail(msglist != NULL, -1);
1054 session = imap_session_get(folder);
1055 if (!session) return -1;
1057 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1058 NULL, NULL, NULL, NULL);
1059 if (ok != IMAP_SUCCESS)
1060 return ok;
1062 for (cur = msglist; cur != NULL; cur = cur->next) {
1063 msginfo = (MsgInfo *)cur->data;
1064 uid = msginfo->msgnum;
1065 ok = imap_set_message_flags
1066 (IMAP_SESSION(REMOTE_FOLDER(folder)->session),
1067 uid, uid, IMAP_FLAG_DELETED, TRUE);
1068 if (ok != IMAP_SUCCESS) {
1069 log_warning(_("can't set deleted flags: %d\n"), uid);
1070 return ok;
1074 ok = imap_cmd_expunge(SESSION(session)->sock);
1075 if (ok != IMAP_SUCCESS) {
1076 log_warning(_("can't expunge\n"));
1077 return ok;
1080 dir = folder_item_get_path(item);
1081 if (is_dir_exist(dir)) {
1082 for (cur = msglist; cur != NULL; cur = cur->next) {
1083 msginfo = (MsgInfo *)cur->data;
1084 uid = msginfo->msgnum;
1085 remove_numbered_files(dir, uid, uid);
1088 g_free(dir);
1090 return IMAP_SUCCESS;
1093 gint imap_remove_all_msg(Folder *folder, FolderItem *item)
1095 gint exists, recent, unseen;
1096 guint32 uid_validity;
1097 gint ok;
1098 IMAPSession *session;
1099 gchar *dir;
1101 g_return_val_if_fail(folder != NULL, -1);
1102 g_return_val_if_fail(item != NULL, -1);
1104 session = imap_session_get(folder);
1105 if (!session) return -1;
1107 ok = imap_select(session, IMAP_FOLDER(folder), item->path,
1108 &exists, &recent, &unseen, &uid_validity);
1109 if (ok != IMAP_SUCCESS)
1110 return ok;
1111 if (exists == 0)
1112 return IMAP_SUCCESS;
1114 imap_cmd_gen_send(SESSION(session)->sock,
1115 "STORE 1:%d +FLAGS (\\Deleted)", exists);
1116 ok = imap_cmd_ok(SESSION(session)->sock, NULL);
1117 if (ok != IMAP_SUCCESS) {
1118 log_warning(_("can't set deleted flags: 1:%d\n"), exists);
1119 return ok;
1122 ok = imap_cmd_expunge(SESSION(session)->sock);
1123 if (ok != IMAP_SUCCESS) {
1124 log_warning(_("can't expunge\n"));
1125 return ok;
1128 dir = folder_item_get_path(item);
1129 if (is_dir_exist(dir))
1130 remove_all_numbered_files(dir);
1131 g_free(dir);
1133 return IMAP_SUCCESS;
1136 gboolean imap_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
1138 /* TODO: properly implement this method */
1139 return FALSE;
1142 gint imap_scan_folder(Folder *folder, FolderItem *item)
1144 IMAPSession *session;
1145 gint messages, recent, unseen;
1146 guint32 uid_next, uid_validity;
1147 gint ok;
1149 g_return_val_if_fail(folder != NULL, -1);
1150 g_return_val_if_fail(item != NULL, -1);
1152 session = imap_session_get(folder);
1153 if (!session) return -1;
1155 ok = imap_status(session, IMAP_FOLDER(folder), item->path,
1156 &messages, &recent, &uid_next, &uid_validity, &unseen);
1157 if (ok != IMAP_SUCCESS) return -1;
1159 item->new = unseen > 0 ? recent : 0;
1160 item->unread = unseen;
1161 item->total = messages;
1162 item->last_num = (messages > 0 && uid_next > 0) ? uid_next - 1 : 0;
1163 /* item->mtime = uid_validity; */
1165 return 0;
1168 void imap_scan_tree(Folder *folder)
1170 FolderItem *item;
1171 IMAPSession *session;
1172 gchar *root_folder = NULL;
1174 g_return_if_fail(folder != NULL);
1175 g_return_if_fail(folder->account != NULL);
1177 session = imap_session_get(folder);
1178 if (!session) {
1179 if (!folder->node) {
1180 folder_tree_destroy(folder);
1181 item = folder_item_new(folder, folder->name, NULL);
1182 item->folder = folder;
1183 folder->node = g_node_new(item);
1185 return;
1188 if (folder->account->imap_dir && *folder->account->imap_dir) {
1189 Xstrdup_a(root_folder, folder->account->imap_dir, return);
1190 strtailchomp(root_folder, '/');
1191 debug_print("IMAP root directory: %s\n", root_folder);
1194 item = folder_item_new(folder, folder->name, root_folder);
1195 item->folder = folder;
1196 item->no_select = TRUE;
1197 folder->node = g_node_new(item);
1199 imap_scan_tree_recursive(session, item);
1201 imap_create_missing_folders(folder);
1204 static gint imap_scan_tree_recursive(IMAPSession *session, FolderItem *item)
1206 Folder *folder;
1207 IMAPFolder *imapfolder;
1208 FolderItem *new_item;
1209 GSList *item_list, *cur;
1210 gchar *real_path;
1211 gchar *wildcard_path;
1212 gchar separator;
1213 gchar wildcard[3];
1215 g_return_val_if_fail(item != NULL, -1);
1216 g_return_val_if_fail(item->folder != NULL, -1);
1217 g_return_val_if_fail(item->no_sub == FALSE, -1);
1219 folder = FOLDER(item->folder);
1220 imapfolder = IMAP_FOLDER(folder);
1222 separator = imap_get_path_separator(imapfolder, item->path);
1224 if (item->folder->ui_func)
1225 item->folder->ui_func(folder, item, folder->ui_func_data);
1227 if (item->path) {
1228 wildcard[0] = separator;
1229 wildcard[1] = '%';
1230 wildcard[2] = '\0';
1231 real_path = imap_get_real_path(imapfolder, item->path);
1232 } else {
1233 wildcard[0] = '%';
1234 wildcard[1] = '\0';
1235 real_path = g_strdup("");
1238 Xstrcat_a(wildcard_path, real_path, wildcard,
1239 {g_free(real_path); return IMAP_ERROR;});
1240 QUOTE_IF_REQUIRED(wildcard_path, wildcard_path);
1242 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" %s",
1243 wildcard_path);
1245 strtailchomp(real_path, separator);
1246 item_list = imap_parse_list(folder, session, real_path, NULL);
1247 g_free(real_path);
1249 for (cur = item_list; cur != NULL; cur = cur->next) {
1250 new_item = cur->data;
1251 if (!strcmp(new_item->path, "INBOX")) {
1252 if (!folder->inbox) {
1253 new_item->stype = F_INBOX;
1254 item->folder->inbox = new_item;
1255 } else {
1256 folder_item_destroy(new_item);
1257 continue;
1259 } else if (!item->parent || item->stype == F_INBOX) {
1260 gchar *base;
1262 base = g_basename(new_item->path);
1264 if (!folder->outbox && !strcasecmp(base, "Sent")) {
1265 new_item->stype = F_OUTBOX;
1266 folder->outbox = new_item;
1267 } else if (!folder->draft && !strcasecmp(base, "Drafts")) {
1268 new_item->stype = F_DRAFT;
1269 folder->draft = new_item;
1270 } else if (!folder->queue && !strcasecmp(base, "Queue")) {
1271 new_item->stype = F_QUEUE;
1272 folder->queue = new_item;
1273 } else if (!folder->trash && !strcasecmp(base, "Trash")) {
1274 new_item->stype = F_TRASH;
1275 folder->trash = new_item;
1278 folder_item_append(item, new_item);
1279 if (new_item->no_select == FALSE)
1280 imap_scan_folder(folder, new_item);
1281 if (new_item->no_sub == FALSE)
1282 imap_scan_tree_recursive(session, new_item);
1285 return IMAP_SUCCESS;
1288 static GSList *imap_parse_list(Folder *folder, IMAPSession *session,
1289 const gchar *real_path, gchar *separator)
1291 gchar buf[IMAPBUFSIZE];
1292 gchar flags[256];
1293 gchar separator_str[16];
1294 gchar *p;
1295 gchar *name;
1296 gchar *loc_name, *loc_path;
1297 GSList *item_list = NULL;
1298 GString *str;
1299 FolderItem *new_item;
1301 debug_print("getting list of %s ...\n",
1302 *real_path ? real_path : "\"\"");
1304 str = g_string_new(NULL);
1306 for (;;) {
1307 if (sock_gets(SESSION(session)->sock, buf, sizeof(buf)) <= 0) {
1308 log_warning(_("error occured while getting LIST.\n"));
1309 break;
1311 strretchomp(buf);
1312 if (buf[0] != '*' || buf[1] != ' ') {
1313 log_print("IMAP4< %s\n", buf);
1314 break;
1316 debug_print("IMAP4< %s\n", buf);
1318 g_string_assign(str, buf);
1319 p = str->str + 2;
1320 if (strncmp(p, "LIST ", 5) != 0) continue;
1321 p += 5;
1323 if (*p != '(') continue;
1324 p++;
1325 p = strchr_cpy(p, ')', flags, sizeof(flags));
1326 if (!p) continue;
1327 while (*p == ' ') p++;
1329 p = strchr_cpy(p, ' ', separator_str, sizeof(separator_str));
1330 if (!p) continue;
1331 extract_quote(separator_str, '"');
1332 if (!strcmp(separator_str, "NIL"))
1333 separator_str[0] = '\0';
1334 if (separator)
1335 *separator = separator_str[0];
1337 buf[0] = '\0';
1338 while (*p == ' ') p++;
1339 if (*p == '{' || *p == '"')
1340 p = imap_parse_atom(SESSION(session)->sock, p,
1341 buf, sizeof(buf), str);
1342 else
1343 strncpy2(buf, p, sizeof(buf));
1344 strtailchomp(buf, separator_str[0]);
1345 if (buf[0] == '\0') continue;
1346 if (!strcmp(buf, real_path)) continue;
1348 if (separator_str[0] != '\0')
1349 subst_char(buf, separator_str[0], '/');
1350 name = g_basename(buf);
1351 if (name[0] == '.') continue;
1353 loc_name = imap_modified_utf7_to_locale(name);
1354 loc_path = imap_modified_utf7_to_locale(buf);
1355 new_item = folder_item_new(folder, loc_name, loc_path);
1356 if (strcasestr(flags, "\\Noinferiors") != NULL)
1357 new_item->no_sub = TRUE;
1358 if (strcmp(buf, "INBOX") != 0 &&
1359 strcasestr(flags, "\\Noselect") != NULL)
1360 new_item->no_select = TRUE;
1362 item_list = g_slist_append(item_list, new_item);
1364 debug_print("folder %s has been added.\n", loc_path);
1365 g_free(loc_path);
1366 g_free(loc_name);
1369 g_string_free(str, TRUE);
1371 return item_list;
1374 gint imap_create_tree(Folder *folder)
1376 g_return_val_if_fail(folder != NULL, -1);
1377 g_return_val_if_fail(folder->node != NULL, -1);
1378 g_return_val_if_fail(folder->node->data != NULL, -1);
1379 g_return_val_if_fail(folder->account != NULL, -1);
1381 imap_scan_tree(folder);
1382 imap_create_missing_folders(folder);
1384 return 0;
1387 static void imap_create_missing_folders(Folder *folder)
1389 g_return_if_fail(folder != NULL);
1391 if (!folder->inbox)
1392 folder->inbox = imap_create_special_folder
1393 (folder, F_INBOX, "INBOX");
1394 #if 0
1395 if (!folder->outbox)
1396 folder->outbox = imap_create_special_folder
1397 (folder, F_OUTBOX, "Sent");
1398 if (!folder->draft)
1399 folder->draft = imap_create_special_folder
1400 (folder, F_DRAFT, "Drafts");
1401 if (!folder->queue)
1402 folder->queue = imap_create_special_folder
1403 (folder, F_QUEUE, "Queue");
1404 #endif
1405 if (!folder->trash)
1406 folder->trash = imap_create_special_folder
1407 (folder, F_TRASH, "Trash");
1410 static FolderItem *imap_create_special_folder(Folder *folder,
1411 SpecialFolderItemType stype,
1412 const gchar *name)
1414 FolderItem *item;
1415 FolderItem *new_item;
1417 g_return_val_if_fail(folder != NULL, NULL);
1418 g_return_val_if_fail(folder->node != NULL, NULL);
1419 g_return_val_if_fail(folder->node->data != NULL, NULL);
1420 g_return_val_if_fail(folder->account != NULL, NULL);
1421 g_return_val_if_fail(name != NULL, NULL);
1423 item = FOLDER_ITEM(folder->node->data);
1424 new_item = imap_create_folder(folder, item, name);
1426 if (!new_item) {
1427 g_warning("Can't create '%s'\n", name);
1428 if (!folder->inbox) return NULL;
1430 new_item = imap_create_folder(folder, folder->inbox, name);
1431 if (!new_item)
1432 g_warning("Can't create '%s' under INBOX\n", name);
1433 else
1434 new_item->stype = stype;
1435 } else
1436 new_item->stype = stype;
1438 return new_item;
1441 FolderItem *imap_create_folder(Folder *folder, FolderItem *parent,
1442 const gchar *name)
1444 gchar *dirpath, *imap_path;
1445 IMAPSession *session;
1446 FolderItem *new_item;
1447 gchar separator;
1448 gchar *new_name;
1449 const gchar *p;
1450 gint ok;
1452 g_return_val_if_fail(folder != NULL, NULL);
1453 g_return_val_if_fail(folder->account != NULL, NULL);
1454 g_return_val_if_fail(parent != NULL, NULL);
1455 g_return_val_if_fail(name != NULL, NULL);
1457 session = imap_session_get(folder);
1458 if (!session) return NULL;
1460 if (!parent->parent && strcmp(name, "INBOX") == 0)
1461 dirpath = g_strdup(name);
1462 else if (parent->path)
1463 dirpath = g_strconcat(parent->path, "/", name, NULL);
1464 else if ((p = strchr(name, '/')) != NULL && *(p + 1) != '\0')
1465 dirpath = g_strdup(name);
1466 else if (folder->account->imap_dir && *folder->account->imap_dir) {
1467 gchar *imap_dir;
1469 Xstrdup_a(imap_dir, folder->account->imap_dir, return NULL);
1470 strtailchomp(imap_dir, '/');
1471 dirpath = g_strconcat(imap_dir, "/", name, NULL);
1472 } else
1473 dirpath = g_strdup(name);
1475 /* keep trailing directory separator to create a folder that contains
1476 sub folder */
1477 imap_path = imap_locale_to_modified_utf7(dirpath);
1478 strtailchomp(dirpath, '/');
1479 Xstrdup_a(new_name, name, {g_free(dirpath); return NULL;});
1480 strtailchomp(new_name, '/');
1481 separator = imap_get_path_separator(IMAP_FOLDER(folder), imap_path);
1482 imap_path_separator_subst(imap_path, separator);
1483 subst_char(new_name, '/', separator);
1485 if (strcmp(name, "INBOX") != 0) {
1486 GPtrArray *argbuf;
1487 gint i;
1488 gboolean exist = FALSE;
1490 argbuf = g_ptr_array_new();
1491 ok = imap_cmd_list(SESSION(session)->sock, NULL, imap_path,
1492 argbuf);
1493 if (ok != IMAP_SUCCESS) {
1494 log_warning(_("can't create mailbox: LIST failed\n"));
1495 g_free(imap_path);
1496 g_free(dirpath);
1497 g_ptr_array_free(argbuf, TRUE);
1498 return NULL;
1501 for (i = 0; i < argbuf->len; i++) {
1502 gchar *str;
1503 str = g_ptr_array_index(argbuf, i);
1504 if (!strncmp(str, "LIST ", 5)) {
1505 exist = TRUE;
1506 break;
1509 g_ptr_array_free(argbuf, TRUE);
1511 if (!exist) {
1512 ok = imap_cmd_create(SESSION(session)->sock, imap_path);
1513 if (ok != IMAP_SUCCESS) {
1514 log_warning(_("can't create mailbox\n"));
1515 g_free(imap_path);
1516 g_free(dirpath);
1517 return NULL;
1522 new_item = folder_item_new(folder, new_name, dirpath);
1523 folder_item_append(parent, new_item);
1524 g_free(imap_path);
1525 g_free(dirpath);
1527 dirpath = folder_item_get_path(new_item);
1528 if (!is_dir_exist(dirpath))
1529 make_dir_hier(dirpath);
1530 g_free(dirpath);
1532 return new_item;
1535 gint imap_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
1537 gchar *dirpath;
1538 gchar *newpath;
1539 gchar *real_oldpath;
1540 gchar *real_newpath;
1541 GNode *node;
1542 gchar *paths[2];
1543 gchar *old_cache_dir;
1544 gchar *new_cache_dir;
1545 IMAPSession *session;
1546 gchar separator;
1547 gint ok;
1548 gint exists, recent, unseen;
1549 guint32 uid_validity;
1551 g_return_val_if_fail(folder != NULL, -1);
1552 g_return_val_if_fail(item != NULL, -1);
1553 g_return_val_if_fail(item->path != NULL, -1);
1554 g_return_val_if_fail(name != NULL, -1);
1556 session = imap_session_get(folder);
1557 if (!session) return -1;
1559 real_oldpath = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1561 g_free(session->mbox);
1562 session->mbox = NULL;
1563 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1564 &exists, &recent, &unseen, &uid_validity);
1565 if (ok != IMAP_SUCCESS) {
1566 g_free(real_oldpath);
1567 return -1;
1570 separator = imap_get_path_separator(IMAP_FOLDER(folder), item->path);
1571 if (strchr(item->path, G_DIR_SEPARATOR)) {
1572 dirpath = g_dirname(item->path);
1573 newpath = g_strconcat(dirpath, G_DIR_SEPARATOR_S, name, NULL);
1574 g_free(dirpath);
1575 } else
1576 newpath = g_strdup(name);
1578 real_newpath = imap_locale_to_modified_utf7(newpath);
1579 imap_path_separator_subst(real_newpath, separator);
1581 ok = imap_cmd_rename(SESSION(session)->sock, real_oldpath, real_newpath);
1582 if (ok != IMAP_SUCCESS) {
1583 log_warning(_("can't rename mailbox: %s to %s\n"),
1584 real_oldpath, real_newpath);
1585 g_free(real_oldpath);
1586 g_free(newpath);
1587 g_free(real_newpath);
1588 return -1;
1591 g_free(item->name);
1592 item->name = g_strdup(name);
1594 old_cache_dir = folder_item_get_path(item);
1596 node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
1597 item);
1598 paths[0] = g_strdup(item->path);
1599 paths[1] = newpath;
1600 g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1601 imap_rename_folder_func, paths);
1603 if (is_dir_exist(old_cache_dir)) {
1604 new_cache_dir = folder_item_get_path(item);
1605 if (rename(old_cache_dir, new_cache_dir) < 0) {
1606 FILE_OP_ERROR(old_cache_dir, "rename");
1608 g_free(new_cache_dir);
1611 g_free(old_cache_dir);
1612 g_free(paths[0]);
1613 g_free(newpath);
1614 g_free(real_oldpath);
1615 g_free(real_newpath);
1617 return 0;
1620 gint imap_remove_folder(Folder *folder, FolderItem *item)
1622 gint ok;
1623 IMAPSession *session;
1624 gchar *path;
1625 gchar *cache_dir;
1626 gint exists, recent, unseen;
1627 guint32 uid_validity;
1629 g_return_val_if_fail(folder != NULL, -1);
1630 g_return_val_if_fail(item != NULL, -1);
1631 g_return_val_if_fail(item->path != NULL, -1);
1633 session = imap_session_get(folder);
1634 if (!session) return -1;
1636 path = imap_get_real_path(IMAP_FOLDER(folder), item->path);
1638 ok = imap_cmd_examine(SESSION(session)->sock, "INBOX",
1639 &exists, &recent, &unseen, &uid_validity);
1640 if (ok != IMAP_SUCCESS) {
1641 g_free(path);
1642 return -1;
1645 ok = imap_cmd_delete(SESSION(session)->sock, path);
1646 if (ok != IMAP_SUCCESS) {
1647 log_warning(_("can't delete mailbox\n"));
1648 g_free(path);
1649 return -1;
1652 g_free(path);
1653 cache_dir = folder_item_get_path(item);
1654 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0)
1655 g_warning("can't remove directory '%s'\n", cache_dir);
1656 g_free(cache_dir);
1657 folder_item_remove(item);
1659 return 0;
1662 static GSList *imap_get_uncached_messages(IMAPSession *session,
1663 FolderItem *item,
1664 guint32 first_uid, guint32 last_uid)
1666 gchar *tmp;
1667 GSList *newlist = NULL;
1668 GSList *llast = NULL;
1669 GString *str;
1670 MsgInfo *msginfo;
1672 g_return_val_if_fail(session != NULL, NULL);
1673 g_return_val_if_fail(item != NULL, NULL);
1674 g_return_val_if_fail(item->folder != NULL, NULL);
1675 g_return_val_if_fail(item->folder->type == F_IMAP, NULL);
1676 g_return_val_if_fail(first_uid <= last_uid, NULL);
1678 if (imap_cmd_envelope(SESSION(session)->sock, first_uid, last_uid)
1679 != IMAP_SUCCESS) {
1680 log_warning(_("can't get envelope\n"));
1681 return NULL;
1684 str = g_string_new(NULL);
1686 for (;;) {
1687 if ((tmp = sock_getline(SESSION(session)->sock)) == NULL) {
1688 log_warning(_("error occurred while getting envelope.\n"));
1689 g_string_free(str, TRUE);
1690 return newlist;
1692 strretchomp(tmp);
1693 if (tmp[0] != '*' || tmp[1] != ' ') {
1694 log_print("IMAP4< %s\n", tmp);
1695 g_free(tmp);
1696 break;
1698 if (strstr(tmp, "FETCH") == NULL) {
1699 log_print("IMAP4< %s\n", tmp);
1700 g_free(tmp);
1701 continue;
1703 log_print("IMAP4< %s\n", tmp);
1704 g_string_assign(str, tmp);
1705 g_free(tmp);
1707 msginfo = imap_parse_envelope
1708 (SESSION(session)->sock, item, str);
1709 if (!msginfo) {
1710 log_warning(_("can't parse envelope: %s\n"), str->str);
1711 continue;
1713 if (item->stype == F_QUEUE) {
1714 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_QUEUED);
1715 } else if (item->stype == F_DRAFT) {
1716 MSG_SET_TMP_FLAGS(msginfo->flags, MSG_DRAFT);
1719 msginfo->folder = item;
1721 if (!newlist)
1722 llast = newlist = g_slist_append(newlist, msginfo);
1723 else {
1724 llast = g_slist_append(llast, msginfo);
1725 llast = llast->next;
1729 g_string_free(str, TRUE);
1731 return newlist;
1734 static GSList *imap_delete_cached_messages(GSList *mlist, FolderItem *item,
1735 guint32 first_uid, guint32 last_uid)
1737 GSList *cur, *next;
1738 MsgInfo *msginfo;
1739 gchar *dir;
1741 g_return_val_if_fail(item != NULL, mlist);
1742 g_return_val_if_fail(item->folder != NULL, mlist);
1743 g_return_val_if_fail(item->folder->type == F_IMAP, mlist);
1745 debug_print("Deleting cached messages %u - %u ... ",
1746 first_uid, last_uid);
1748 dir = folder_item_get_path(item);
1749 if (is_dir_exist(dir))
1750 remove_numbered_files(dir, first_uid, last_uid);
1751 g_free(dir);
1753 for (cur = mlist; cur != NULL; ) {
1754 next = cur->next;
1756 msginfo = (MsgInfo *)cur->data;
1757 if (msginfo != NULL && first_uid <= msginfo->msgnum &&
1758 msginfo->msgnum <= last_uid) {
1759 procmsg_msginfo_free(msginfo);
1760 mlist = g_slist_remove(mlist, msginfo);
1763 cur = next;
1766 debug_print("done.\n");
1768 return mlist;
1771 static void imap_delete_all_cached_messages(FolderItem *item)
1773 gchar *dir;
1775 g_return_if_fail(item != NULL);
1776 g_return_if_fail(item->folder != NULL);
1777 g_return_if_fail(item->folder->type == F_IMAP);
1779 debug_print("Deleting all cached messages...\n");
1781 dir = folder_item_get_path(item);
1782 if (is_dir_exist(dir))
1783 remove_all_numbered_files(dir);
1784 g_free(dir);
1786 debug_print("done.\n");
1789 #if USE_OPENSSL
1790 static SockInfo *imap_open_tunnel(const gchar *server,
1791 const gchar *tunnelcmd,
1792 SSLType ssl_type)
1793 #else
1794 static SockInfo *imap_open_tunnel(const gchar *server,
1795 const gchar *tunnelcmd)
1796 #endif
1798 SockInfo *sock;
1800 if ((sock = sock_connect_cmd(server, tunnelcmd)) == NULL)
1801 log_warning(_("Can't establish IMAP4 session with: %s\n"),
1802 server);
1803 return NULL;
1804 #if USE_OPENSSL
1805 return imap_init_sock(sock, ssl_type);
1806 #else
1807 return imap_init_sock(sock);
1808 #endif
1812 #if USE_OPENSSL
1813 static SockInfo *imap_open(const gchar *server, gushort port,
1814 SSLType ssl_type)
1815 #else
1816 static SockInfo *imap_open(const gchar *server, gushort port)
1817 #endif
1819 SockInfo *sock;
1821 if ((sock = sock_connect(server, port)) == NULL) {
1822 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1823 server, port);
1824 return NULL;
1827 #if USE_OPENSSL
1828 if (ssl_type == SSL_TUNNEL && !ssl_init_socket(sock)) {
1829 log_warning(_("Can't establish IMAP4 session with: %s:%d\n"),
1830 server, port);
1831 sock_close(sock);
1832 return NULL;
1834 return imap_init_sock(sock, ssl_type);
1835 #else
1836 return imap_init_sock(sock);
1837 #endif
1840 #if USE_OPENSSL
1841 static SockInfo *imap_init_sock(SockInfo *sock, SSLType ssl_type)
1842 #else
1843 static SockInfo *imap_init_sock(SockInfo *sock)
1844 #endif
1846 imap_cmd_count = 0;
1847 #if USE_OPENSSL
1848 if (ssl_type == SSL_STARTTLS) {
1849 gint ok;
1851 ok = imap_cmd_starttls(sock);
1852 if (ok != IMAP_SUCCESS) {
1853 log_warning(_("Can't start TLS session.\n"));
1854 sock_close(sock);
1855 return NULL;
1857 if (!ssl_init_socket_with_method(sock, SSL_METHOD_TLSv1)) {
1858 sock_close(sock);
1859 return NULL;
1862 #endif
1864 if (imap_cmd_noop(sock) != IMAP_SUCCESS) {
1865 log_warning(_("Can't establish IMAP4 session.\n"));
1866 sock_close(sock);
1867 return NULL;
1870 return sock;
1873 static GList *imap_parse_namespace_str(gchar *str)
1875 gchar *p = str;
1876 gchar *name;
1877 gchar *separator;
1878 IMAPNameSpace *namespace;
1879 GList *ns_list = NULL;
1881 while (*p != '\0') {
1882 /* parse ("#foo" "/") */
1884 while (*p && *p != '(') p++;
1885 if (*p == '\0') break;
1886 p++;
1888 while (*p && *p != '"') p++;
1889 if (*p == '\0') break;
1890 p++;
1891 name = p;
1893 while (*p && *p != '"') p++;
1894 if (*p == '\0') break;
1895 *p = '\0';
1896 p++;
1898 while (*p && isspace(*p)) p++;
1899 if (*p == '\0') break;
1900 if (strncmp(p, "NIL", 3) == 0)
1901 separator = NULL;
1902 else if (*p == '"') {
1903 p++;
1904 separator = p;
1905 while (*p && *p != '"') p++;
1906 if (*p == '\0') break;
1907 *p = '\0';
1908 p++;
1909 } else break;
1911 while (*p && *p != ')') p++;
1912 if (*p == '\0') break;
1913 p++;
1915 namespace = g_new(IMAPNameSpace, 1);
1916 namespace->name = g_strdup(name);
1917 namespace->separator = separator ? separator[0] : '\0';
1918 ns_list = g_list_append(ns_list, namespace);
1921 return ns_list;
1924 static void imap_parse_namespace(IMAPSession *session, IMAPFolder *folder)
1926 gchar *ns_str;
1927 gchar **str_array;
1929 g_return_if_fail(session != NULL);
1930 g_return_if_fail(folder != NULL);
1932 if (folder->ns_personal != NULL ||
1933 folder->ns_others != NULL ||
1934 folder->ns_shared != NULL)
1935 return;
1937 if (imap_cmd_namespace(SESSION(session)->sock, &ns_str)
1938 != IMAP_SUCCESS) {
1939 log_warning(_("can't get namespace\n"));
1940 imap_get_namespace_by_list(session, folder);
1941 return;
1944 str_array = strsplit_parenthesis(ns_str, '(', ')', 3);
1945 if (str_array[0])
1946 folder->ns_personal = imap_parse_namespace_str(str_array[0]);
1947 if (str_array[0] && str_array[1])
1948 folder->ns_others = imap_parse_namespace_str(str_array[1]);
1949 if (str_array[0] && str_array[1] && str_array[2])
1950 folder->ns_shared = imap_parse_namespace_str(str_array[2]);
1951 g_strfreev(str_array);
1952 g_free(ns_str);
1955 static void imap_get_namespace_by_list(IMAPSession *session, IMAPFolder *folder)
1957 GSList *item_list, *cur;
1958 gchar separator = '\0';
1959 IMAPNameSpace *namespace;
1961 g_return_if_fail(session != NULL);
1962 g_return_if_fail(folder != NULL);
1964 if (folder->ns_personal != NULL ||
1965 folder->ns_others != NULL ||
1966 folder->ns_shared != NULL)
1967 return;
1969 imap_cmd_gen_send(SESSION(session)->sock, "LIST \"\" \"\"");
1970 item_list = imap_parse_list(NULL, session, "", &separator);
1971 for (cur = item_list; cur != NULL; cur = cur->next)
1972 folder_item_destroy(FOLDER_ITEM(cur->data));
1973 g_slist_free(item_list);
1975 namespace = g_new(IMAPNameSpace, 1);
1976 namespace->name = g_strdup("");
1977 namespace->separator = separator;
1978 folder->ns_personal = g_list_append(NULL, namespace);
1981 static IMAPNameSpace *imap_find_namespace_from_list(GList *ns_list,
1982 const gchar *path)
1984 IMAPNameSpace *namespace = NULL;
1985 gchar *tmp_path, *name;
1987 if (!path) path = "";
1989 Xstrcat_a(tmp_path, path, "/", return NULL);
1991 for (; ns_list != NULL; ns_list = ns_list->next) {
1992 IMAPNameSpace *tmp_ns = ns_list->data;
1994 Xstrdup_a(name, tmp_ns->name, return namespace);
1995 if (tmp_ns->separator && tmp_ns->separator != '/')
1996 subst_char(name, tmp_ns->separator, '/');
1997 if (strncmp(tmp_path, name, strlen(name)) == 0)
1998 namespace = tmp_ns;
2001 return namespace;
2004 static IMAPNameSpace *imap_find_namespace(IMAPFolder *folder,
2005 const gchar *path)
2007 IMAPNameSpace *namespace;
2009 g_return_val_if_fail(folder != NULL, NULL);
2011 namespace = imap_find_namespace_from_list(folder->ns_personal, path);
2012 if (namespace) return namespace;
2013 namespace = imap_find_namespace_from_list(folder->ns_others, path);
2014 if (namespace) return namespace;
2015 namespace = imap_find_namespace_from_list(folder->ns_shared, path);
2016 if (namespace) return namespace;
2018 return NULL;
2021 static gchar imap_get_path_separator(IMAPFolder *folder, const gchar *path)
2023 IMAPNameSpace *namespace;
2024 gchar separator = '/';
2026 namespace = imap_find_namespace(folder, path);
2027 if (namespace && namespace->separator)
2028 separator = namespace->separator;
2030 return separator;
2033 static gchar *imap_get_real_path(IMAPFolder *folder, const gchar *path)
2035 gchar *real_path;
2036 gchar separator;
2038 g_return_val_if_fail(folder != NULL, NULL);
2039 g_return_val_if_fail(path != NULL, NULL);
2041 real_path = imap_locale_to_modified_utf7(path);
2042 separator = imap_get_path_separator(folder, path);
2043 imap_path_separator_subst(real_path, separator);
2045 return real_path;
2048 static gchar *imap_parse_atom(SockInfo *sock, gchar *src,
2049 gchar *dest, gint dest_len, GString *str)
2051 gchar *cur_pos = src;
2052 gchar *nextline;
2054 g_return_val_if_fail(str != NULL, cur_pos);
2056 /* read the next line if the current response buffer is empty */
2057 while (isspace(*cur_pos)) cur_pos++;
2058 while (*cur_pos == '\0') {
2059 if ((nextline = sock_getline(sock)) == NULL)
2060 return cur_pos;
2061 g_string_assign(str, nextline);
2062 cur_pos = str->str;
2063 strretchomp(nextline);
2064 /* log_print("IMAP4< %s\n", nextline); */
2065 debug_print("IMAP4< %s\n", nextline);
2066 g_free(nextline);
2068 while (isspace(*cur_pos)) cur_pos++;
2071 if (!strncmp(cur_pos, "NIL", 3)) {
2072 *dest = '\0';
2073 cur_pos += 3;
2074 } else if (*cur_pos == '\"') {
2075 gchar *p;
2077 p = get_quoted(cur_pos, '\"', dest, dest_len);
2078 cur_pos = p ? p : cur_pos + 2;
2079 } else if (*cur_pos == '{') {
2080 gchar buf[32];
2081 gint len;
2082 gint line_len = 0;
2084 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2085 len = atoi(buf);
2087 g_string_truncate(str, 0);
2088 cur_pos = str->str;
2090 do {
2091 if ((nextline = sock_getline(sock)) == NULL)
2092 return cur_pos;
2093 line_len += strlen(nextline);
2094 g_string_append(str, nextline);
2095 cur_pos = str->str;
2096 strretchomp(nextline);
2097 /* log_print("IMAP4< %s\n", nextline); */
2098 debug_print("IMAP4< %s\n", nextline);
2099 g_free(nextline);
2100 } while (line_len < len);
2102 memcpy(dest, cur_pos, MIN(len, dest_len - 1));
2103 dest[MIN(len, dest_len - 1)] = '\0';
2104 cur_pos += len;
2107 return cur_pos;
2110 static gchar *imap_get_header(SockInfo *sock, gchar *cur_pos, gchar **headers,
2111 GString *str)
2113 gchar *nextline;
2114 gchar buf[32];
2115 gint len;
2116 gint block_len = 0;
2118 *headers = NULL;
2120 g_return_val_if_fail(str != NULL, cur_pos);
2122 while (isspace(*cur_pos)) cur_pos++;
2124 g_return_val_if_fail(*cur_pos == '{', cur_pos);
2126 cur_pos = strchr_cpy(cur_pos + 1, '}', buf, sizeof(buf));
2127 len = atoi(buf);
2129 g_string_truncate(str, 0);
2130 cur_pos = str->str;
2132 do {
2133 if ((nextline = sock_getline(sock)) == NULL)
2134 return cur_pos;
2135 block_len += strlen(nextline);
2136 g_string_append(str, nextline);
2137 cur_pos = str->str;
2138 strretchomp(nextline);
2139 /* debug_print("IMAP4< %s\n", nextline); */
2140 g_free(nextline);
2141 } while (block_len < len);
2143 debug_print("IMAP4< [contents of RFC822.HEADER]\n");
2145 *headers = g_strndup(cur_pos, len);
2146 cur_pos += len;
2148 while (isspace(*cur_pos)) cur_pos++;
2149 while (*cur_pos == '\0') {
2150 if ((nextline = sock_getline(sock)) == NULL)
2151 return cur_pos;
2152 g_string_assign(str, nextline);
2153 cur_pos = str->str;
2154 strretchomp(nextline);
2155 debug_print("IMAP4< %s\n", nextline);
2156 g_free(nextline);
2158 while (isspace(*cur_pos)) cur_pos++;
2161 return cur_pos;
2164 static MsgFlags imap_parse_flags(const gchar *flag_str)
2166 const gchar *p = flag_str;
2167 MsgFlags flags = {0, 0};
2169 flags.perm_flags = MSG_UNREAD;
2171 while ((p = strchr(p, '\\')) != NULL) {
2172 p++;
2174 if (g_strncasecmp(p, "Recent", 6) == 0 && MSG_IS_UNREAD(flags)) {
2175 MSG_SET_PERM_FLAGS(flags, MSG_NEW);
2176 } else if (g_strncasecmp(p, "Seen", 4) == 0) {
2177 MSG_UNSET_PERM_FLAGS(flags, MSG_NEW|MSG_UNREAD);
2178 } else if (g_strncasecmp(p, "Deleted", 7) == 0) {
2179 MSG_SET_PERM_FLAGS(flags, MSG_DELETED);
2180 } else if (g_strncasecmp(p, "Flagged", 7) == 0) {
2181 MSG_SET_PERM_FLAGS(flags, MSG_MARKED);
2182 } else if (g_strncasecmp(p, "Answered", 8) == 0) {
2183 MSG_SET_PERM_FLAGS(flags, MSG_REPLIED);
2187 return flags;
2190 static MsgInfo *imap_parse_envelope(SockInfo *sock, FolderItem *item,
2191 GString *line_str)
2193 gchar buf[IMAPBUFSIZE];
2194 MsgInfo *msginfo = NULL;
2195 gchar *cur_pos;
2196 gint msgnum;
2197 guint32 uid = 0;
2198 size_t size = 0;
2199 MsgFlags flags = {0, 0}, imap_flags = {0, 0};
2201 g_return_val_if_fail(line_str != NULL, NULL);
2202 g_return_val_if_fail(line_str->str[0] == '*' &&
2203 line_str->str[1] == ' ', NULL);
2205 MSG_SET_TMP_FLAGS(flags, MSG_IMAP);
2206 if (item->stype == F_QUEUE) {
2207 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
2208 } else if (item->stype == F_DRAFT) {
2209 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
2212 cur_pos = line_str->str + 2;
2214 #define PARSE_ONE_ELEMENT(ch) \
2216 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
2217 if (cur_pos == NULL) { \
2218 g_warning("cur_pos == NULL\n"); \
2219 procmsg_msginfo_free(msginfo); \
2220 return NULL; \
2224 PARSE_ONE_ELEMENT(' ');
2225 msgnum = atoi(buf);
2227 PARSE_ONE_ELEMENT(' ');
2228 g_return_val_if_fail(!strcmp(buf, "FETCH"), NULL);
2230 g_return_val_if_fail(*cur_pos == '(', NULL);
2231 cur_pos++;
2233 while (*cur_pos != '\0' && *cur_pos != ')') {
2234 while (*cur_pos == ' ') cur_pos++;
2236 if (!strncmp(cur_pos, "UID ", 4)) {
2237 cur_pos += 4;
2238 uid = strtoul(cur_pos, &cur_pos, 10);
2239 } else if (!strncmp(cur_pos, "FLAGS ", 6)) {
2240 cur_pos += 6;
2241 if (*cur_pos != '(') {
2242 g_warning("*cur_pos != '('\n");
2243 procmsg_msginfo_free(msginfo);
2244 return NULL;
2246 cur_pos++;
2247 PARSE_ONE_ELEMENT(')');
2248 imap_flags = imap_parse_flags(buf);
2249 } else if (!strncmp(cur_pos, "RFC822.SIZE ", 12)) {
2250 cur_pos += 12;
2251 size = strtol(cur_pos, &cur_pos, 10);
2252 } else if (!strncmp(cur_pos, "RFC822.HEADER ", 14)) {
2253 gchar *headers;
2255 cur_pos += 14;
2256 cur_pos = imap_get_header(sock, cur_pos, &headers,
2257 line_str);
2258 msginfo = procheader_parse_str(headers, flags, FALSE, FALSE);
2259 g_free(headers);
2260 } else {
2261 g_warning("invalid FETCH response: %s\n", cur_pos);
2262 break;
2266 if (msginfo) {
2267 msginfo->msgnum = uid;
2268 msginfo->size = size;
2269 msginfo->flags.tmp_flags |= imap_flags.tmp_flags;
2270 msginfo->flags.perm_flags = imap_flags.perm_flags;
2273 return msginfo;
2276 gint imap_msg_set_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2278 Folder *folder;
2279 IMAPSession *session;
2280 IMAPFlags iflags = 0;
2281 gint ok = IMAP_SUCCESS;
2283 g_return_val_if_fail(msginfo != NULL, -1);
2284 g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
2285 g_return_val_if_fail(msginfo->folder != NULL, -1);
2286 g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2288 folder = msginfo->folder->folder;
2289 g_return_val_if_fail(folder->type == F_IMAP, -1);
2291 session = imap_session_get(folder);
2292 if (!session) return -1;
2294 if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2295 if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2296 if (iflags) {
2297 ok = imap_set_message_flags(session, msginfo->msgnum,
2298 msginfo->msgnum, iflags, TRUE);
2299 if (ok != IMAP_SUCCESS) return ok;
2302 if (flags & MSG_UNREAD)
2303 ok = imap_set_message_flags(session, msginfo->msgnum,
2304 msginfo->msgnum, IMAP_FLAG_SEEN,
2305 FALSE);
2306 return ok;
2309 gint imap_msg_unset_perm_flags(MsgInfo *msginfo, MsgPermFlags flags)
2311 Folder *folder;
2312 IMAPSession *session;
2313 IMAPFlags iflags = 0;
2314 gint ok = IMAP_SUCCESS;
2316 g_return_val_if_fail(msginfo != NULL, -1);
2317 g_return_val_if_fail(MSG_IS_IMAP(msginfo->flags), -1);
2318 g_return_val_if_fail(msginfo->folder != NULL, -1);
2319 g_return_val_if_fail(msginfo->folder->folder != NULL, -1);
2321 folder = msginfo->folder->folder;
2322 g_return_val_if_fail(folder->type == F_IMAP, -1);
2324 session = imap_session_get(folder);
2325 if (!session) return -1;
2327 if (flags & MSG_MARKED) iflags |= IMAP_FLAG_FLAGGED;
2328 if (flags & MSG_REPLIED) iflags |= IMAP_FLAG_ANSWERED;
2329 if (iflags) {
2330 ok = imap_set_message_flags(session, msginfo->msgnum,
2331 msginfo->msgnum, iflags, FALSE);
2332 if (ok != IMAP_SUCCESS) return ok;
2335 if (flags & MSG_UNREAD)
2336 ok = imap_set_message_flags(session, msginfo->msgnum,
2337 msginfo->msgnum, IMAP_FLAG_SEEN,
2338 TRUE);
2339 return ok;
2342 static gint imap_set_message_flags(IMAPSession *session,
2343 guint32 first_uid,
2344 guint32 last_uid,
2345 IMAPFlags flags,
2346 gboolean is_set)
2348 GString *buf;
2349 gint ok;
2351 buf = g_string_new(is_set ? "+FLAGS (" : "-FLAGS (");
2353 if (IMAP_IS_SEEN(flags)) g_string_append(buf, "\\Seen ");
2354 if (IMAP_IS_ANSWERED(flags)) g_string_append(buf, "\\Answered ");
2355 if (IMAP_IS_FLAGGED(flags)) g_string_append(buf, "\\Flagged ");
2356 if (IMAP_IS_DELETED(flags)) g_string_append(buf, "\\Deleted ");
2357 if (IMAP_IS_DRAFT(flags)) g_string_append(buf, "\\Draft");
2359 if (buf->str[buf->len - 1] == ' ')
2360 g_string_truncate(buf, buf->len - 1);
2362 g_string_append_c(buf, ')');
2364 ok = imap_cmd_store(SESSION(session)->sock, first_uid, last_uid,
2365 buf->str);
2366 g_string_free(buf, TRUE);
2368 return ok;
2371 static gint imap_select(IMAPSession *session, IMAPFolder *folder,
2372 const gchar *path,
2373 gint *exists, gint *recent, gint *unseen,
2374 guint32 *uid_validity)
2376 gchar *real_path;
2377 gint ok;
2378 gint exists_, recent_, unseen_, uid_validity_;
2380 if (!exists || !recent || !unseen || !uid_validity) {
2381 if (session->mbox && strcmp(session->mbox, path) == 0)
2382 return IMAP_SUCCESS;
2383 exists = &exists_;
2384 recent = &recent_;
2385 unseen = &unseen_;
2386 uid_validity = &uid_validity_;
2389 g_free(session->mbox);
2390 session->mbox = NULL;
2392 real_path = imap_get_real_path(folder, path);
2393 ok = imap_cmd_select(SESSION(session)->sock, real_path,
2394 exists, recent, unseen, uid_validity);
2395 if (ok != IMAP_SUCCESS)
2396 log_warning(_("can't select folder: %s\n"), real_path);
2397 else
2398 session->mbox = g_strdup(path);
2399 g_free(real_path);
2401 return ok;
2404 #define THROW(err) { ok = err; goto catch; }
2406 static gint imap_get_uid(IMAPSession *session, gint msgnum, guint32 *uid)
2408 gint ok;
2409 GPtrArray *argbuf;
2410 gchar *str;
2411 gint num;
2413 *uid = 0;
2414 argbuf = g_ptr_array_new();
2416 imap_cmd_gen_send(SESSION(session)->sock, "FETCH %d (UID)", msgnum);
2417 if ((ok = imap_cmd_ok(SESSION(session)->sock, argbuf)) != IMAP_SUCCESS)
2418 THROW(ok);
2420 str = search_array_contain_str(argbuf, "FETCH");
2421 if (!str) THROW(IMAP_ERROR);
2423 if (sscanf(str, "%d FETCH (UID %d)", &num, uid) != 2 ||
2424 num != msgnum) {
2425 g_warning("imap_get_uid(): invalid FETCH line.\n");
2426 THROW(IMAP_ERROR);
2429 catch:
2430 ptr_array_free_strings(argbuf);
2431 g_ptr_array_free(argbuf, TRUE);
2433 return ok;
2436 static gint imap_status(IMAPSession *session, IMAPFolder *folder,
2437 const gchar *path,
2438 gint *messages, gint *recent,
2439 guint32 *uid_next, guint32 *uid_validity,
2440 gint *unseen)
2442 gchar *real_path;
2443 gchar *real_path_;
2444 gint ok;
2445 GPtrArray *argbuf;
2446 gchar *str;
2448 *messages = *recent = *uid_next = *uid_validity = *unseen = 0;
2450 argbuf = g_ptr_array_new();
2452 real_path = imap_get_real_path(folder, path);
2453 QUOTE_IF_REQUIRED(real_path_, real_path);
2454 imap_cmd_gen_send(SESSION(session)->sock, "STATUS %s "
2455 "(MESSAGES RECENT UIDNEXT UIDVALIDITY UNSEEN)",
2456 real_path_);
2458 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
2459 if (ok != IMAP_SUCCESS) THROW(ok);
2461 str = search_array_str(argbuf, "STATUS");
2462 if (!str) THROW(IMAP_ERROR);
2464 str = strchr(str, '(');
2465 if (!str) THROW(IMAP_ERROR);
2466 str++;
2467 while (*str != '\0' && *str != ')') {
2468 while (*str == ' ') str++;
2470 if (!strncmp(str, "MESSAGES ", 9)) {
2471 str += 9;
2472 *messages = strtol(str, &str, 10);
2473 } else if (!strncmp(str, "RECENT ", 7)) {
2474 str += 7;
2475 *recent = strtol(str, &str, 10);
2476 } else if (!strncmp(str, "UIDNEXT ", 8)) {
2477 str += 8;
2478 *uid_next = strtoul(str, &str, 10);
2479 } else if (!strncmp(str, "UIDVALIDITY ", 12)) {
2480 str += 12;
2481 *uid_validity = strtoul(str, &str, 10);
2482 } else if (!strncmp(str, "UNSEEN ", 7)) {
2483 str += 7;
2484 *unseen = strtol(str, &str, 10);
2485 } else {
2486 g_warning("invalid STATUS response: %s\n", str);
2487 break;
2491 catch:
2492 g_free(real_path);
2493 ptr_array_free_strings(argbuf);
2494 g_ptr_array_free(argbuf, TRUE);
2496 return ok;
2499 #undef THROW
2502 /* low-level IMAP4rev1 commands */
2504 static gint imap_cmd_login(SockInfo *sock,
2505 const gchar *user, const gchar *pass)
2507 gchar *user_, *pass_;
2508 gint ok;
2510 QUOTE_IF_REQUIRED(user_, user);
2511 QUOTE_IF_REQUIRED(pass_, pass);
2512 imap_cmd_gen_send(sock, "LOGIN %s %s", user_, pass_);
2514 ok = imap_cmd_ok(sock, NULL);
2515 if (ok != IMAP_SUCCESS)
2516 log_warning(_("IMAP4 login failed.\n"));
2518 return ok;
2521 static gint imap_cmd_logout(SockInfo *sock)
2523 imap_cmd_gen_send(sock, "LOGOUT");
2524 return imap_cmd_ok(sock, NULL);
2527 /* Send a NOOP, and examine the server's response to see whether this
2528 * connection is pre-authenticated or not. */
2529 static gint imap_greeting(SockInfo *sock, gboolean *is_preauth)
2531 GPtrArray *argbuf;
2532 gint r;
2534 imap_cmd_gen_send(sock, "NOOP");
2535 argbuf = g_ptr_array_new(); /* will hold messages sent back */
2536 r = imap_cmd_ok(sock, argbuf);
2537 *is_preauth = search_array_starts(argbuf, "PREAUTH") != NULL;
2539 return r;
2542 static gint imap_cmd_noop(SockInfo *sock)
2544 imap_cmd_gen_send(sock, "NOOP");
2545 return imap_cmd_ok(sock, NULL);
2548 static gint imap_cmd_starttls(SockInfo *sock)
2550 imap_cmd_gen_send(sock, "STARTTLS");
2551 return imap_cmd_ok(sock, NULL);
2554 #define THROW(err) { ok = err; goto catch; }
2556 static gint imap_cmd_namespace(SockInfo *sock, gchar **ns_str)
2558 gint ok;
2559 GPtrArray *argbuf;
2560 gchar *str;
2562 argbuf = g_ptr_array_new();
2564 imap_cmd_gen_send(sock, "NAMESPACE");
2565 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW(ok);
2567 str = search_array_str(argbuf, "NAMESPACE");
2568 if (!str) THROW(IMAP_ERROR);
2570 *ns_str = g_strdup(str);
2572 catch:
2573 ptr_array_free_strings(argbuf);
2574 g_ptr_array_free(argbuf, TRUE);
2576 return ok;
2579 #undef THROW
2581 static gint imap_cmd_list(SockInfo *sock, const gchar *ref,
2582 const gchar *mailbox, GPtrArray *argbuf)
2584 gchar *ref_, *mailbox_;
2586 if (!ref) ref = "\"\"";
2587 if (!mailbox) mailbox = "\"\"";
2589 QUOTE_IF_REQUIRED(ref_, ref);
2590 QUOTE_IF_REQUIRED(mailbox_, mailbox);
2591 imap_cmd_gen_send(sock, "LIST %s %s", ref_, mailbox_);
2593 return imap_cmd_ok(sock, argbuf);
2596 #define THROW goto catch
2598 static gint imap_cmd_do_select(SockInfo *sock, const gchar *folder,
2599 gboolean examine,
2600 gint *exists, gint *recent, gint *unseen,
2601 guint32 *uid_validity)
2603 gint ok;
2604 gchar *resp_str;
2605 GPtrArray *argbuf;
2606 gchar *select_cmd;
2607 gchar *folder_;
2609 *exists = *recent = *unseen = *uid_validity = 0;
2610 argbuf = g_ptr_array_new();
2612 if (examine)
2613 select_cmd = "EXAMINE";
2614 else
2615 select_cmd = "SELECT";
2617 QUOTE_IF_REQUIRED(folder_, folder);
2618 imap_cmd_gen_send(sock, "%s %s", select_cmd, folder_);
2620 if ((ok = imap_cmd_ok(sock, argbuf)) != IMAP_SUCCESS) THROW;
2622 resp_str = search_array_contain_str(argbuf, "EXISTS");
2623 if (resp_str) {
2624 if (sscanf(resp_str,"%d EXISTS", exists) != 1) {
2625 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
2626 THROW;
2630 resp_str = search_array_contain_str(argbuf, "RECENT");
2631 if (resp_str) {
2632 if (sscanf(resp_str, "%d RECENT", recent) != 1) {
2633 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2634 THROW;
2638 resp_str = search_array_contain_str(argbuf, "UIDVALIDITY");
2639 if (resp_str) {
2640 if (sscanf(resp_str, "OK [UIDVALIDITY %u] ", uid_validity)
2641 != 1) {
2642 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2643 THROW;
2647 resp_str = search_array_contain_str(argbuf, "UNSEEN");
2648 if (resp_str) {
2649 if (sscanf(resp_str, "OK [UNSEEN %d] ", unseen) != 1) {
2650 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2651 THROW;
2655 catch:
2656 ptr_array_free_strings(argbuf);
2657 g_ptr_array_free(argbuf, TRUE);
2659 return ok;
2662 static gint imap_cmd_select(SockInfo *sock, const gchar *folder,
2663 gint *exists, gint *recent, gint *unseen,
2664 guint32 *uid_validity)
2666 return imap_cmd_do_select(sock, folder, FALSE,
2667 exists, recent, unseen, uid_validity);
2670 static gint imap_cmd_examine(SockInfo *sock, const gchar *folder,
2671 gint *exists, gint *recent, gint *unseen,
2672 guint32 *uid_validity)
2674 return imap_cmd_do_select(sock, folder, TRUE,
2675 exists, recent, unseen, uid_validity);
2678 #undef THROW
2680 static gint imap_cmd_create(SockInfo *sock, const gchar *folder)
2682 gchar *folder_;
2684 QUOTE_IF_REQUIRED(folder_, folder);
2685 imap_cmd_gen_send(sock, "CREATE %s", folder_);
2687 return imap_cmd_ok(sock, NULL);
2690 static gint imap_cmd_rename(SockInfo *sock, const gchar *old_folder,
2691 const gchar *new_folder)
2693 gchar *old_folder_, *new_folder_;
2695 QUOTE_IF_REQUIRED(old_folder_, old_folder);
2696 QUOTE_IF_REQUIRED(new_folder_, new_folder);
2697 imap_cmd_gen_send(sock, "RENAME %s %s", old_folder_, new_folder_);
2699 return imap_cmd_ok(sock, NULL);
2702 static gint imap_cmd_delete(SockInfo *sock, const gchar *folder)
2704 gchar *folder_;
2706 QUOTE_IF_REQUIRED(folder_, folder);
2707 imap_cmd_gen_send(sock, "DELETE %s", folder_);
2709 return imap_cmd_ok(sock, NULL);
2712 static gint imap_cmd_fetch(SockInfo *sock, guint32 uid, const gchar *filename)
2714 gint ok;
2715 gchar buf[IMAPBUFSIZE];
2716 gchar *cur_pos;
2717 gchar size_str[32];
2718 glong size_num;
2720 g_return_val_if_fail(filename != NULL, IMAP_ERROR);
2722 imap_cmd_gen_send(sock, "UID FETCH %d BODY[]", uid);
2724 while ((ok = imap_cmd_gen_recv(sock, buf, sizeof(buf)))
2725 == IMAP_SUCCESS) {
2726 if (buf[0] != '*' || buf[1] != ' ')
2727 return IMAP_ERROR;
2728 if (strstr(buf, "FETCH") != NULL)
2729 break;
2731 if (ok != IMAP_SUCCESS)
2732 return ok;
2734 cur_pos = strchr(buf, '{');
2735 g_return_val_if_fail(cur_pos != NULL, IMAP_ERROR);
2736 cur_pos = strchr_cpy(cur_pos + 1, '}', size_str, sizeof(size_str));
2737 g_return_val_if_fail(cur_pos != NULL, IMAP_ERROR);
2738 size_num = atol(size_str);
2740 if (*cur_pos != '\0') return IMAP_ERROR;
2742 if (recv_bytes_write_to_file(sock, size_num, filename) != 0)
2743 return IMAP_ERROR;
2745 if (imap_cmd_gen_recv(sock, buf, sizeof(buf)) != IMAP_SUCCESS)
2746 return IMAP_ERROR;
2748 if (buf[0] == '\0' || buf[strlen(buf) - 1] != ')')
2749 return IMAP_ERROR;
2751 ok = imap_cmd_ok(sock, NULL);
2753 return ok;
2756 static gint imap_cmd_append(SockInfo *sock, const gchar *destfolder,
2757 const gchar *file)
2759 gint ok;
2760 gint size;
2761 gchar *destfolder_;
2762 gchar buf[BUFFSIZE];
2763 FILE *fp;
2765 g_return_val_if_fail(file != NULL, IMAP_ERROR);
2767 size = get_file_size_as_crlf(file);
2768 if ((fp = fopen(file, "rb")) == NULL) {
2769 FILE_OP_ERROR(file, "fopen");
2770 return -1;
2772 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2773 imap_cmd_gen_send(sock, "APPEND %s (\\Seen) {%d}", destfolder_, size);
2775 ok = imap_cmd_gen_recv(sock, buf, sizeof(buf));
2776 if (ok != IMAP_SUCCESS || buf[0] != '+' || buf[1] != ' ') {
2777 log_warning(_("can't append %s to %s\n"), file, destfolder_);
2778 fclose(fp);
2779 return IMAP_ERROR;
2782 log_print("IMAP4> %s\n", _("(sending file...)"));
2784 while (fgets(buf, sizeof(buf), fp) != NULL) {
2785 strretchomp(buf);
2786 if (sock_puts(sock, buf) < 0) {
2787 fclose(fp);
2788 sock_close(sock);
2789 return -1;
2793 if (ferror(fp)) {
2794 FILE_OP_ERROR(file, "fgets");
2795 fclose(fp);
2796 sock_close(sock);
2797 return -1;
2800 sock_puts(sock, "");
2802 fclose(fp);
2803 return imap_cmd_ok(sock, NULL);
2806 static gint imap_cmd_copy(SockInfo *sock, guint32 uid, const gchar *destfolder)
2808 gint ok;
2809 gchar *destfolder_;
2811 g_return_val_if_fail(destfolder != NULL, IMAP_ERROR);
2813 QUOTE_IF_REQUIRED(destfolder_, destfolder);
2814 imap_cmd_gen_send(sock, "UID COPY %d %s", uid, destfolder_);
2816 ok = imap_cmd_ok(sock, NULL);
2817 if (ok != IMAP_SUCCESS) {
2818 log_warning(_("can't copy %d to %s\n"), uid, destfolder_);
2819 return -1;
2822 return ok;
2825 gint imap_cmd_envelope(SockInfo *sock, guint32 first_uid, guint32 last_uid)
2827 imap_cmd_gen_send
2828 (sock, "UID FETCH %d:%d (UID FLAGS RFC822.SIZE RFC822.HEADER)",
2829 first_uid, last_uid);
2831 return IMAP_SUCCESS;
2834 static gint imap_cmd_store(SockInfo *sock, guint32 first_uid, guint32 last_uid,
2835 gchar *sub_cmd)
2837 gint ok;
2839 imap_cmd_gen_send(sock, "UID STORE %d:%d %s",
2840 first_uid, last_uid, sub_cmd);
2842 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2843 log_warning(_("error while imap command: STORE %d:%d %s\n"),
2844 first_uid, last_uid, sub_cmd);
2845 return ok;
2848 return IMAP_SUCCESS;
2851 static gint imap_cmd_expunge(SockInfo *sock)
2853 gint ok;
2855 imap_cmd_gen_send(sock, "EXPUNGE");
2856 if ((ok = imap_cmd_ok(sock, NULL)) != IMAP_SUCCESS) {
2857 log_warning(_("error while imap command: EXPUNGE\n"));
2858 return ok;
2861 return IMAP_SUCCESS;
2864 static gint imap_cmd_ok(SockInfo *sock, GPtrArray *argbuf)
2866 gint ok;
2867 gchar buf[IMAPBUFSIZE];
2868 gint cmd_num;
2869 gchar cmd_status[IMAPBUFSIZE];
2871 while ((ok = imap_cmd_gen_recv(sock, buf, sizeof(buf)))
2872 == IMAP_SUCCESS) {
2873 if (buf[0] == '*' && buf[1] == ' ') {
2874 if (argbuf)
2875 g_ptr_array_add(argbuf, g_strdup(buf + 2));
2876 continue;
2879 if (sscanf(buf, "%d %s", &cmd_num, cmd_status) < 2)
2880 return IMAP_ERROR;
2881 else if (cmd_num == imap_cmd_count &&
2882 !strcmp(cmd_status, "OK")) {
2883 if (argbuf)
2884 g_ptr_array_add(argbuf, g_strdup(buf));
2885 return IMAP_SUCCESS;
2886 } else
2887 return IMAP_ERROR;
2890 return ok;
2893 static void imap_cmd_gen_send(SockInfo *sock, const gchar *format, ...)
2895 gchar buf[IMAPBUFSIZE];
2896 gchar tmp[IMAPBUFSIZE];
2897 gchar *p;
2898 va_list args;
2900 va_start(args, format);
2901 g_vsnprintf(tmp, sizeof(tmp), format, args);
2902 va_end(args);
2904 imap_cmd_count++;
2906 g_snprintf(buf, sizeof(buf), "%d %s\r\n", imap_cmd_count, tmp);
2907 if (!strncasecmp(tmp, "LOGIN ", 6) && (p = strchr(tmp + 6, ' '))) {
2908 *p = '\0';
2909 log_print("IMAP4> %d %s ********\n", imap_cmd_count, tmp);
2910 } else
2911 log_print("IMAP4> %d %s\n", imap_cmd_count, tmp);
2913 sock_write(sock, buf, strlen(buf));
2916 static gint imap_cmd_gen_recv(SockInfo *sock, gchar *buf, gint size)
2918 if (sock_gets(sock, buf, size) == -1)
2919 return IMAP_SOCKET;
2921 strretchomp(buf);
2923 log_print("IMAP4< %s\n", buf);
2925 return IMAP_SUCCESS;
2929 /* misc utility functions */
2931 static gchar *strchr_cpy(const gchar *src, gchar ch, gchar *dest, gint len)
2933 gchar *tmp;
2935 dest[0] = '\0';
2936 tmp = strchr(src, ch);
2937 if (!tmp)
2938 return NULL;
2940 memcpy(dest, src, MIN(tmp - src, len - 1));
2941 dest[MIN(tmp - src, len - 1)] = '\0';
2943 return tmp + 1;
2946 static gchar *get_quoted(const gchar *src, gchar ch, gchar *dest, gint len)
2948 const gchar *p = src;
2949 gint n = 0;
2951 g_return_val_if_fail(*p == ch, NULL);
2953 *dest = '\0';
2954 p++;
2956 while (*p != '\0' && *p != ch) {
2957 if (n < len - 1) {
2958 if (*p == '\\' && *(p + 1) != '\0')
2959 p++;
2960 *dest++ = *p++;
2961 } else
2962 p++;
2963 n++;
2966 *dest = '\0';
2967 return (gchar *)(*p == ch ? p + 1 : p);
2970 static gchar *search_array_starts(GPtrArray *array, const gchar *str)
2972 gint i;
2973 size_t len;
2975 g_return_val_if_fail(str != NULL, NULL);
2976 len = strlen(str);
2977 for (i = 0; i < array->len; i++) {
2978 gchar *tmp;
2979 tmp = g_ptr_array_index(array, i);
2980 if (strncmp(tmp, str, len) == 0)
2981 return tmp;
2983 return NULL;
2986 static gchar *search_array_contain_str(GPtrArray *array, gchar *str)
2988 gint i;
2990 for (i = 0; i < array->len; i++) {
2991 gchar *tmp;
2993 tmp = g_ptr_array_index(array, i);
2994 if (strstr(tmp, str) != NULL)
2995 return tmp;
2998 return NULL;
3001 static gchar *search_array_str(GPtrArray *array, gchar *str)
3003 gint i;
3004 gint len;
3006 len = strlen(str);
3008 for (i = 0; i < array->len; i++) {
3009 gchar *tmp;
3011 tmp = g_ptr_array_index(array, i);
3012 if (!strncmp(tmp, str, len))
3013 return tmp;
3016 return NULL;
3019 static void imap_path_separator_subst(gchar *str, gchar separator)
3021 gchar *p;
3022 gboolean in_escape = FALSE;
3024 if (!separator || separator == '/') return;
3026 for (p = str; *p != '\0'; p++) {
3027 if (*p == '/' && !in_escape)
3028 *p = separator;
3029 else if (*p == '&' && *(p + 1) != '-' && !in_escape)
3030 in_escape = TRUE;
3031 else if (*p == '-' && in_escape)
3032 in_escape = FALSE;
3036 static gchar *imap_modified_utf7_to_locale(const gchar *mutf7_str)
3038 #if !HAVE_LIBJCONV
3039 return g_strdup(mutf7_str);
3040 #else
3041 static iconv_t cd = (iconv_t)-1;
3042 static gboolean iconv_ok = TRUE;
3043 GString *norm_utf7;
3044 gchar *norm_utf7_p;
3045 size_t norm_utf7_len;
3046 const gchar *p;
3047 gchar *to_str, *to_p;
3048 size_t to_len;
3049 gboolean in_escape = FALSE;
3051 if (!iconv_ok) return g_strdup(mutf7_str);
3053 if (cd == (iconv_t)-1) {
3054 cd = iconv_open(conv_get_current_charset_str(), "UTF-7");
3055 if (cd == (iconv_t)-1) {
3056 g_warning("iconv cannot convert UTF-7 to %s\n",
3057 conv_get_current_charset_str());
3058 iconv_ok = FALSE;
3059 return g_strdup(mutf7_str);
3063 norm_utf7 = g_string_new(NULL);
3065 for (p = mutf7_str; *p != '\0'; p++) {
3066 /* replace: '&' -> '+',
3067 "&-" -> '&',
3068 escaped ',' -> '/' */
3069 if (!in_escape && *p == '&') {
3070 if (*(p + 1) != '-') {
3071 g_string_append_c(norm_utf7, '+');
3072 in_escape = TRUE;
3073 } else {
3074 g_string_append_c(norm_utf7, '&');
3075 p++;
3077 } else if (in_escape && *p == ',') {
3078 g_string_append_c(norm_utf7, '/');
3079 } else if (in_escape && *p == '-') {
3080 g_string_append_c(norm_utf7, '-');
3081 in_escape = FALSE;
3082 } else {
3083 g_string_append_c(norm_utf7, *p);
3087 norm_utf7_p = norm_utf7->str;
3088 norm_utf7_len = norm_utf7->len;
3089 to_len = strlen(mutf7_str) * 5;
3090 to_p = to_str = g_malloc(to_len + 1);
3092 if (iconv(cd, &norm_utf7_p, &norm_utf7_len, &to_p, &to_len) == -1) {
3093 g_warning("iconv cannot convert UTF-7 to %s\n",
3094 conv_get_current_charset_str());
3095 g_string_free(norm_utf7, TRUE);
3096 g_free(to_str);
3097 return g_strdup(mutf7_str);
3100 /* second iconv() call for flushing */
3101 iconv(cd, NULL, NULL, &to_p, &to_len);
3102 g_string_free(norm_utf7, TRUE);
3103 *to_p = '\0';
3105 return to_str;
3106 #endif /* !HAVE_LIBJCONV */
3109 static gchar *imap_locale_to_modified_utf7(const gchar *from)
3111 #if !HAVE_LIBJCONV
3112 return g_strdup(from);
3113 #else
3114 static iconv_t cd = (iconv_t)-1;
3115 static gboolean iconv_ok = TRUE;
3116 gchar *norm_utf7, *norm_utf7_p;
3117 size_t from_len, norm_utf7_len;
3118 GString *to_str;
3119 gchar *from_tmp, *to, *p;
3120 gboolean in_escape = FALSE;
3122 if (!iconv_ok) return g_strdup(from);
3124 if (cd == (iconv_t)-1) {
3125 cd = iconv_open("UTF-7", conv_get_current_charset_str());
3126 if (cd == (iconv_t)-1) {
3127 g_warning("iconv cannot convert %s to UTF-7\n",
3128 conv_get_current_charset_str());
3129 iconv_ok = FALSE;
3130 return g_strdup(from);
3134 Xstrdup_a(from_tmp, from, return g_strdup(from));
3135 from_len = strlen(from);
3136 norm_utf7_len = from_len * 5;
3137 Xalloca(norm_utf7, norm_utf7_len + 1, return g_strdup(from));
3138 norm_utf7_p = norm_utf7;
3140 #define IS_PRINT(ch) (isprint(ch) && isascii(ch))
3142 while (from_len > 0) {
3143 if (IS_PRINT(*from_tmp)) {
3144 /* printable ascii char */
3145 *norm_utf7_p = *from_tmp;
3146 norm_utf7_p++;
3147 from_tmp++;
3148 from_len--;
3149 } else {
3150 size_t mblen;
3152 /* unprintable char: convert to UTF-7 */
3153 for (mblen = 0;
3154 !IS_PRINT(from_tmp[mblen]) && mblen < from_len;
3155 mblen++)
3157 from_len -= mblen;
3158 if (iconv(cd, &from_tmp, &mblen,
3159 &norm_utf7_p, &norm_utf7_len) == -1) {
3160 g_warning("iconv cannot convert %s to UTF-7\n",
3161 conv_get_current_charset_str());
3162 return g_strdup(from);
3165 /* second iconv() call for flushing */
3166 iconv(cd, NULL, NULL, &norm_utf7_p, &norm_utf7_len);
3170 #undef IS_PRINT
3172 *norm_utf7_p = '\0';
3173 to_str = g_string_new(NULL);
3174 for (p = norm_utf7; p < norm_utf7_p; p++) {
3175 /* replace: '&' -> "&-",
3176 '+' -> '&',
3177 escaped '/' -> ',' */
3178 if (!in_escape && *p == '&') {
3179 g_string_append(to_str, "&-");
3180 } else if (!in_escape && *p == '+') {
3181 g_string_append_c(to_str, '&');
3182 in_escape = TRUE;
3183 } else if (in_escape && *p == '/') {
3184 g_string_append_c(to_str, ',');
3185 } else if (in_escape && *p == '-') {
3186 in_escape = FALSE;
3187 g_string_append_c(to_str, '-');
3188 } else {
3189 g_string_append_c(to_str, *p);
3193 if (in_escape) {
3194 in_escape = FALSE;
3195 g_string_append_c(to_str, '-');
3198 to = to_str->str;
3199 g_string_free(to_str, FALSE);
3201 return to;
3202 #endif
3205 static gboolean imap_rename_folder_func(GNode *node, gpointer data)
3207 FolderItem *item = node->data;
3208 gchar **paths = data;
3209 const gchar *oldpath = paths[0];
3210 const gchar *newpath = paths[1];
3211 gchar *base;
3212 gchar *new_itempath;
3213 gint oldpathlen;
3215 oldpathlen = strlen(oldpath);
3216 if (strncmp(oldpath, item->path, oldpathlen) != 0) {
3217 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
3218 return TRUE;
3221 base = item->path + oldpathlen;
3222 while (*base == G_DIR_SEPARATOR) base++;
3223 if (*base == '\0')
3224 new_itempath = g_strdup(newpath);
3225 else
3226 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
3227 NULL);
3228 g_free(item->path);
3229 item->path = new_itempath;
3231 return FALSE;
3234 gint imap_get_num_list(Folder *folder, FolderItem *_item, GSList **msgnum_list)
3236 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3237 IMAPSession *session;
3238 gint ok, i, lastuid_old, nummsgs = 0;
3239 GPtrArray *argbuf;
3240 gchar *cmdbuf = NULL;
3241 gchar *dir;
3243 g_return_val_if_fail(folder != NULL, -1);
3244 g_return_val_if_fail(item != NULL, -1);
3245 g_return_val_if_fail(item->item.path != NULL, -1);
3246 g_return_val_if_fail(folder->type == F_IMAP, -1);
3247 g_return_val_if_fail(folder->account != NULL, -1);
3249 session = imap_session_get(folder);
3250 g_return_val_if_fail(session != NULL, -1);
3252 ok = imap_select(session, IMAP_FOLDER(folder), item->item.path,
3253 NULL, NULL, NULL, NULL);
3254 if (ok != IMAP_SUCCESS)
3255 return -1;
3257 argbuf = g_ptr_array_new();
3258 if(item->lastuid) {
3259 cmdbuf = g_strdup_printf("UID FETCH %d:* (UID)", (item->lastuid + 1));
3260 } else {
3261 cmdbuf = g_strdup("FETCH 1:* (UID)");
3263 imap_cmd_gen_send(SESSION(session)->sock, cmdbuf);
3264 g_free(cmdbuf);
3265 ok = imap_cmd_ok(SESSION(session)->sock, argbuf);
3266 if (ok != IMAP_SUCCESS) {
3267 ptr_array_free_strings(argbuf);
3268 g_ptr_array_free(argbuf, TRUE);
3269 return -1;
3272 lastuid_old = item->lastuid;
3273 *msgnum_list = g_slist_copy(item->uid_list);
3274 debug_print("Got %d uids from cache\n", g_slist_length(item->uid_list));
3275 for(i = 0; i < argbuf->len; i++) {
3276 int ret, msgidx, msgnum;
3278 if((ret = sscanf(g_ptr_array_index(argbuf, i), "%d FETCH (UID %d)", &msgidx, &msgnum)) == 2) {
3279 if(msgnum > lastuid_old) {
3280 *msgnum_list = g_slist_prepend(*msgnum_list, GINT_TO_POINTER(msgnum));
3281 item->uid_list = g_slist_prepend(item->uid_list, GINT_TO_POINTER(msgnum));
3282 nummsgs++;
3284 if(msgnum > item->lastuid)
3285 item->lastuid = msgnum;
3290 dir = folder_item_get_path((FolderItem *)item);
3291 debug_print("removing old messages from %s\n", dir);
3292 remove_numbered_files_not_in_list(dir, *msgnum_list);
3293 g_free(dir);
3295 return nummsgs;
3298 static MsgInfo *imap_parse_msg(const gchar *file, FolderItem *item)
3300 MsgInfo *msginfo;
3301 MsgFlags flags;
3303 flags.perm_flags = MSG_NEW|MSG_UNREAD;
3304 flags.tmp_flags = 0;
3306 g_return_val_if_fail(item != NULL, NULL);
3307 g_return_val_if_fail(file != NULL, NULL);
3309 if (item->stype == F_QUEUE) {
3310 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
3311 } else if (item->stype == F_DRAFT) {
3312 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
3315 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3316 if (!msginfo) return NULL;
3318 msginfo->folder = item;
3320 return msginfo;
3324 MsgInfo *imap_get_msginfo(Folder *folder, FolderItem *item, gint uid)
3326 IMAPSession *session;
3327 MsgInfo *msginfo = NULL;
3329 g_return_val_if_fail(folder != NULL, NULL);
3330 g_return_val_if_fail(item != NULL, NULL);
3332 session = imap_session_get(folder);
3333 g_return_val_if_fail(session != NULL, NULL);
3335 if (!(item->stype == F_QUEUE || item->stype == F_DRAFT)) {
3336 GSList *list;
3338 list = imap_get_uncached_messages(session, item, uid, uid);
3339 if (list) {
3340 msginfo = (MsgInfo *)list->data;
3341 list->data = NULL;
3343 procmsg_msg_list_free(list);
3344 } else {
3345 gchar *file;
3347 file = imap_fetch_msg(folder, item, uid);
3348 if (file != NULL) {
3349 msginfo = imap_parse_msg(file, item);
3350 if (msginfo != NULL)
3351 msginfo->msgnum = uid;
3352 g_free(file);
3356 return msginfo;
3359 gboolean imap_check_msgnum_validity(Folder *folder, FolderItem *_item)
3361 IMAPSession *session;
3362 IMAPFolderItem *item = (IMAPFolderItem *)_item;
3363 gint ok, exists = 0, recent = 0, unseen = 0;
3364 guint32 uid_next, uid_validity = 0;
3366 g_return_val_if_fail(folder != NULL, FALSE);
3367 g_return_val_if_fail(item != NULL, FALSE);
3368 g_return_val_if_fail(item->item.folder != NULL, FALSE);
3369 g_return_val_if_fail(item->item.folder->type == F_IMAP, FALSE);
3371 session = imap_session_get(folder);
3372 g_return_val_if_fail(session != NULL, FALSE);
3374 ok = imap_status(session, IMAP_FOLDER(folder), item->item.path,
3375 &exists, &recent, &uid_next, &uid_validity, &unseen);
3376 if (ok != IMAP_SUCCESS)
3377 return FALSE;
3379 if(item->item.mtime == uid_validity)
3380 return TRUE;
3382 debug_print("Freeing imap uid cache");
3383 item->lastuid = 0;
3384 g_slist_free(item->uid_list);
3385 item->uid_list = NULL;
3387 item->item.mtime = uid_validity;
3389 imap_delete_all_cached_messages((FolderItem *)item);
3391 return FALSE;