2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 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.
37 #include "procheader.h"
39 #include "statusbar.h"
40 #include "prefs_account.h"
43 #include "inputdialog.h"
46 #define IMAP4_PORT 143
48 #define IMAPS_PORT 993
51 static GList
*session_list
= NULL
;
53 static gint imap_cmd_count
= 0;
55 static IMAPSession
*imap_session_get (Folder
*folder
);
56 static gchar
*imap_query_password (const gchar
*server
,
59 static void imap_scan_tree_recursive (IMAPSession
*session
,
61 IMAPNameSpace
*namespace);
62 static GSList
*imap_parse_list (IMAPSession
*session
,
64 static gint
imap_create_trash (Folder
*folder
);
66 static gint
imap_do_copy (Folder
*folder
,
69 gboolean remove_source
);
70 static gint
imap_do_copy_msgs_with_dest (Folder
*folder
,
73 gboolean remove_source
);
75 static GSList
*imap_get_uncached_messages (IMAPSession
*session
,
79 static GSList
*imap_delete_cached_messages (GSList
*mlist
,
83 static void imap_delete_all_cached_messages (FolderItem
*item
);
86 static SockInfo
*imap_open (const gchar
*server
,
90 static SockInfo
*imap_open (const gchar
*server
,
96 static gint
imap_set_message_flags (IMAPSession
*session
,
101 static gint
imap_select (IMAPSession
*session
,
107 guint32
*uid_validity
);
108 static gint
imap_get_uid (IMAPSession
*session
,
111 static gint
imap_status (IMAPSession
*session
,
117 guint32
*uid_validity
);
119 static void imap_parse_namespace (IMAPSession
*session
,
121 static IMAPNameSpace
*imap_find_namespace (IMAPFolder
*folder
,
123 static gchar
*imap_get_real_path (IMAPFolder
*folder
,
126 static gchar
*imap_parse_atom (SockInfo
*sock
,
131 static gchar
*imap_parse_one_address (SockInfo
*sock
,
134 gchar
*out_fromname_str
,
136 static gchar
*imap_parse_address (SockInfo
*sock
,
138 gchar
**out_from_str
,
139 gchar
**out_fromname_str
,
141 static MsgFlags
imap_parse_flags (const gchar
*flag_str
);
142 static MsgInfo
*imap_parse_envelope (SockInfo
*sock
,
145 /* low-level IMAP4rev1 commands */
146 static gint
imap_cmd_login (SockInfo
*sock
,
149 static gint
imap_cmd_logout (SockInfo
*sock
);
150 static gint
imap_cmd_noop (SockInfo
*sock
);
151 static gint
imap_cmd_namespace (SockInfo
*sock
,
153 static gint
imap_cmd_list (SockInfo
*sock
,
155 const gchar
*mailbox
,
157 static gint
imap_cmd_do_select (SockInfo
*sock
,
163 guint32
*uid_validity
);
164 static gint
imap_cmd_select (SockInfo
*sock
,
169 guint32
*uid_validity
);
170 static gint
imap_cmd_examine (SockInfo
*sock
,
175 guint32
*uid_validity
);
176 static gint
imap_cmd_create (SockInfo
*sock
,
177 const gchar
*folder
);
178 static gint
imap_cmd_delete (SockInfo
*sock
,
179 const gchar
*folder
);
180 static gint
imap_cmd_envelope (SockInfo
*sock
,
184 static gint
imap_cmd_search (SockInfo
*sock
,
187 static gint
imap_cmd_fetch (SockInfo
*sock
,
189 const gchar
*filename
);
190 static gint
imap_cmd_append (SockInfo
*sock
,
191 const gchar
*destfolder
,
193 static gint
imap_cmd_copy (SockInfo
*sock
,
195 const gchar
*destfolder
);
196 static gint
imap_cmd_store (SockInfo
*sock
,
200 static gint
imap_cmd_expunge (SockInfo
*sock
);
202 static gint
imap_cmd_ok (SockInfo
*sock
,
204 static void imap_cmd_gen_send (SockInfo
*sock
,
205 const gchar
*format
, ...);
206 static gint
imap_cmd_gen_recv (SockInfo
*sock
,
210 /* misc utility functions */
211 static gchar
*strchr_cpy (const gchar
*src
,
215 static gchar
*get_quoted (const gchar
*src
,
219 static gchar
*search_array_contain_str (GPtrArray
*array
,
221 static void imap_path_separator_subst (gchar
*str
,
224 static IMAPSession
*imap_session_get(Folder
*folder
)
226 RemoteFolder
*rfolder
= REMOTE_FOLDER(folder
);
229 g_return_val_if_fail(folder
!= NULL
, NULL
);
230 g_return_val_if_fail(folder
->type
== F_IMAP
, NULL
);
231 g_return_val_if_fail(folder
->account
!= NULL
, NULL
);
234 port
= folder
->account
->set_imapport
? folder
->account
->imapport
237 port
= folder
->account
->set_imapport
? folder
->account
->imapport
238 : (folder
->account
->ssl_imap
? IMAPS_PORT
: IMAP4_PORT
);
241 if (!rfolder
->session
) {
244 imap_session_new(folder
->account
->recv_server
, port
,
245 folder
->account
->userid
,
246 folder
->account
->passwd
);
248 imap_session_new(folder
->account
->recv_server
, port
,
249 folder
->account
->userid
,
250 folder
->account
->passwd
,
251 folder
->account
->ssl_imap
);
253 if (rfolder
->session
)
254 imap_parse_namespace(IMAP_SESSION(rfolder
->session
),
255 IMAP_FOLDER(folder
));
257 return IMAP_SESSION(rfolder
->session
);
260 if (imap_cmd_noop(rfolder
->session
->sock
) != IMAP_SUCCESS
) {
261 log_warning(_("IMAP4 connection to %s:%d has been"
262 " disconnected. Reconnecting...\n"),
263 folder
->account
->recv_server
, port
);
264 session_destroy(rfolder
->session
);
267 imap_session_new(folder
->account
->recv_server
, port
,
268 folder
->account
->userid
,
269 folder
->account
->passwd
);
271 imap_session_new(folder
->account
->recv_server
, port
,
272 folder
->account
->userid
,
273 folder
->account
->passwd
,
274 folder
->account
->ssl_imap
);
276 if (rfolder
->session
)
277 imap_parse_namespace(IMAP_SESSION(rfolder
->session
),
278 IMAP_FOLDER(folder
));
282 return IMAP_SESSION(rfolder
->session
);
285 static gchar
*imap_query_password(const gchar
*server
, const gchar
*user
)
290 message
= g_strdup_printf(_("Input password for %s on %s:"),
292 pass
= input_dialog_with_invisible(_("Input password"), message
, NULL
);
299 Session
*imap_session_new(const gchar
*server
, gushort port
,
300 const gchar
*user
, const gchar
*pass
)
302 Session
*imap_session_new(const gchar
*server
, gushort port
,
303 const gchar
*user
, const gchar
*pass
,
307 gchar buf
[IMAPBUFSIZE
];
308 IMAPSession
*session
;
311 g_return_val_if_fail(server
!= NULL
, NULL
);
312 g_return_val_if_fail(user
!= NULL
, NULL
);
316 tmp_pass
= imap_query_password(server
, user
);
319 Xstrdup_a(pass
, tmp_pass
, {g_free(tmp_pass
); return NULL
;});
323 log_message(_("creating IMAP4 connection to %s:%d ...\n"),
327 if ((imap_sock
= imap_open(server
, port
, buf
)) == NULL
)
329 if ((imap_sock
= imap_open(server
, port
, buf
, use_ssl
)) == NULL
)
332 if (imap_cmd_login(imap_sock
, user
, pass
) != IMAP_SUCCESS
) {
333 imap_cmd_logout(imap_sock
);
334 sock_close(imap_sock
);
338 session
= g_new(IMAPSession
, 1);
339 SESSION(session
)->type
= SESSION_IMAP
;
340 SESSION(session
)->server
= g_strdup(server
);
341 SESSION(session
)->sock
= imap_sock
;
342 SESSION(session
)->connected
= TRUE
;
343 SESSION(session
)->phase
= SESSION_READY
;
344 SESSION(session
)->data
= NULL
;
345 session
->mbox
= NULL
;
347 session_list
= g_list_append(session_list
, session
);
349 return SESSION(session
);
352 void imap_session_destroy(IMAPSession
*session
)
355 ssl_done_socket(SESSION(session
)->sock
);
357 sock_close(SESSION(session
)->sock
);
358 SESSION(session
)->sock
= NULL
;
360 g_free(session
->mbox
);
362 session_list
= g_list_remove(session_list
, session
);
365 void imap_session_destroy_all(void)
367 while (session_list
!= NULL
) {
368 IMAPSession
*session
= (IMAPSession
*)session_list
->data
;
370 imap_cmd_logout(SESSION(session
)->sock
);
371 imap_session_destroy(session
);
375 #define THROW goto catch
377 GSList
*imap_get_msg_list(Folder
*folder
, FolderItem
*item
, gboolean use_cache
)
379 GSList
*mlist
= NULL
;
380 IMAPSession
*session
;
381 gint ok
, exists
= 0, recent
= 0, unseen
= 0;
382 guint32 uid_validity
= 0;
383 guint32 first_uid
= 0, last_uid
= 0, begin
;
385 g_return_val_if_fail(folder
!= NULL
, NULL
);
386 g_return_val_if_fail(item
!= NULL
, NULL
);
387 g_return_val_if_fail(folder
->type
== F_IMAP
, NULL
);
388 g_return_val_if_fail(folder
->account
!= NULL
, NULL
);
390 session
= imap_session_get(folder
);
393 mlist
= procmsg_read_cache(item
, FALSE
);
394 item
->last_num
= procmsg_get_last_num_in_cache(mlist
);
395 procmsg_set_flags(mlist
, item
);
400 ok
= imap_select(session
, IMAP_FOLDER(folder
), item
->path
,
401 &exists
, &recent
, &unseen
, &uid_validity
);
402 if (ok
!= IMAP_SUCCESS
) THROW
;
404 ok
= imap_get_uid(session
, 1, &first_uid
);
405 if (ok
!= IMAP_SUCCESS
) THROW
;
407 ok
= imap_get_uid(session
, exists
, &last_uid
);
408 if (ok
!= IMAP_SUCCESS
) THROW
;
410 last_uid
= first_uid
;
412 imap_delete_all_cached_messages(item
);
420 mlist
= procmsg_read_cache(item
, FALSE
);
421 procmsg_set_flags(mlist
, item
);
422 cache_last
= procmsg_get_last_num_in_cache(mlist
);
424 /* calculating the range of envelope to get */
425 if (item
->mtime
!= uid_validity
) {
426 /* mailbox is changed (get all) */
428 } else if (last_uid
< cache_last
) {
429 /* mailbox is changed (get all) */
431 } else if (last_uid
== cache_last
) {
432 /* mailbox unchanged (get none)*/
435 begin
= cache_last
+ 1;
438 item
->mtime
= uid_validity
;
440 if (first_uid
> 0 && last_uid
> 0) {
441 mlist
= imap_delete_cached_messages(mlist
, item
,
443 mlist
= imap_delete_cached_messages(mlist
, item
,
448 mlist
= imap_delete_cached_messages(mlist
, item
,
451 imap_delete_all_cached_messages(item
);
455 if (begin
> 0 && begin
<= last_uid
) {
457 newlist
= imap_get_uncached_messages(session
, item
,
459 mlist
= g_slist_concat(mlist
, newlist
);
462 item
->last_num
= last_uid
;
471 gchar
*imap_fetch_msg(Folder
*folder
, FolderItem
*item
, gint uid
)
473 gchar
*path
, *filename
;
474 IMAPSession
*session
;
477 g_return_val_if_fail(folder
!= NULL
, NULL
);
478 g_return_val_if_fail(item
!= NULL
, NULL
);
480 path
= folder_item_get_path(item
);
481 if (!is_dir_exist(path
))
483 filename
= g_strconcat(path
, G_DIR_SEPARATOR_S
, itos(uid
), NULL
);
486 if (is_file_exist(filename
)) {
487 debug_print(_("message %d has been already cached.\n"), uid
);
491 session
= imap_session_get(folder
);
497 debug_print(_("getting message %d...\n"), uid
);
498 ok
= imap_cmd_fetch(SESSION(session
)->sock
, (guint32
)uid
, filename
);
502 if (ok
!= IMAP_SUCCESS
) {
503 g_warning(_("can't fetch message %d\n"), uid
);
511 gint
imap_add_msg(Folder
*folder
, FolderItem
*dest
, const gchar
*file
,
512 gboolean remove_source
)
514 IMAPSession
*session
;
517 g_return_val_if_fail(folder
!= NULL
, -1);
518 g_return_val_if_fail(dest
!= NULL
, -1);
519 g_return_val_if_fail(file
!= NULL
, -1);
521 session
= imap_session_get(folder
);
525 ok
= imap_cmd_append(SESSION(session
)->sock
, dest
->path
, file
);
526 if (ok
!= IMAP_SUCCESS
) {
527 g_warning(_("can't append message %s\n"), file
);
532 if (unlink(file
) < 0)
533 FILE_OP_ERROR(file
, "unlink");
536 return dest
->last_num
;
539 static gint
imap_do_copy(Folder
*folder
, FolderItem
*dest
, MsgInfo
*msginfo
,
540 gboolean remove_source
)
543 IMAPSession
*session
;
546 g_return_val_if_fail(folder
!= NULL
, -1);
547 g_return_val_if_fail(folder
->type
== F_IMAP
, -1);
548 g_return_val_if_fail(dest
!= NULL
, -1);
549 g_return_val_if_fail(msginfo
!= NULL
, -1);
551 session
= imap_session_get(folder
);
552 if (!session
) return -1;
554 if (msginfo
->folder
== dest
) {
555 g_warning(_("the src folder is identical to the dest.\n"));
559 destdir
= imap_get_real_path(IMAP_FOLDER(folder
), dest
->path
);
562 debug_print(_("Moving message %s%c%d to %s ...\n"),
563 msginfo
->folder
->path
, G_DIR_SEPARATOR
,
564 msginfo
->msgnum
, destdir
);
566 debug_print(_("Copying message %s%c%d to %s ...\n"),
567 msginfo
->folder
->path
, G_DIR_SEPARATOR
,
568 msginfo
->msgnum
, destdir
);
570 ok
= imap_cmd_copy(SESSION(session
)->sock
, msginfo
->msgnum
, destdir
);
572 if (ok
== IMAP_SUCCESS
&& remove_source
) {
573 imap_set_message_flags(session
, msginfo
->msgnum
, msginfo
->msgnum
,
574 IMAP_FLAG_DELETED
, TRUE
);
575 ok
= imap_cmd_expunge(SESSION(session
)->sock
);
584 static gint
imap_do_copy_msgs_with_dest(Folder
*folder
, FolderItem
*dest
,
586 gboolean remove_source
)
591 IMAPSession
*session
;
594 g_return_val_if_fail(folder
!= NULL
, -1);
595 g_return_val_if_fail(dest
!= NULL
, -1);
596 g_return_val_if_fail(msglist
!= NULL
, -1);
598 session
= imap_session_get(folder
);
599 if (!session
) return -1;
601 destdir
= imap_get_real_path(IMAP_FOLDER(folder
), dest
->path
);
603 for (cur
= msglist
; cur
!= NULL
; cur
= cur
->next
) {
604 msginfo
= (MsgInfo
*)cur
->data
;
606 if (msginfo
->folder
== dest
) {
607 g_warning(_("the src folder is identical to the dest.\n"));
612 debug_print(_("Moving message %s%c%d to %s ...\n"),
613 msginfo
->folder
->path
, G_DIR_SEPARATOR
,
614 msginfo
->msgnum
, destdir
);
616 debug_print(_("Copying message %s%c%d to %s ...\n"),
617 msginfo
->folder
->path
, G_DIR_SEPARATOR
,
618 msginfo
->msgnum
, destdir
);
620 ok
= imap_cmd_copy(SESSION(session
)->sock
, msginfo
->msgnum
,
623 if (ok
== IMAP_SUCCESS
&& remove_source
) {
624 imap_set_message_flags
625 (session
, msginfo
->msgnum
, msginfo
->msgnum
,
626 IMAP_FLAG_DELETED
, TRUE
);
631 ok
= imap_cmd_expunge(SESSION(session
)->sock
);
639 gint
imap_move_msg(Folder
*folder
, FolderItem
*dest
, MsgInfo
*msginfo
)
641 return imap_do_copy(folder
, dest
, msginfo
, TRUE
);
644 gint
imap_move_msgs_with_dest(Folder
*folder
, FolderItem
*dest
,
647 return imap_do_copy_msgs_with_dest(folder
, dest
, msglist
, TRUE
);
650 gint
imap_copy_msg(Folder
*folder
, FolderItem
*dest
, MsgInfo
*msginfo
)
652 return imap_do_copy(folder
, dest
, msginfo
, FALSE
);
655 gint
imap_copy_msgs_with_dest(Folder
*folder
, FolderItem
*dest
,
658 return imap_do_copy_msgs_with_dest(folder
, dest
, msglist
, FALSE
);
661 gint
imap_remove_msg(Folder
*folder
, FolderItem
*item
, gint uid
)
663 gint exists
, recent
, unseen
;
664 guint32 uid_validity
;
666 IMAPSession
*session
;
668 g_return_val_if_fail(folder
!= NULL
, -1);
669 g_return_val_if_fail(folder
->type
== F_IMAP
, -1);
670 g_return_val_if_fail(item
!= NULL
, -1);
672 session
= imap_session_get(folder
);
673 if (!session
) return -1;
675 ok
= imap_select(session
, IMAP_FOLDER(folder
), item
->path
,
676 &exists
, &recent
, &unseen
, &uid_validity
);
678 if (ok
!= IMAP_SUCCESS
)
681 ok
= imap_set_message_flags
682 (IMAP_SESSION(REMOTE_FOLDER(folder
)->session
),
683 (guint32
)uid
, (guint32
)uid
, IMAP_FLAG_DELETED
, TRUE
);
685 if (ok
!= IMAP_SUCCESS
) {
686 log_warning(_("can't set deleted flags: %d\n"), uid
);
690 ok
= imap_cmd_expunge(SESSION(session
)->sock
);
692 if (ok
!= IMAP_SUCCESS
) {
693 log_warning(_("can't expunge\n"));
700 gint
imap_remove_all_msg(Folder
*folder
, FolderItem
*item
)
702 gint exists
, recent
, unseen
;
703 guint32 uid_validity
;
705 IMAPSession
*session
;
707 g_return_val_if_fail(folder
!= NULL
, -1);
708 g_return_val_if_fail(item
!= NULL
, -1);
710 session
= imap_session_get(folder
);
711 if (!session
) return -1;
713 ok
= imap_select(session
, IMAP_FOLDER(folder
), item
->path
,
714 &exists
, &recent
, &unseen
, &uid_validity
);
716 if (ok
!= IMAP_SUCCESS
)
721 imap_cmd_gen_send(SESSION(session
)->sock
,
722 "STORE 1:%d +FLAGS (\\Deleted)", exists
);
723 ok
= imap_cmd_ok(SESSION(session
)->sock
, NULL
);
725 if (ok
!= IMAP_SUCCESS
) {
726 log_warning(_("can't set deleted flags: 1:%d\n"), exists
);
730 ok
= imap_cmd_expunge(SESSION(session
)->sock
);
732 if (ok
!= IMAP_SUCCESS
) {
733 log_warning(_("can't expunge\n"));
740 void imap_scan_folder(Folder
*folder
, FolderItem
*item
)
742 IMAPSession
*session
;
743 gint messages
, recent
, unseen
;
744 guint32 uid_validity
;
747 g_return_if_fail(folder
!= NULL
);
748 g_return_if_fail(item
!= NULL
);
750 session
= imap_session_get(folder
);
751 if (!session
) return;
753 ok
= imap_status(session
, IMAP_FOLDER(folder
), item
->path
,
754 &messages
, &recent
, &unseen
, &uid_validity
);
756 if (ok
!= IMAP_SUCCESS
) return;
759 item
->unread
= unseen
;
760 item
->total
= messages
;
761 /* item->mtime = uid_validity; */
764 void imap_scan_tree(Folder
*folder
)
766 IMAPFolder
*imapfolder
= IMAP_FOLDER(folder
);
767 FolderItem
*item
, *inbox
;
768 IMAPSession
*session
;
769 IMAPNameSpace
*namespace = NULL
;
770 gchar
*root_folder
= NULL
;
772 g_return_if_fail(folder
!= NULL
);
773 g_return_if_fail(folder
->account
!= NULL
);
775 session
= imap_session_get(folder
);
776 if (!session
) return;
778 if (imapfolder
->namespace && imapfolder
->namespace->data
)
779 namespace = (IMAPNameSpace
*)imapfolder
->namespace->data
;
781 if (folder
->account
->imap_dir
&& *folder
->account
->imap_dir
) {
783 Xstrdup_a(imap_dir
, folder
->account
->imap_dir
, return);
784 strtailchomp(imap_dir
, '/');
785 root_folder
= g_strconcat
786 (namespace && namespace->name
? namespace->name
: "",
788 if (namespace && namespace->separator
)
789 subst_char(root_folder
, namespace->separator
, '/');
793 debug_print("IMAP root directory: %s\n", root_folder
);
795 folder_tree_destroy(folder
);
796 item
= folder_item_new(folder
->name
, root_folder
);
797 item
->folder
= folder
;
798 folder
->node
= g_node_new(item
);
801 imap_scan_tree_recursive(session
, item
, namespace);
803 if (!folder
->inbox
) {
804 inbox
= folder_item_new("INBOX", "INBOX");
805 inbox
->stype
= F_INBOX
;
806 folder_item_append(item
, inbox
);
807 folder
->inbox
= inbox
;
810 imap_create_trash(folder
);
813 static void imap_scan_tree_recursive(IMAPSession
*session
,
815 IMAPNameSpace
*namespace)
817 IMAPFolder
*imapfolder
;
818 FolderItem
*new_item
;
819 GSList
*item_list
, *cur
;
822 g_return_if_fail(item
!= NULL
);
823 g_return_if_fail(item
->folder
!= NULL
);
824 g_return_if_fail(item
->no_sub
== FALSE
);
826 imapfolder
= IMAP_FOLDER(item
->folder
);
828 if (item
->folder
->ui_func
)
829 item
->folder
->ui_func(item
->folder
, item
,
830 item
->folder
->ui_func_data
);
833 real_path
= imap_get_real_path(imapfolder
, item
->path
);
834 imap_cmd_gen_send(SESSION(session
)->sock
, "LIST \"\" %s%c%%",
836 namespace && namespace->separator
837 ? namespace->separator
: '/');
839 real_path
= g_strdup(namespace && namespace->name
840 ? namespace->name
: "");
841 imap_cmd_gen_send(SESSION(session
)->sock
, "LIST \"\" %s%%",
845 strtailchomp(real_path
, namespace && namespace->separator
846 ? namespace->separator
: '/');
848 item_list
= imap_parse_list(session
, real_path
);
849 for (cur
= item_list
; cur
!= NULL
; cur
= cur
->next
) {
850 new_item
= cur
->data
;
851 if (!strcmp(new_item
->path
, "INBOX")) {
852 if (!item
->folder
->inbox
) {
853 new_item
->stype
= F_INBOX
;
854 item
->folder
->inbox
= new_item
;
856 folder_item_destroy(new_item
);
859 } else if (!item
->parent
&& !item
->folder
->trash
) {
860 if (!strcasecmp(g_basename(new_item
->path
), "Trash")) {
861 new_item
->stype
= F_TRASH
;
862 item
->folder
->trash
= new_item
;
865 folder_item_append(item
, new_item
);
866 if (new_item
->no_select
== FALSE
)
867 imap_scan_folder(new_item
->folder
, new_item
);
868 if (new_item
->no_sub
== FALSE
)
869 imap_scan_tree_recursive(session
, new_item
, namespace);
873 static GSList
*imap_parse_list(IMAPSession
*session
, const gchar
*path
)
875 gchar buf
[IMAPBUFSIZE
];
880 GSList
*item_list
= NULL
;
882 FolderItem
*new_item
;
884 debug_print("getting list of %s ...\n", *path
? path
: "\"\"");
886 str
= g_string_new(NULL
);
889 if (sock_gets(SESSION(session
)->sock
, buf
, sizeof(buf
)) <= 0) {
890 log_warning(_("error occured while getting LIST.\n"));
894 if (buf
[0] != '*' || buf
[1] != ' ') {
895 log_print("IMAP4< %s\n", buf
);
898 debug_print("IMAP4< %s\n", buf
);
900 g_string_assign(str
, buf
);
902 if (strncmp(p
, "LIST ", 5) != 0) continue;
905 if (*p
!= '(') continue;
907 p
= strchr_cpy(p
, ')', flags
, sizeof(flags
));
909 while (*p
== ' ') p
++;
911 p
= strchr_cpy(p
, ' ', separator
, sizeof(separator
));
913 extract_quote(separator
, '"');
914 if (!strcmp(separator
, "NIL"))
918 while (*p
== ' ') p
++;
919 if (*p
== '{' || *p
== '"')
920 p
= imap_parse_atom(SESSION(session
)->sock
, p
,
921 buf
, sizeof(buf
), str
);
923 strncpy2(buf
, p
, sizeof(buf
));
924 strtailchomp(buf
, separator
[0]);
925 if (buf
[0] == '\0') continue;
926 if (!strcmp(buf
, path
)) continue;
928 if (separator
[0] != '\0')
929 subst_char(buf
, separator
[0], '/');
930 name
= g_basename(buf
);
931 if (name
[0] == '.') continue;
933 new_item
= folder_item_new(name
, buf
);
934 if (strcasestr(flags
, "\\Noinferiors") != NULL
)
935 new_item
->no_sub
= TRUE
;
936 if (strcasestr(flags
, "\\Noselect") != NULL
)
937 new_item
->no_select
= TRUE
;
939 item_list
= g_slist_append(item_list
, new_item
);
941 debug_print("folder %s has been added.\n", buf
);
944 g_string_free(str
, TRUE
);
950 gint
imap_create_tree(Folder
*folder
)
954 g_return_val_if_fail(folder
!= NULL
, -1);
955 g_return_val_if_fail(folder
->node
!= NULL
, -1);
956 g_return_val_if_fail(folder
->node
->data
!= NULL
, -1);
957 g_return_val_if_fail(folder
->account
!= NULL
, -1);
959 imap_scan_tree(folder
);
961 item
= FOLDER_ITEM(folder
->node
->data
);
963 if (!folder
->inbox
) {
966 inbox
= folder_item_new("INBOX", "INBOX");
967 inbox
->stype
= F_INBOX
;
968 folder_item_append(item
, inbox
);
969 folder
->inbox
= inbox
;
972 imap_create_trash(folder
);
977 static gint
imap_create_trash(Folder
*folder
)
979 IMAPFolder
*imapfolder
= IMAP_FOLDER(folder
);
981 FolderItem
*new_item
;
983 gchar
*imap_dir
= "";
985 g_return_val_if_fail(folder
!= NULL
, -1);
986 g_return_val_if_fail(folder
->node
!= NULL
, -1);
987 g_return_val_if_fail(folder
->node
->data
!= NULL
, -1);
988 g_return_val_if_fail(folder
->account
!= NULL
, -1);
990 if (folder
->account
->imap_dir
&& *folder
->account
->imap_dir
) {
993 Xstrdup_a(tmpdir
, folder
->account
->imap_dir
, return -1);
994 strtailchomp(tmpdir
, '/');
995 Xalloca(imap_dir
, strlen(tmpdir
) + 2, return -1);
996 g_snprintf(imap_dir
, strlen(tmpdir
) + 2, "%s%c", tmpdir
, '/');
999 if (imapfolder
->namespace && imapfolder
->namespace->data
) {
1000 IMAPNameSpace
*namespace =
1001 (IMAPNameSpace
*)imapfolder
->namespace->data
;
1003 if (*namespace->name
!= '\0') {
1006 Xstrdup_a(name
, namespace->name
, return -1);
1007 subst_char(name
, namespace->separator
, '/');
1008 trash_path
= g_strconcat(name
, imap_dir
, "Trash", NULL
);
1010 trash_path
= g_strconcat(imap_dir
, "Trash", NULL
);
1012 trash_path
= g_strconcat(imap_dir
, "Trash", NULL
);
1014 item
= FOLDER_ITEM(folder
->node
->data
);
1015 new_item
= imap_create_folder(folder
, item
, trash_path
);
1020 new_item
= folder_item_new("Trash", trash_path
);
1021 folder_item_append(item
, new_item
);
1023 path
= folder_item_get_path(new_item
);
1024 if (!is_dir_exist(path
))
1025 make_dir_hier(path
);
1028 g_free(new_item
->name
);
1029 new_item
->name
= g_strdup("Trash");
1031 new_item
->stype
= F_TRASH
;
1032 folder
->trash
= new_item
;
1039 FolderItem
*imap_create_folder(Folder
*folder
, FolderItem
*parent
,
1042 gchar
*dirpath
, *imappath
;
1043 IMAPSession
*session
;
1044 IMAPNameSpace
*namespace;
1045 FolderItem
*new_item
;
1050 g_return_val_if_fail(folder
!= NULL
, NULL
);
1051 g_return_val_if_fail(folder
->account
!= NULL
, NULL
);
1052 g_return_val_if_fail(parent
!= NULL
, NULL
);
1053 g_return_val_if_fail(name
!= NULL
, NULL
);
1055 session
= imap_session_get(folder
);
1056 if (!session
) return NULL
;
1059 dirpath
= g_strconcat(parent
->path
, "/", name
, NULL
);
1060 else if ((p
= strchr(name
, '/')) != NULL
&& *(p
+ 1) != '\0')
1061 dirpath
= g_strdup(name
);
1062 else if (folder
->account
->imap_dir
&& *folder
->account
->imap_dir
) {
1065 Xstrdup_a(imap_dir
, folder
->account
->imap_dir
, return NULL
);
1066 strtailchomp(imap_dir
, '/');
1067 dirpath
= g_strconcat(imap_dir
, "/", name
, NULL
);
1069 dirpath
= g_strdup(name
);
1071 Xstrdup_a(imappath
, dirpath
, {g_free(dirpath
); return NULL
;});
1072 Xstrdup_a(new_name
, name
, {g_free(dirpath
); return NULL
;});
1073 namespace = imap_find_namespace(IMAP_FOLDER(folder
), imappath
);
1074 if (namespace && namespace->separator
) {
1075 imap_path_separator_subst(imappath
, namespace->separator
);
1076 imap_path_separator_subst(new_name
, namespace->separator
);
1077 strtailchomp(new_name
, namespace->separator
);
1079 strtailchomp(dirpath
, '/');
1081 if (strcmp(name
, "INBOX") != 0) {
1084 gboolean exist
= FALSE
;
1086 argbuf
= g_ptr_array_new();
1087 ok
= imap_cmd_list(SESSION(session
)->sock
, NULL
, imappath
,
1089 statusbar_pop_all();
1090 if (ok
!= IMAP_SUCCESS
) {
1091 log_warning(_("can't create mailbox: LIST failed\n"));
1093 g_ptr_array_free(argbuf
, TRUE
);
1097 for (i
= 0; i
< argbuf
->len
; i
++) {
1099 str
= g_ptr_array_index(argbuf
, i
);
1100 if (!strncmp(str
, "LIST ", 5)) {
1105 g_ptr_array_free(argbuf
, TRUE
);
1108 ok
= imap_cmd_create(SESSION(session
)->sock
, imappath
);
1109 statusbar_pop_all();
1110 if (ok
!= IMAP_SUCCESS
) {
1111 log_warning(_("can't create mailbox\n"));
1118 new_item
= folder_item_new(new_name
, dirpath
);
1119 folder_item_append(parent
, new_item
);
1122 dirpath
= folder_item_get_path(new_item
);
1123 if (!is_dir_exist(dirpath
))
1124 make_dir_hier(dirpath
);
1130 gint
imap_remove_folder(Folder
*folder
, FolderItem
*item
)
1133 IMAPSession
*session
;
1135 gint exists
, recent
, unseen
;
1136 guint32 uid_validity
;
1138 g_return_val_if_fail(folder
!= NULL
, -1);
1139 g_return_val_if_fail(item
!= NULL
, -1);
1140 g_return_val_if_fail(item
->path
!= NULL
, -1);
1142 session
= imap_session_get(folder
);
1143 if (!session
) return -1;
1145 path
= imap_get_real_path(IMAP_FOLDER(folder
), item
->path
);
1147 ok
= imap_cmd_examine(SESSION(session
)->sock
, "INBOX",
1148 &exists
, &recent
, &unseen
, &uid_validity
);
1149 statusbar_pop_all();
1150 if (ok
!= IMAP_SUCCESS
) {
1155 ok
= imap_cmd_delete(SESSION(session
)->sock
, path
);
1156 statusbar_pop_all();
1157 if (ok
!= IMAP_SUCCESS
) {
1158 log_warning(_("can't delete mailbox\n"));
1164 folder_item_remove(item
);
1169 static GSList
*imap_get_uncached_messages(IMAPSession
*session
,
1171 guint32 first_uid
, guint32 last_uid
)
1174 GSList
*newlist
= NULL
;
1175 GSList
*llast
= NULL
;
1179 g_return_val_if_fail(session
!= NULL
, NULL
);
1180 g_return_val_if_fail(item
!= NULL
, NULL
);
1181 g_return_val_if_fail(item
->folder
!= NULL
, NULL
);
1182 g_return_val_if_fail(item
->folder
->type
== F_IMAP
, NULL
);
1183 g_return_val_if_fail(first_uid
<= last_uid
, NULL
);
1185 if (imap_cmd_envelope(SESSION(session
)->sock
, first_uid
, last_uid
)
1187 log_warning(_("can't get envelope\n"));
1191 str
= g_string_new(NULL
);
1194 if ((tmp
= sock_getline(SESSION(session
)->sock
)) == NULL
) {
1195 log_warning(_("error occurred while getting envelope.\n"));
1196 g_string_free(str
, TRUE
);
1200 log_print("IMAP4< %s\n", tmp
);
1201 if (tmp
[0] != '*' || tmp
[1] != ' ') {
1205 g_string_assign(str
, tmp
);
1208 msginfo
= imap_parse_envelope(SESSION(session
)->sock
, str
);
1210 log_warning(_("can't parse envelope: %s\n"), str
->str
);
1214 msginfo
->folder
= item
;
1217 llast
= newlist
= g_slist_append(newlist
, msginfo
);
1219 llast
= g_slist_append(llast
, msginfo
);
1220 llast
= llast
->next
;
1224 g_string_free(str
, TRUE
);
1229 static GSList
*imap_delete_cached_messages(GSList
*mlist
, FolderItem
*item
,
1230 guint32 first_uid
, guint32 last_uid
)
1236 g_return_val_if_fail(item
!= NULL
, mlist
);
1237 g_return_val_if_fail(item
->folder
!= NULL
, mlist
);
1238 g_return_val_if_fail(item
->folder
->type
== F_IMAP
, mlist
);
1240 debug_print(_("Deleting cached messages %d - %d ... "),
1241 first_uid
, last_uid
);
1243 dir
= folder_item_get_path(item
);
1244 remove_numbered_files(dir
, first_uid
, last_uid
);
1247 for (cur
= mlist
; cur
!= NULL
; ) {
1250 msginfo
= (MsgInfo
*)cur
->data
;
1251 if (msginfo
!= NULL
&& first_uid
<= msginfo
->msgnum
&&
1252 msginfo
->msgnum
<= last_uid
) {
1253 procmsg_msginfo_free(msginfo
);
1254 mlist
= g_slist_remove(mlist
, msginfo
);
1260 debug_print(_("done.\n"));
1265 static void imap_delete_all_cached_messages(FolderItem
*item
)
1269 g_return_if_fail(item
!= NULL
);
1270 g_return_if_fail(item
->folder
!= NULL
);
1271 g_return_if_fail(item
->folder
->type
== F_IMAP
);
1273 debug_print(_("Deleting all cached messages... "));
1275 dir
= folder_item_get_path(item
);
1276 remove_all_numbered_files(dir
);
1279 debug_print(_("done.\n"));
1283 static SockInfo
*imap_open(const gchar
*server
, gushort port
, gchar
*buf
)
1285 static SockInfo
*imap_open(const gchar
*server
, gushort port
, gchar
*buf
, gboolean use_ssl
)
1290 if ((sock
= sock_connect(server
, port
)) == NULL
) {
1291 log_warning(_("Can't connect to IMAP4 server: %s:%d\n"),
1297 if(use_ssl
&& !ssl_init_socket(sock
)) {
1305 if (imap_cmd_noop(sock
) != IMAP_SUCCESS
) {
1313 #define THROW goto catch
1315 static void imap_parse_namespace(IMAPSession
*session
, IMAPFolder
*folder
)
1321 IMAPNameSpace
*namespace;
1322 GList
*ns_list
= NULL
;
1324 g_return_if_fail(session
!= NULL
);
1325 g_return_if_fail(folder
!= NULL
);
1327 if (folder
->namespace != NULL
) return;
1329 if (imap_cmd_namespace(SESSION(session
)->sock
, &ns_str
)
1331 log_warning(_("can't get namespace\n"));
1335 /* get the first element */
1336 extract_one_parenthesis_with_skip_quote(ns_str
, '"', '(', ')');
1340 while (*p
!= '\0') {
1341 /* parse ("#foo" "/") */
1343 while (*p
&& *p
!= '(') p
++;
1344 if (*p
== '\0') THROW
;
1347 while (*p
&& *p
!= '"') p
++;
1348 if (*p
== '\0') THROW
;
1352 while (*p
&& *p
!= '"') p
++;
1353 if (*p
== '\0') THROW
;
1357 while (*p
&& isspace(*p
)) p
++;
1358 if (*p
== '\0') THROW
;
1359 if (strncmp(p
, "NIL", 3) == 0)
1361 else if (*p
== '"') {
1364 while (*p
&& *p
!= '"') p
++;
1365 if (*p
== '\0') THROW
;
1370 while (*p
&& *p
!= ')') p
++;
1371 if (*p
== '\0') THROW
;
1374 namespace = g_new(IMAPNameSpace
, 1);
1375 namespace->name
= g_strdup(name
);
1376 namespace->separator
= separator
? separator
[0] : '\0';
1377 ns_list
= g_list_append(ns_list
, namespace);
1378 IMAP_FOLDER(folder
)->namespace = ns_list
;
1388 static IMAPNameSpace
*imap_find_namespace(IMAPFolder
*folder
,
1391 IMAPNameSpace
*namespace = NULL
;
1395 g_return_val_if_fail(folder
!= NULL
, NULL
);
1396 g_return_val_if_fail(path
!= NULL
, NULL
);
1398 ns_list
= folder
->namespace;
1400 for (; ns_list
!= NULL
; ns_list
= ns_list
->next
) {
1401 IMAPNameSpace
*tmp_ns
= ns_list
->data
;
1403 Xstrdup_a(name
, tmp_ns
->name
, return namespace);
1404 if (tmp_ns
->separator
&& tmp_ns
->separator
!= '/')
1405 subst_char(name
, tmp_ns
->separator
, '/');
1406 if (strncmp(path
, name
, strlen(name
)) == 0)
1413 static gchar
*imap_get_real_path(IMAPFolder
*folder
, const gchar
*path
)
1416 IMAPNameSpace
*namespace;
1418 g_return_val_if_fail(folder
!= NULL
, NULL
);
1419 g_return_val_if_fail(path
!= NULL
, NULL
);
1421 real_path
= g_strdup(path
);
1422 namespace = imap_find_namespace(folder
, path
);
1423 if (namespace && namespace->separator
)
1424 imap_path_separator_subst(real_path
, namespace->separator
);
1429 static gchar
*imap_parse_atom(SockInfo
*sock
, gchar
*src
,
1430 gchar
*dest
, gint dest_len
, GString
*str
)
1432 gchar
*cur_pos
= src
;
1434 g_return_val_if_fail(str
!= NULL
, cur_pos
);
1436 while (*cur_pos
== ' ') cur_pos
++;
1438 if (!strncmp(cur_pos
, "NIL", 3)) {
1441 } else if (*cur_pos
== '\"') {
1444 p
= get_quoted(cur_pos
, '\"', dest
, dest_len
);
1445 cur_pos
= p
? p
: cur_pos
+ 2;
1446 } else if (*cur_pos
== '{') {
1451 cur_pos
= strchr_cpy(cur_pos
+ 1, '}', buf
, sizeof(buf
));
1454 if ((nextline
= sock_getline(sock
)) == NULL
)
1456 strretchomp(nextline
);
1457 log_print("IMAP4< %s\n", nextline
);
1458 g_string_assign(str
, nextline
);
1460 len
= MIN(len
, strlen(nextline
));
1461 memcpy(dest
, nextline
, MIN(len
, dest_len
- 1));
1462 dest
[MIN(len
, dest_len
- 1)] = '\0';
1463 cur_pos
= str
->str
+ len
;
1469 static gchar
*imap_parse_one_address(SockInfo
*sock
, gchar
*start
,
1470 gchar
*out_from_str
,
1471 gchar
*out_fromname_str
,
1474 gchar buf
[IMAPBUFSIZE
];
1477 gchar
*cur_pos
= start
;
1479 cur_pos
= imap_parse_atom(sock
, cur_pos
, buf
, sizeof(buf
), str
);
1480 conv_unmime_header(out_fromname_str
, IMAPBUFSIZE
, buf
, NULL
);
1482 cur_pos
= imap_parse_atom(sock
, cur_pos
, buf
, sizeof(buf
), str
);
1484 cur_pos
= imap_parse_atom(sock
, cur_pos
, buf
, sizeof(buf
), str
);
1485 Xstrdup_a(userid
, buf
, return cur_pos
+ 1);
1487 cur_pos
= imap_parse_atom(sock
, cur_pos
, buf
, sizeof(buf
), str
);
1488 Xstrdup_a(domain
, buf
, return cur_pos
+ 1);
1490 if (out_fromname_str
[0] != '\0') {
1491 g_snprintf(out_from_str
, IMAPBUFSIZE
, "\"%s\" <%s@%s>",
1492 out_fromname_str
, userid
, domain
);
1494 g_snprintf(out_from_str
, IMAPBUFSIZE
, "%s@%s",
1496 strcpy(out_fromname_str
, out_from_str
);
1499 while (*cur_pos
== ' ') cur_pos
++;
1500 g_return_val_if_fail(*cur_pos
== ')', NULL
);
1505 static gchar
*imap_parse_address(SockInfo
*sock
, gchar
*start
,
1506 gchar
**out_from_str
,
1507 gchar
**out_fromname_str
,
1510 gchar buf
[IMAPBUFSIZE
];
1511 gchar name_buf
[IMAPBUFSIZE
];
1512 gchar
*cur_pos
= start
;
1515 if (out_from_str
) *out_from_str
= NULL
;
1516 if (out_fromname_str
) *out_fromname_str
= NULL
;
1517 buf
[0] = name_buf
[0] = '\0';
1519 if (!strncmp(cur_pos
, "NIL", 3)) {
1520 if (out_from_str
) *out_from_str
= g_strdup("");
1521 if (out_fromname_str
) *out_fromname_str
= g_strdup("");
1525 g_return_val_if_fail(*cur_pos
== '(', NULL
);
1528 addr_str
= g_string_new(NULL
);
1531 gchar ch
= *cur_pos
++;
1532 if (ch
== ')') break;
1534 cur_pos
= imap_parse_one_address
1535 (sock
, cur_pos
, buf
, name_buf
, str
);
1537 g_string_free(addr_str
, TRUE
);
1540 if (addr_str
->str
[0] != '\0')
1541 g_string_append(addr_str
, ", ");
1542 g_string_append(addr_str
, buf
);
1546 if (out_from_str
) *out_from_str
= g_strdup(addr_str
->str
);
1547 if (out_fromname_str
) *out_fromname_str
= g_strdup(name_buf
);
1549 g_string_free(addr_str
, TRUE
);
1554 static MsgFlags
imap_parse_flags(const gchar
*flag_str
)
1556 const gchar
*p
= flag_str
;
1559 flags
.perm_flags
= MSG_UNREAD
;
1560 flags
.tmp_flags
= MSG_IMAP
;
1562 while ((p
= strchr(p
, '\\')) != NULL
) {
1565 if (g_strncasecmp(p
, "Recent", 6) == 0) {
1566 MSG_SET_PERM_FLAGS(flags
, MSG_NEW
|MSG_UNREAD
);
1567 } else if (g_strncasecmp(p
, "Seen", 4) == 0) {
1568 MSG_UNSET_PERM_FLAGS(flags
, MSG_NEW
|MSG_UNREAD
);
1569 } else if (g_strncasecmp(p
, "Deleted", 7) == 0) {
1570 MSG_SET_PERM_FLAGS(flags
, MSG_DELETED
);
1571 } else if (g_strncasecmp(p
, "Flagged", 7) == 0) {
1572 MSG_SET_PERM_FLAGS(flags
, MSG_MARKED
);
1579 static MsgInfo
*imap_parse_envelope(SockInfo
*sock
, GString
*line_str
)
1582 gchar buf
[IMAPBUFSIZE
];
1583 gchar tmp
[IMAPBUFSIZE
];
1590 gchar
*subject
= NULL
;
1592 gchar
*tmp_fromname
;
1594 gchar
*fromname
= NULL
;
1597 gchar
*inreplyto
= NULL
;
1598 gchar
*msgid
= NULL
;
1599 MsgFlags flags
= {0, 0};
1601 g_return_val_if_fail(line_str
!= NULL
, NULL
);
1602 g_return_val_if_fail(line_str
->str
[0] == '*' &&
1603 line_str
->str
[1] == ' ', NULL
);
1605 cur_pos
= line_str
->str
+ 2;
1607 #define PARSE_ONE_ELEMENT(ch) \
1609 cur_pos = strchr_cpy(cur_pos, ch, buf, sizeof(buf)); \
1610 g_return_val_if_fail(cur_pos != NULL, NULL); \
1613 PARSE_ONE_ELEMENT(' ');
1616 PARSE_ONE_ELEMENT(' ');
1617 g_return_val_if_fail(!strcmp(buf
, "FETCH"), NULL
);
1619 g_return_val_if_fail(*cur_pos
== '(', NULL
);
1622 while (*cur_pos
!= '\0' && *cur_pos
!= ')') {
1623 while (*cur_pos
== ' ') cur_pos
++;
1625 if (!strncmp(cur_pos
, "UID ", 4)) {
1627 uid
= strtoul(cur_pos
, &cur_pos
, 10);
1628 } else if (!strncmp(cur_pos
, "FLAGS ", 6)) {
1630 g_return_val_if_fail(*cur_pos
== '(', NULL
);
1632 PARSE_ONE_ELEMENT(')');
1633 flags
= imap_parse_flags(buf
);
1634 } else if (!strncmp(cur_pos
, "RFC822.SIZE ", 12)) {
1636 size
= strtol(cur_pos
, &cur_pos
, 10);
1637 } else if (!strncmp(cur_pos
, "ENVELOPE ", 9)) {
1639 g_return_val_if_fail(*cur_pos
== '(', NULL
);
1640 cur_pos
= imap_parse_atom
1641 (sock
, cur_pos
+ 1, buf
, sizeof(buf
), line_str
);
1642 Xstrdup_a(date
, buf
, return NULL
);
1643 date_t
= procheader_date_parse(NULL
, date
, 0);
1645 cur_pos
= imap_parse_atom
1646 (sock
, cur_pos
, buf
, sizeof(buf
), line_str
);
1647 if (buf
[0] != '\0') {
1648 conv_unmime_header(tmp
, sizeof(tmp
), buf
, NULL
);
1649 Xstrdup_a(subject
, tmp
, return NULL
);
1652 g_return_val_if_fail(*cur_pos
== ' ', NULL
);
1653 cur_pos
= imap_parse_address(sock
, cur_pos
+ 1,
1654 &tmp_from
, &tmp_fromname
,
1656 Xstrdup_a(from
, tmp_from
,
1657 {g_free(tmp_from
); g_free(tmp_fromname
);
1659 Xstrdup_a(fromname
, tmp_fromname
,
1660 {g_free(tmp_from
); g_free(tmp_fromname
);
1663 g_free(tmp_fromname
);
1665 #define SKIP_ONE_ELEMENT() \
1667 g_return_val_if_fail(*cur_pos == ' ', NULL); \
1668 cur_pos = imap_parse_address(sock, cur_pos + 1, NULL, NULL, \
1672 /* skip sender and reply-to */
1676 g_return_val_if_fail(*cur_pos
== ' ', NULL
);
1677 cur_pos
= imap_parse_address(sock
, cur_pos
+ 1,
1678 &tmp_to
, NULL
, line_str
);
1679 Xstrdup_a(to
, tmp_to
, {g_free(tmp_to
); return NULL
;});
1682 /* skip Cc and Bcc */
1686 #undef SKIP_ONE_ELEMENT
1688 g_return_val_if_fail(*cur_pos
== ' ', NULL
);
1689 cur_pos
= imap_parse_atom
1690 (sock
, cur_pos
, buf
, sizeof(buf
), line_str
);
1691 if (buf
[0] != '\0') {
1692 eliminate_parenthesis(buf
, '(', ')');
1693 extract_parenthesis(buf
, '<', '>');
1695 Xstrdup_a(inreplyto
, buf
, return NULL
);
1698 g_return_val_if_fail(*cur_pos
== ' ', NULL
);
1699 cur_pos
= imap_parse_atom
1700 (sock
, cur_pos
, buf
, sizeof(buf
), line_str
);
1701 if (buf
[0] != '\0') {
1702 extract_parenthesis(buf
, '<', '>');
1704 Xstrdup_a(msgid
, buf
, return NULL
);
1707 g_return_val_if_fail(*cur_pos
== ')', NULL
);
1710 g_warning("invalid FETCH response: %s\n", cur_pos
);
1715 msginfo
= g_new0(MsgInfo
, 1);
1716 msginfo
->msgnum
= uid
;
1717 msginfo
->size
= size
;
1718 msginfo
->date
= g_strdup(date
);
1719 msginfo
->date_t
= date_t
;
1720 msginfo
->subject
= g_strdup(subject
);
1721 msginfo
->from
= g_strdup(from
);
1722 msginfo
->fromname
= g_strdup(fromname
);
1723 msginfo
->to
= g_strdup(to
);
1724 msginfo
->inreplyto
= g_strdup(inreplyto
);
1725 msginfo
->msgid
= g_strdup(msgid
);
1726 msginfo
->flags
= flags
;
1731 static gint
imap_set_message_flags(IMAPSession
*session
,
1740 buf
= g_string_new(is_set
? "+FLAGS (" : "-FLAGS (");
1742 if (IMAP_IS_SEEN(flags
)) g_string_append(buf
, "\\Seen ");
1743 if (IMAP_IS_ANSWERED(flags
)) g_string_append(buf
, "\\Answered ");
1744 if (IMAP_IS_FLAGGED(flags
)) g_string_append(buf
, "\\Flagged ");
1745 if (IMAP_IS_DELETED(flags
)) g_string_append(buf
, "\\Deleted ");
1746 if (IMAP_IS_DRAFT(flags
)) g_string_append(buf
, "\\Draft");
1748 if (buf
->str
[buf
->len
- 1] == ' ')
1749 g_string_truncate(buf
, buf
->len
- 1);
1751 g_string_append_c(buf
, ')');
1753 ok
= imap_cmd_store(SESSION(session
)->sock
, first_uid
, last_uid
,
1755 g_string_free(buf
, TRUE
);
1760 static gint
imap_select(IMAPSession
*session
, IMAPFolder
*folder
,
1762 gint
*exists
, gint
*recent
, gint
*unseen
,
1763 guint32
*uid_validity
)
1768 real_path
= imap_get_real_path(folder
, path
);
1769 ok
= imap_cmd_select(SESSION(session
)->sock
, real_path
,
1770 exists
, recent
, unseen
, uid_validity
);
1771 if (ok
!= IMAP_SUCCESS
)
1772 log_warning(_("can't select folder: %s\n"), real_path
);
1778 #define THROW(err) { ok = err; goto catch; }
1780 static gint
imap_get_uid(IMAPSession
*session
, gint msgnum
, guint32
*uid
)
1788 argbuf
= g_ptr_array_new();
1790 imap_cmd_gen_send(SESSION(session
)->sock
, "FETCH %d (UID)", msgnum
);
1791 if ((ok
= imap_cmd_ok(SESSION(session
)->sock
, argbuf
)) != IMAP_SUCCESS
)
1794 str
= search_array_contain_str(argbuf
, "FETCH");
1795 if (!str
) THROW(IMAP_ERROR
);
1797 if (sscanf(str
, "%d FETCH (UID %d)", &num
, uid
) != 2 ||
1799 g_warning("imap_get_uid(): invalid FETCH line.\n");
1804 ptr_array_free_strings(argbuf
);
1805 g_ptr_array_free(argbuf
, TRUE
);
1810 static gint
imap_status(IMAPSession
*session
, IMAPFolder
*folder
,
1812 gint
*messages
, gint
*recent
, gint
*unseen
,
1813 guint32
*uid_validity
)
1820 *messages
= *recent
= *unseen
= *uid_validity
= 0;
1822 argbuf
= g_ptr_array_new();
1824 real_path
= imap_get_real_path(folder
, path
);
1825 if (strchr(real_path
, ' ') != NULL
)
1826 imap_cmd_gen_send(SESSION(session
)->sock
, "STATUS \"%s\" "
1827 "(MESSAGES RECENT UNSEEN UIDVALIDITY)",
1830 imap_cmd_gen_send(SESSION(session
)->sock
, "STATUS %s "
1831 "(MESSAGES RECENT UNSEEN UIDVALIDITY)",
1834 ok
= imap_cmd_ok(SESSION(session
)->sock
, argbuf
);
1835 if (ok
!= IMAP_SUCCESS
) THROW(ok
);
1837 str
= search_array_contain_str(argbuf
, "STATUS");
1838 if (!str
) THROW(IMAP_ERROR
);
1840 str
= strchr(str
, '(');
1841 if (!str
) THROW(IMAP_ERROR
);
1843 while (*str
!= '\0' && *str
!= ')') {
1844 while (*str
== ' ') str
++;
1846 if (!strncmp(str
, "MESSAGES ", 9)) {
1848 *messages
= strtol(str
, &str
, 10);
1849 } else if (!strncmp(str
, "RECENT ", 7)) {
1851 *recent
= strtol(str
, &str
, 10);
1852 } else if (!strncmp(str
, "UNSEEN ", 7)) {
1854 *unseen
= strtol(str
, &str
, 10);
1855 } else if (!strncmp(str
, "UIDVALIDITY ", 12)) {
1857 *uid_validity
= strtoul(str
, &str
, 10);
1859 g_warning("invalid STATUS response: %s\n", str
);
1866 ptr_array_free_strings(argbuf
);
1867 g_ptr_array_free(argbuf
, TRUE
);
1875 /* low-level IMAP4rev1 commands */
1877 static gint
imap_cmd_login(SockInfo
*sock
,
1878 const gchar
*user
, const gchar
*pass
)
1882 if (strchr(user
, ' ') != NULL
)
1883 imap_cmd_gen_send(sock
, "LOGIN \"%s\" %s", user
, pass
);
1885 imap_cmd_gen_send(sock
, "LOGIN %s %s", user
, pass
);
1887 ok
= imap_cmd_ok(sock
, NULL
);
1888 if (ok
!= IMAP_SUCCESS
)
1889 log_warning(_("IMAP4 login failed.\n"));
1894 static gint
imap_cmd_logout(SockInfo
*sock
)
1896 imap_cmd_gen_send(sock
, "LOGOUT");
1897 return imap_cmd_ok(sock
, NULL
);
1900 static gint
imap_cmd_noop(SockInfo
*sock
)
1902 imap_cmd_gen_send(sock
, "NOOP");
1903 return imap_cmd_ok(sock
, NULL
);
1906 #define THROW(err) { ok = err; goto catch; }
1908 static gint
imap_cmd_namespace(SockInfo
*sock
, gchar
**ns_str
)
1914 argbuf
= g_ptr_array_new();
1916 imap_cmd_gen_send(sock
, "NAMESPACE");
1917 if ((ok
= imap_cmd_ok(sock
, argbuf
)) != IMAP_SUCCESS
) THROW(ok
);
1919 str
= search_array_contain_str(argbuf
, "NAMESPACE");
1920 if (!str
) THROW(IMAP_ERROR
);
1922 *ns_str
= g_strdup(str
);
1925 ptr_array_free_strings(argbuf
);
1926 g_ptr_array_free(argbuf
, TRUE
);
1933 static gint
imap_cmd_list(SockInfo
*sock
, const gchar
*ref
,
1934 const gchar
*mailbox
, GPtrArray
*argbuf
)
1939 if (!ref
) ref
= "\"\"";
1940 if (!mailbox
) mailbox
= "\"\"";
1942 if (*ref
!= '"' && strchr(ref
, ' ') != NULL
)
1943 new_ref
= g_strdup_printf("\"%s\"", ref
);
1945 new_ref
= g_strdup(ref
);
1946 if (*mailbox
!= '"' && strchr(mailbox
, ' ') != NULL
)
1947 new_mailbox
= g_strdup_printf("\"%s\"", mailbox
);
1949 new_mailbox
= g_strdup(mailbox
);
1951 imap_cmd_gen_send(sock
, "LIST %s %s", new_ref
, new_mailbox
);
1954 g_free(new_mailbox
);
1956 return imap_cmd_ok(sock
, argbuf
);
1959 #define THROW goto catch
1961 static gint
imap_cmd_do_select(SockInfo
*sock
, const gchar
*folder
,
1963 gint
*exists
, gint
*recent
, gint
*unseen
,
1964 guint32
*uid_validity
)
1971 *exists
= *recent
= *unseen
= *uid_validity
= 0;
1972 argbuf
= g_ptr_array_new();
1975 select_cmd
= "EXAMINE";
1977 select_cmd
= "SELECT";
1979 if (strchr(folder
, ' ') != NULL
)
1980 imap_cmd_gen_send(sock
, "%s \"%s\"", select_cmd
, folder
);
1982 imap_cmd_gen_send(sock
, "%s %s", select_cmd
, folder
);
1984 if ((ok
= imap_cmd_ok(sock
, argbuf
)) != IMAP_SUCCESS
) THROW
;
1986 resp_str
= search_array_contain_str(argbuf
, "EXISTS");
1988 if (sscanf(resp_str
,"%d EXISTS", exists
) != 1) {
1989 g_warning("imap_cmd_select(): invalid EXISTS line.\n");
1994 resp_str
= search_array_contain_str(argbuf
, "RECENT");
1996 if (sscanf(resp_str
, "%d RECENT", recent
) != 1) {
1997 g_warning("imap_cmd_select(): invalid RECENT line.\n");
2002 resp_str
= search_array_contain_str(argbuf
, "UIDVALIDITY");
2004 if (sscanf(resp_str
, "OK [UIDVALIDITY %u] ", uid_validity
)
2006 g_warning("imap_cmd_select(): invalid UIDVALIDITY line.\n");
2011 resp_str
= search_array_contain_str(argbuf
, "UNSEEN");
2013 if (sscanf(resp_str
, "OK [UNSEEN %d] ", unseen
) != 1) {
2014 g_warning("imap_cmd_select(): invalid UNSEEN line.\n");
2020 ptr_array_free_strings(argbuf
);
2021 g_ptr_array_free(argbuf
, TRUE
);
2026 static gint
imap_cmd_select(SockInfo
*sock
, const gchar
*folder
,
2027 gint
*exists
, gint
*recent
, gint
*unseen
,
2028 guint32
*uid_validity
)
2030 return imap_cmd_do_select(sock
, folder
, FALSE
,
2031 exists
, recent
, unseen
, uid_validity
);
2034 static gint
imap_cmd_examine(SockInfo
*sock
, const gchar
*folder
,
2035 gint
*exists
, gint
*recent
, gint
*unseen
,
2036 guint32
*uid_validity
)
2038 return imap_cmd_do_select(sock
, folder
, TRUE
,
2039 exists
, recent
, unseen
, uid_validity
);
2044 static gint
imap_cmd_create(SockInfo
*sock
, const gchar
*folder
)
2046 if (strchr(folder
, ' ') != NULL
)
2047 imap_cmd_gen_send(sock
, "CREATE \"%s\"", folder
);
2049 imap_cmd_gen_send(sock
, "CREATE %s", folder
);
2051 return imap_cmd_ok(sock
, NULL
);
2054 static gint
imap_cmd_delete(SockInfo
*sock
, const gchar
*folder
)
2056 if (strchr(folder
, ' ') != NULL
)
2057 imap_cmd_gen_send(sock
, "DELETE \"%s\"", folder
);
2059 imap_cmd_gen_send(sock
, "DELETE %s", folder
);
2061 return imap_cmd_ok(sock
, NULL
);
2064 static gint
imap_cmd_fetch(SockInfo
*sock
, guint32 uid
, const gchar
*filename
)
2067 gchar buf
[IMAPBUFSIZE
];
2072 g_return_val_if_fail(filename
!= NULL
, IMAP_ERROR
);
2074 imap_cmd_gen_send(sock
, "UID FETCH %d BODY[]", uid
);
2076 if (sock_gets(sock
, buf
, sizeof(buf
)) < 0)
2079 if (buf
[0] != '*' || buf
[1] != ' ')
2081 log_print("IMAP4< %s\n", buf
);
2083 cur_pos
= strchr(buf
, '{');
2084 g_return_val_if_fail(cur_pos
!= NULL
, IMAP_ERROR
);
2085 cur_pos
= strchr_cpy(cur_pos
+ 1, '}', size_str
, sizeof(size_str
));
2086 g_return_val_if_fail(cur_pos
!= NULL
, IMAP_ERROR
);
2087 size_num
= atol(size_str
);
2089 if (*cur_pos
!= '\0') return IMAP_ERROR
;
2091 if (recv_bytes_write_to_file(sock
, size_num
, filename
) != 0)
2094 if (imap_cmd_gen_recv(sock
, buf
, sizeof(buf
)) != IMAP_SUCCESS
)
2097 if (buf
[0] == '\0' || buf
[strlen(buf
) - 1] != ')')
2100 ok
= imap_cmd_ok(sock
, NULL
);
2105 static gint
imap_cmd_append(SockInfo
*sock
, const gchar
*destfolder
,
2111 g_return_val_if_fail(file
!= NULL
, IMAP_ERROR
);
2113 size
= get_file_size(file
);
2114 imap_cmd_gen_send(sock
, "APPEND %s {%d}", destfolder
, size
);
2115 ok
= imap_cmd_ok(sock
, NULL
);
2116 if (ok
!= IMAP_SUCCESS
) {
2117 log_warning(_("can't append %s to %s\n"), file
, destfolder
);
2124 static gint
imap_cmd_copy(SockInfo
*sock
, guint32 uid
, const gchar
*destfolder
)
2128 g_return_val_if_fail(destfolder
!= NULL
, IMAP_ERROR
);
2130 if (strchr(destfolder
, ' ') != NULL
)
2131 imap_cmd_gen_send(sock
, "UID COPY %d \"%s\"", uid
, destfolder
);
2133 imap_cmd_gen_send(sock
, "UID COPY %d %s", uid
, destfolder
);
2135 ok
= imap_cmd_ok(sock
, NULL
);
2136 if (ok
!= IMAP_SUCCESS
) {
2137 log_warning(_("can't copy %d to %s\n"), uid
, destfolder
);
2144 gint
imap_cmd_envelope(SockInfo
*sock
, guint32 first_uid
, guint32 last_uid
)
2147 (sock
, "UID FETCH %d:%d (UID FLAGS RFC822.SIZE ENVELOPE)",
2148 first_uid
, last_uid
);
2150 return IMAP_SUCCESS
;
2153 static gint
imap_cmd_store(SockInfo
*sock
, guint32 first_uid
, guint32 last_uid
,
2158 imap_cmd_gen_send(sock
, "UID STORE %d:%d %s",
2159 first_uid
, last_uid
, sub_cmd
);
2161 if ((ok
= imap_cmd_ok(sock
, NULL
)) != IMAP_SUCCESS
) {
2162 log_warning(_("error while imap command: STORE %d:%d %s\n"),
2163 first_uid
, last_uid
, sub_cmd
);
2167 return IMAP_SUCCESS
;
2170 static gint
imap_cmd_expunge(SockInfo
*sock
)
2174 imap_cmd_gen_send(sock
, "EXPUNGE");
2175 if ((ok
= imap_cmd_ok(sock
, NULL
)) != IMAP_SUCCESS
) {
2176 log_warning(_("error while imap command: EXPUNGE\n"));
2180 return IMAP_SUCCESS
;
2183 static gint
imap_cmd_ok(SockInfo
*sock
, GPtrArray
*argbuf
)
2186 gchar buf
[IMAPBUFSIZE
];
2188 gchar cmd_status
[IMAPBUFSIZE
];
2190 while ((ok
= imap_cmd_gen_recv(sock
, buf
, sizeof(buf
)))
2192 if (buf
[0] == '*' && buf
[1] == ' ') {
2194 g_ptr_array_add(argbuf
, g_strdup(buf
+ 2));
2198 if (sscanf(buf
, "%d %s", &cmd_num
, cmd_status
) < 2)
2200 else if (cmd_num
== imap_cmd_count
&&
2201 !strcmp(cmd_status
, "OK")) {
2203 g_ptr_array_add(argbuf
, g_strdup(buf
));
2204 return IMAP_SUCCESS
;
2212 static void imap_cmd_gen_send(SockInfo
*sock
, const gchar
*format
, ...)
2214 gchar buf
[IMAPBUFSIZE
];
2215 gchar tmp
[IMAPBUFSIZE
];
2219 va_start(args
, format
);
2220 g_vsnprintf(tmp
, sizeof(tmp
), format
, args
);
2225 g_snprintf(buf
, sizeof(buf
), "%d %s\r\n", imap_cmd_count
, tmp
);
2226 if (!strncasecmp(tmp
, "LOGIN ", 6) && (p
= strchr(tmp
+ 6, ' '))) {
2228 log_print("IMAP4> %d %s ********\n", imap_cmd_count
, tmp
);
2230 log_print("IMAP4> %d %s\n", imap_cmd_count
, tmp
);
2232 sock_write(sock
, buf
, strlen(buf
));
2235 static gint
imap_cmd_gen_recv(SockInfo
*sock
, gchar
*buf
, gint size
)
2237 if (sock_gets(sock
, buf
, size
) == -1)
2242 log_print("IMAP4< %s\n", buf
);
2244 return IMAP_SUCCESS
;
2248 /* misc utility functions */
2250 static gchar
*strchr_cpy(const gchar
*src
, gchar ch
, gchar
*dest
, gint len
)
2255 tmp
= strchr(src
, ch
);
2259 memcpy(dest
, src
, MIN(tmp
- src
, len
- 1));
2260 dest
[MIN(tmp
- src
, len
- 1)] = '\0';
2265 static gchar
*get_quoted(const gchar
*src
, gchar ch
, gchar
*dest
, gint len
)
2267 const gchar
*p
= src
;
2270 g_return_val_if_fail(*p
== ch
, NULL
);
2275 while (*p
!= '\0' && *p
!= ch
) {
2277 if (*p
== '\\' && *(p
+ 1) != '\0')
2286 return (gchar
*)(*p
== ch
? p
+ 1 : p
);
2289 static gchar
*search_array_contain_str(GPtrArray
*array
, gchar
*str
)
2293 for (i
= 0; i
< array
->len
; i
++) {
2296 tmp
= g_ptr_array_index(array
, i
);
2297 if (strstr(tmp
, str
) != NULL
)
2304 static void imap_path_separator_subst(gchar
*str
, gchar separator
)
2306 if (separator
&& separator
!= '/')
2307 subst_char(str
, '/', separator
);