Commit fa42589c fa42589cef090379bda4ea4e0838474520f9f4f6 by Sergey Poznyakoff

imap client: implement folder API.

* libmailutils/list/listlist.c (mu_list_append_list): Do nothing if the
source list is empty.

* include/mailutils/sys/imap.h (_mu_imap_url_init)
(_mu_imaps_url_init): New protos.
* libproto/imap/Makefile.am (libmu_imap_la_SOURCES): Restore url.c
* libproto/imap/mbox.c: Deleted
* libproto/imap/url.c: Rewrite.
* libproto/imap/folder.c: Rewrite from scratch.
* configure.ac: Build libproto/imap/tests/Makefile
* include/mailutils/imap.h (mu_imap_session_state)
(mu_imap_iserror, mu_imap_clearerr)
(mu_imap_login_secret): New protos.
* include/mailutils/sys/imap.h (_mu_imap_list_element_is_nil): New proto.

* libmailutils/mailbox/folder.c (mu_folder_list): Pass
MU_FOLDER_ATTRIBUTE_ALL.
* libproto/imap/fake-folder.c: Remove.
* libproto/imap/Makefile.am (libmu_imap_la_SOURCES): Remove fake-folder.c
Add url.c and folder.c
* libproto/imap/create.c (mu_imap_session_state)
(mu_imap_iserror, mu_imap_clearerr): New functions.
* libproto/imap/delete.c: Check input parameters.

* libproto/imap/fetch.c: Use _mu_imap_list_element_is_nil to check for
empty lists.
* libproto/imap/genlist.c: Likewise.
* libproto/imap/rename.c: Likewise.
* libproto/imap/subscribe.c: Likewise.
* libproto/imap/unsubscribe.c: Likewise.

* libproto/imap/resplist.c: Treat NIL and () equally.
* libproto/imap/login.c (mu_imap_login_secret): New function.

* mu/imap.c: Fix a typo.
1 parent 0e8ae1c3
...@@ -1460,6 +1460,7 @@ AC_CONFIG_FILES([ ...@@ -1460,6 +1460,7 @@ AC_CONFIG_FILES([
1460 libproto/pop/Makefile 1460 libproto/pop/Makefile
1461 libproto/nntp/Makefile 1461 libproto/nntp/Makefile
1462 libproto/imap/Makefile 1462 libproto/imap/Makefile
1463 libproto/imap/tests/Makefile
1463 libmu_compat/Makefile 1464 libmu_compat/Makefile
1464 maidag/Makefile 1465 maidag/Makefile
1465 mail/Makefile 1466 mail/Makefile
......
...@@ -47,6 +47,10 @@ void mu_imap_destroy (mu_imap_t *pimap); ...@@ -47,6 +47,10 @@ void mu_imap_destroy (mu_imap_t *pimap);
47 int mu_imap_connect (mu_imap_t imap); 47 int mu_imap_connect (mu_imap_t imap);
48 int mu_imap_disconnect (mu_imap_t imap); 48 int mu_imap_disconnect (mu_imap_t imap);
49 49
50 int mu_imap_session_state (mu_imap_t imap);
51 int mu_imap_iserror (mu_imap_t imap);
52 void mu_imap_clearerr (mu_imap_t imap);
53
50 int mu_imap_capability (mu_imap_t imap, int reread, mu_iterator_t *piter); 54 int mu_imap_capability (mu_imap_t imap, int reread, mu_iterator_t *piter);
51 int mu_imap_capability_test (mu_imap_t imap, const char *name, 55 int mu_imap_capability_test (mu_imap_t imap, const char *name,
52 const char **pret); 56 const char **pret);
...@@ -54,6 +58,8 @@ int mu_imap_capability_test (mu_imap_t imap, const char *name, ...@@ -54,6 +58,8 @@ int mu_imap_capability_test (mu_imap_t imap, const char *name,
54 int mu_imap_starttls (mu_imap_t imap); 58 int mu_imap_starttls (mu_imap_t imap);
55 59
56 int mu_imap_login (mu_imap_t imap, const char *user, const char *pass); 60 int mu_imap_login (mu_imap_t imap, const char *user, const char *pass);
61 int mu_imap_login_secret (mu_imap_t imap, const char *user,
62 mu_secret_t secret);
57 int mu_imap_logout (mu_imap_t imap); 63 int mu_imap_logout (mu_imap_t imap);
58 64
59 int mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t *passoc); 65 int mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t *passoc);
......
...@@ -215,6 +215,8 @@ int _mu_imap_response (mu_imap_t imap, mu_imap_response_action_t fun, ...@@ -215,6 +215,8 @@ int _mu_imap_response (mu_imap_t imap, mu_imap_response_action_t fun,
215 215
216 int _mu_imap_list_element_is_string (struct imap_list_element *elt, 216 int _mu_imap_list_element_is_string (struct imap_list_element *elt,
217 const char *str); 217 const char *str);
218 int _mu_imap_list_element_is_nil (struct imap_list_element *elt);
219
218 int _mu_imap_list_nth_element_is_string (mu_list_t list, size_t n, 220 int _mu_imap_list_nth_element_is_string (mu_list_t list, size_t n,
219 const char *str); 221 const char *str);
220 222
...@@ -226,6 +228,17 @@ int _mu_imap_parse_fetch_response (mu_list_t resp, mu_list_t *result_list); ...@@ -226,6 +228,17 @@ int _mu_imap_parse_fetch_response (mu_list_t resp, mu_list_t *result_list);
226 228
227 void _mu_close_handler (mu_imap_t imap); 229 void _mu_close_handler (mu_imap_t imap);
228 230
231 /* ----------------------------- */
232 /* URL Auxiliaries */
233 /* ----------------------------- */
234 int _mu_imap_url_init (mu_url_t url);
235 int _mu_imaps_url_init (mu_url_t url);
236
237 /* ----------------------------- */
238 /* Folder interface */
239 /* ----------------------------- */
240
241
229 # ifdef __cplusplus 242 # ifdef __cplusplus
230 } 243 }
231 # endif 244 # endif
......
...@@ -99,7 +99,9 @@ mu_list_insert_list (mu_list_t list, void *item, mu_list_t new_list, ...@@ -99,7 +99,9 @@ mu_list_insert_list (mu_list_t list, void *item, mu_list_t new_list,
99 void 99 void
100 mu_list_append_list (mu_list_t list, mu_list_t new_list) 100 mu_list_append_list (mu_list_t list, mu_list_t new_list)
101 { 101 {
102 if (list->count == 0) 102 if (new_list->count == 0)
103 return;
104 else if (list->count == 0)
103 { 105 {
104 list->head = new_list->head; 106 list->head = new_list->head;
105 list->head.next->prev = list->head.prev->next = &list->head; 107 list->head.next->prev = list->head.prev->next = &list->head;
......
...@@ -368,7 +368,8 @@ mu_folder_list (mu_folder_t folder, const char *dirname, void *pattern, ...@@ -368,7 +368,8 @@ mu_folder_list (mu_folder_t folder, const char *dirname, void *pattern,
368 size_t max_level, 368 size_t max_level,
369 mu_list_t *pflist) 369 mu_list_t *pflist)
370 { 370 {
371 return mu_folder_enumerate (folder, dirname, pattern, 0, max_level, 371 return mu_folder_enumerate (folder, dirname, pattern,
372 MU_FOLDER_ATTRIBUTE_ALL, max_level,
372 pflist, NULL, NULL); 373 pflist, NULL, NULL);
373 } 374 }
374 375
......
...@@ -21,16 +21,14 @@ lib_LTLIBRARIES = libmu_imap.la ...@@ -21,16 +21,14 @@ lib_LTLIBRARIES = libmu_imap.la
21 libmu_imap_la_LDFLAGS=-version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@ 21 libmu_imap_la_LDFLAGS=-version-info @VI_CURRENT@:@VI_REVISION@:@VI_AGE@
22 libmu_imap_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@ 22 libmu_imap_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@
23 23
24 # FIXME: Put these back when ready 24 SUBDIRS = . tests
25 25
26 # folder.c\ 26 # FIXME: Put this back when ready
27 # mbox.c\ 27 # mbox.c
28 # url.c
29 libmu_imap_la_SOURCES = \ 28 libmu_imap_la_SOURCES = \
30 appmsg.c\ 29 appmsg.c\
31 appstr.c\ 30 appstr.c\
32 appstrsiz.c\ 31 appstrsiz.c\
33 fake-folder.c\
34 fetch.c\ 32 fetch.c\
35 gencom.c\ 33 gencom.c\
36 genlist.c\ 34 genlist.c\
...@@ -68,5 +66,6 @@ libmu_imap_la_SOURCES = \ ...@@ -68,5 +66,6 @@ libmu_imap_la_SOURCES = \
68 tag.c\ 66 tag.c\
69 trace.c\ 67 trace.c\
70 unselect.c\ 68 unselect.c\
71 unsubscribe.c 69 unsubscribe.c\
72 70 folder.c\
71 url.c
......
...@@ -64,3 +64,30 @@ _mu_imap_init (mu_imap_t imap) ...@@ -64,3 +64,30 @@ _mu_imap_init (mu_imap_t imap)
64 imap->session_state = MU_IMAP_SESSION_INIT; 64 imap->session_state = MU_IMAP_SESSION_INIT;
65 return 0; 65 return 0;
66 } 66 }
67
68 int
69 mu_imap_session_state (mu_imap_t imap)
70 {
71 if (!imap)
72 return -1;
73 return imap->session_state;
74 }
75
76 int
77 mu_imap_iserror (mu_imap_t imap)
78 {
79 if (!imap)
80 return -1;
81 return imap->client_state == MU_IMAP_CLIENT_ERROR;
82 }
83
84 void
85 mu_imap_clearerr (mu_imap_t imap)
86 {
87 if (imap)
88 {
89 imap->client_state = MU_IMAP_CLIENT_READY;
90 if (imap->io)
91 mu_imapio_clearerr (imap->io);
92 }
93 }
......
...@@ -31,6 +31,9 @@ mu_imap_delete (mu_imap_t imap, const char *mailbox) ...@@ -31,6 +31,9 @@ mu_imap_delete (mu_imap_t imap, const char *mailbox)
31 char const *argv[2]; 31 char const *argv[2];
32 static struct imap_command com; 32 static struct imap_command com;
33 33
34 if (!mailbox)
35 return EINVAL;
36
34 argv[0] = "DELETE"; 37 argv[0] = "DELETE";
35 argv[1] = mailbox; 38 argv[1] = mailbox;
36 39
......
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 #include <mailutils/mailutils.h>
5
6 mu_record_t mu_imap_record = NULL;
7 mu_record_t mu_imaps_record = NULL;
...@@ -355,10 +355,10 @@ elt_to_string (struct imap_list_element *elt, char **pstr) ...@@ -355,10 +355,10 @@ elt_to_string (struct imap_list_element *elt, char **pstr)
355 { 355 {
356 char *p; 356 char *p;
357 357
358 if (elt->type != imap_eltype_string) 358 if (_mu_imap_list_element_is_nil (elt))
359 return EINVAL;
360 if (mu_c_strcasecmp (elt->v.string, "NIL") == 0)
361 p = NULL; 359 p = NULL;
360 else if (elt->type != imap_eltype_string)
361 return EINVAL;
362 else 362 else
363 { 363 {
364 p = strdup (elt->v.string); 364 p = strdup (elt->v.string);
...@@ -386,13 +386,13 @@ _fill_subaddr (void *item, void *data) ...@@ -386,13 +386,13 @@ _fill_subaddr (void *item, void *data)
386 return 0; 386 return 0;
387 387
388 arg = _mu_imap_list_at (elt->v.list, 0); 388 arg = _mu_imap_list_at (elt->v.list, 0);
389 if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL")) 389 if (arg && arg->type == imap_eltype_string)
390 personal = arg->v.string; 390 personal = arg->v.string;
391 arg = _mu_imap_list_at (elt->v.list, 2); 391 arg = _mu_imap_list_at (elt->v.list, 2);
392 if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL")) 392 if (arg && arg->type == imap_eltype_string)
393 local = arg->v.string; 393 local = arg->v.string;
394 arg = _mu_imap_list_at (elt->v.list, 3); 394 arg = _mu_imap_list_at (elt->v.list, 3);
395 if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL")) 395 if (arg && arg->type == imap_eltype_string)
396 domain = arg->v.string; 396 domain = arg->v.string;
397 397
398 if (domain && local) 398 if (domain && local)
...@@ -414,13 +414,10 @@ _fill_subaddr (void *item, void *data) ...@@ -414,13 +414,10 @@ _fill_subaddr (void *item, void *data)
414 static int 414 static int
415 elt_to_address (struct imap_list_element *elt, mu_address_t *paddr) 415 elt_to_address (struct imap_list_element *elt, mu_address_t *paddr)
416 { 416 {
417 if (elt->type != imap_eltype_list) 417 if (_mu_imap_list_element_is_nil (elt))
418 {
419 if (mu_c_strcasecmp (elt->v.string, "NIL") == 0)
420 *paddr = NULL; 418 *paddr = NULL;
421 else 419 else if (elt->type != imap_eltype_list)
422 return EINVAL; 420 return EINVAL;
423 }
424 else 421 else
425 { 422 {
426 struct addr_env addr_env; 423 struct addr_env addr_env;
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail 1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2009, 2 Copyright (C) 2011 Free Software Foundation, Inc.
3 2010, 2011 Free Software Foundation, Inc.
4 3
5 This library is free software; you can redistribute it and/or 4 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public 5 modify it under the terms of the GNU Lesser General Public
...@@ -20,352 +19,395 @@ ...@@ -20,352 +19,395 @@
20 # include <config.h> 19 # include <config.h>
21 #endif 20 #endif
22 21
23 #ifdef ENABLE_IMAP
24
25 #include <stdlib.h> 22 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <errno.h>
28 #include <string.h> 23 #include <string.h>
29 #include <assert.h> 24 #include <netinet/in.h>
30 #include <fnmatch.h>
31
32 #ifdef HAVE_STRINGS_H
33 # include <strings.h>
34 #endif
35
36 #include <mailutils/sys/imap.h>
37 #include <mailutils/sys/url.h>
38 25
39 #include <mailutils/auth.h> 26 #include <mailutils/imap.h>
40 #include <mailutils/attribute.h> 27 #include <mailutils/diag.h>
41 #include <mailutils/debug.h> 28 #include <mailutils/debug.h>
42 #include <mailutils/error.h> 29 #include <mailutils/error.h>
43 #include <mailutils/errno.h> 30 #include <mailutils/errno.h>
44 #include <mailutils/header.h>
45 #include <mailutils/observer.h>
46 #include <mailutils/stream.h>
47 #include <mailutils/iterator.h>
48 #include <mailutils/argcv.h>
49 #include <mailutils/tls.h>
50 #include <mailutils/nls.h> 31 #include <mailutils/nls.h>
32 #include <mailutils/tls.h>
33 #include <mailutils/url.h>
34 #include <mailutils/list.h>
51 #include <mailutils/secret.h> 35 #include <mailutils/secret.h>
52 #include <mailutils/util.h>
53 #include <mailutils/cctype.h>
54 #include <mailutils/cstr.h> 36 #include <mailutils/cstr.h>
37 #include <mailutils/sockaddr.h>
38 #include <mailutils/wordsplit.h>
39 #include <mailutils/sys/imap.h>
40 #include <mailutils/sys/folder.h>
55 41
56 /* For dbg purposes set to one to see different level of traffic. */ 42 /* Placeholders. */
57 /* Print to stderr the command sent to the IMAP server. */ 43 #define _mu_imap_mailbox_init NULL
58 #define DEBUG_SHOW_COMMAND 0 44 #define _mu_imaps_mailbox_init NULL
59 /* Print to stderr the responses received from the IMAP server. */
60 #define DEBUG_SHOW_RESPONSE 0
61 /* Print to stderr the literal/quoted string received from the IMAP server. */
62 #define DEBUG_SHOW_DATA 0
63 45
64 /* Variable use for the registrar. */
65 static struct _mu_record _imap_record =
66 {
67 MU_IMAP_PRIO,
68 MU_IMAP_SCHEME,
69 MU_RECORD_DEFAULT,
70 MU_URL_SCHEME | MU_URL_CRED | MU_URL_INET | MU_URL_PATH,
71 MU_URL_HOST,
72 _url_imap_init, /* url entry. */
73 _mailbox_imap_init, /* Mailbox entry. */
74 NULL, /* Mailer entry. */
75 _folder_imap_init, /* Folder entry. */
76 NULL, /* No need for a back pointer. */
77 NULL, /* _is_scheme method. */
78 NULL, /* _get_url method. */
79 NULL, /* _get_mailbox method. */
80 NULL, /* _get_mailer method. */
81 NULL /* _get_folder method. */
82 };
83 46
84 /* We export this variable: url parsing and the initialisation of the mailbox, 47 static void
85 via the register entry/record. */ 48 _mu_imap_folder_destroy (mu_folder_t folder)
86 mu_record_t mu_imap_record = &_imap_record; 49 {
50 mu_imap_t imap = folder->data;
51 if (imap)
52 {
53 /*
54 mu_imap_logout (imap);
55 mu_imap_disconnect (imap);
56 */
57 mu_imap_destroy (&imap);
58 folder->data = imap;
59 }
60 }
87 61
88 #ifdef WITH_TLS 62 /* Imap callbacks */
89 static struct _mu_record _imaps_record = 63 static void
64 _mu_folder_preauth_callback (void *data, int code, size_t sdat, void *pdat)
90 { 65 {
91 MU_IMAP_PRIO, 66 /* mu_folder_t folder = data;*/
92 MU_IMAPS_SCHEME, 67 const char *text = pdat;
93 MU_RECORD_DEFAULT, 68 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_TRACE1,
94 MU_URL_SCHEME | MU_URL_CRED | MU_URL_INET | MU_URL_PATH | MU_URL_PARAM, 69 (_("IMAP server opened in preauth mode: %s"), text));
95 MU_URL_HOST, 70 }
96 _url_imaps_init, /* url entry. */
97 _mailbox_imaps_init, /* Mailbox entry. */
98 NULL, /* Mailer entry. */
99 _folder_imap_init, /* Folder entry. */
100 NULL, /* No need for a back pointer. */
101 NULL, /* _is_scheme method. */
102 NULL, /* _get_url method. */
103 NULL, /* _get_mailbox method. */
104 NULL, /* _get_mailer method. */
105 NULL /* _get_folder method. */
106 };
107 mu_record_t mu_imaps_record = &_imaps_record;
108 #else
109 mu_record_t mu_imaps_record = NULL;
110 #endif /* WITH_TLS */
111 71
112 #ifndef HAVE_STRTOK_R 72 static void
113 char *strtok_r (char *, const char *, char **); 73 _mu_folder_bye_callback (void *data, int code, size_t sdat, void *pdat)
114 #endif 74 {
75 mu_folder_t folder = data;
76 const char *text = pdat;
77 mu_imap_t imap = folder->data;
78 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_TRACE1,
79 (_("IMAP server closing connection: %s"), text));
80 /*FIXME: mu_imap_disconnect (imap);*/
81 }
115 82
116 /* Concrete mu_folder_t IMAP implementation. */ 83 static void
117 static int folder_imap_open (mu_folder_t, int); 84 _mu_folder_bad_callback (void *data, int code, size_t sdat, void *pdat)
118 static int folder_imap_close (mu_folder_t); 85 {
119 static void folder_imap_destroy (mu_folder_t); 86 /* mu_folder_t folder = data;*/
120 static int folder_imap_delete (mu_folder_t, const char *); 87 const char *text = pdat;
121 static int folder_imap_list (mu_folder_t, const char *, void *,
122 int, size_t,
123 mu_list_t,
124 mu_folder_enumerate_fp efp, void *edp);
125 static int folder_imap_lsub (mu_folder_t, const char *, const char *,
126 mu_list_t);
127 static int folder_imap_rename (mu_folder_t, const char *,
128 const char *);
129 static int folder_imap_subscribe (mu_folder_t, const char *);
130 static int folder_imap_unsubscribe (mu_folder_t, const char *);
131 static int folder_imap_get_authority (mu_folder_t, mu_authority_t *);
132
133 /* FETCH */
134 static int imap_fetch (f_imap_t);
135 static int imap_rfc822 (f_imap_t, char **);
136 static int imap_rfc822_size (f_imap_t, char **);
137 static int imap_rfc822_header (f_imap_t, char **);
138 static int imap_rfc822_text (f_imap_t, char **);
139 static int imap_fetch_flags (f_imap_t, char **);
140 static int imap_permanentflags (f_imap_t, char **);
141 static int imap_flags (char **, int *);
142 static int imap_bodystructure (f_imap_t, char **);
143 static int imap_body (f_imap_t, char **);
144 static int imap_internaldate (f_imap_t, char **);
145
146 static int imap_uid (f_imap_t, char **);
147 static int imap_status (f_imap_t);
148 static int imap_expunge (f_imap_t, unsigned int);
149 static int imap_search (f_imap_t);
150
151 /* String. */
152 static int imap_literal_string (f_imap_t, char **);
153 static int imap_string (f_imap_t, char **);
154 static int imap_quoted_string (f_imap_t, char **);
155 static int imap_mailbox_name_match (const char* pattern, const char* mailbox);
156
157 static int imap_token (char *, size_t, char **);
158
159 /* Capability */
160 static int parse_capa (f_imap_t f_imap, char *str);
161 static int read_capa (f_imap_t f_imap, int force);
162 static int check_capa (f_imap_t f_imap, char *capa);
163
164
165 /* Authentication methods */
166 88
167 typedef int (*auth_method_t) (mu_authority_t); 89 mu_error (_("IMAP server complains: %s"), text);
90 mu_error (_("This probably indicates a bug in Mailutils client code."));
91 mu_error (_("Please, report that to <%s>."), PACKAGE_BUGREPORT);
92 }
93 #if 0
94 static void
95 _mu_folder_fetch_callback (void *data, int code, size_t sdat, void *pdat)
96 {
97 mu_folder_t folder = data;
98 }
99 #endif
168 100
169 /* Simple User/pass authentication for imap. */ 101 /* Set up an IMAP(S) connection for this folder */
170 static int 102 static int
171 authenticate_imap_login (mu_authority_t auth) 103 _mu_imap_folder_open (mu_folder_t folder, int flags)
172 { 104 {
173 mu_folder_t folder = mu_authority_get_owner (auth); 105 int rc;
174 f_imap_t f_imap = folder->data; 106 mu_imap_t imap = folder->data;
175 mu_ticket_t ticket; 107 struct mu_sockaddr *sa;
176 int status = 0; 108 struct mu_sockaddr_hints hints;
109 char const *s;
110 int tls;
111 mu_stream_t transport;
112
113 /* FIXME: This monitor business is suspicious */
114 mu_monitor_wrlock (folder->monitor);
115 rc = mu_imap_session_state (imap);
116 mu_monitor_unlock (folder->monitor);
117 if (rc != MU_IMAP_SESSION_INIT)
118 return 0;
119
120 mu_url_sget_scheme (folder->url, &s);
121 tls = strcmp (s, "imaps") == 0;
122
123 memset (&hints, 0, sizeof (hints));
124 hints.flags = MU_AH_DETECT_FAMILY;
125 hints.port = tls ? MU_IMAP_DEFAULT_SSL_PORT : MU_IMAP_DEFAULT_PORT;
126 hints.protocol = IPPROTO_TCP;
127 hints.socktype = SOCK_STREAM;
177 128
178 if (check_capa (f_imap, "LOGINDISABLED") == 0) 129 rc = mu_sockaddr_from_url (&sa, folder->url, &hints);
130 if (rc)
179 { 131 {
180 MU_DEBUG (folder->debug, MU_DEBUG_TRACE, "LOGIN command disabled\n"); 132 s = mu_url_to_string (folder->url);
181 return ENOSYS; 133 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
134 (_("cannot create sockaddr from URL %s: %s"),
135 s, mu_strerror (rc)));
136 return rc;
182 } 137 }
183 138
184 switch (f_imap->state) 139 rc = mu_tcp_stream_create_from_sa (&transport, sa, NULL, 0);
140 if (rc)
185 { 141 {
186 case IMAP_AUTH: 142 s = mu_url_to_string (folder->url);
143 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
144 (_("cannot create stream from URL %s: %s"),
145 s, mu_strerror (rc)));
146 mu_sockaddr_free (sa);
147 return rc;
148 }
149
150 #ifdef WITH_TLS
151 if (tls)
187 { 152 {
188 /* Grab the User and Passwd information. */ 153 mu_stream_t tlsstream;
189 mu_authority_get_ticket (auth, &ticket);
190 if (f_imap->user)
191 free (f_imap->user);
192 /* Was it in the URL? */
193 status = mu_url_aget_user (folder->url, &f_imap->user);
194 if (status == MU_ERR_NOENT)
195 status = mu_ticket_get_cred (ticket, folder->url,
196 "Imap User: ", &f_imap->user, NULL);
197 if (status == MU_ERR_NOENT || f_imap->user == NULL)
198 return MU_ERR_NOUSERNAME;
199 else if (status)
200 return status;
201 154
202 status = mu_url_get_secret (folder->url, &f_imap->secret); 155 rc = mu_tls_client_stream_create (&tlsstream, transport, transport, 0);
203 if (status == MU_ERR_NOENT) 156 mu_stream_unref (transport);
204 status = mu_ticket_get_cred (ticket, folder->url, 157 if (rc)
205 "Imap Passwd: ", 158 {
206 NULL, &f_imap->secret); 159 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
160 (_("cannot create TLS stream: %s"),
161 mu_strerror (rc)));
162 return rc;
163 }
164 transport = tlsstream;
165 }
166 #endif
207 167
208 if (status == MU_ERR_NOENT || !f_imap->secret) 168 mu_imap_set_carrier (imap, transport);
209 /* FIXME: Is this always right? The user might legitimately have 169
210 no password */ 170 if (mu_debug_level_p (MU_DEBCAT_FOLDER, MU_DEBUG_PROT) ||
211 return MU_ERR_NOPASSWORD; 171 mu_debug_level_p (MU_DEBCAT_MAILBOX, MU_DEBUG_PROT))
212 else if (status) 172 mu_imap_trace (imap, MU_IMAP_TRACE_SET);
213 return status; 173 if (mu_debug_level_p (MU_DEBCAT_FOLDER, MU_DEBUG_TRACE6) ||
214 174 mu_debug_level_p (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE6))
215 status = imap_writeline (f_imap, "g%lu LOGIN \"%s\" \"%s\"\r\n", 175 mu_imap_trace_mask (imap, MU_IMAP_TRACE_SET, MU_XSCRIPT_SECURE);
216 (unsigned long) f_imap->seq, f_imap->user, 176 if (mu_debug_level_p (MU_DEBCAT_FOLDER, MU_DEBUG_TRACE7) ||
217 mu_secret_password (f_imap->secret)); 177 mu_debug_level_p (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE7))
218 mu_secret_password_unref (f_imap->secret); 178 mu_imap_trace_mask (imap, MU_IMAP_TRACE_SET, MU_XSCRIPT_PAYLOAD);
219 mu_secret_unref (f_imap->secret); 179
220 f_imap->secret = NULL; 180 /* Set callbacks */
221 CHECK_ERROR_CLOSE (folder, f_imap, status); 181 mu_imap_register_callback_function (imap, MU_IMAP_CB_PREAUTH,
222 MU_DEBUG2 (folder->debug, MU_DEBUG_TRACE, "g%lu LOGIN %s *\n", 182 _mu_folder_preauth_callback,
223 (unsigned long) f_imap->seq, f_imap->user); 183 folder);
224 f_imap->seq++; 184 mu_imap_register_callback_function (imap, MU_IMAP_CB_BYE,
225 free (f_imap->user); 185 _mu_folder_bye_callback,
226 f_imap->user = NULL; 186 folder);
227 f_imap->secret = NULL; 187 mu_imap_register_callback_function (imap, MU_IMAP_CB_BAD,
228 f_imap->state = IMAP_LOGIN; 188 _mu_folder_bad_callback,
189 folder);
190 #if 0
191 mu_imap_register_callback_function (imap, MU_IMAP_CB_FETCH,
192 _mu_folder_fetch_callback,
193 folder);
194 #endif
195 rc = mu_imap_connect (imap);
196 if (rc)
197 {
198 s = mu_url_to_string (folder->url);
199 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
200 (_("failed to connect to %s: %s"),
201 s, mu_strerror (rc)));
202 if (mu_imap_strerror (imap, &s))
203 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
204 (_("server response: %s"), s));
205 mu_imap_destroy (&imap);
206 return rc;
229 } 207 }
230 208
231 case IMAP_LOGIN: 209 if (mu_imap_session_state (imap) == MU_IMAP_SESSION_NONAUTH)
232 /* Send it across. */ 210 {
233 status = imap_send (f_imap); 211 rc = mu_authority_authenticate (folder->authority);
234 CHECK_EAGAIN (f_imap, status); 212 if (rc)
235 /* Clear the buffer it contains the passwd. */ 213 {
236 memset (f_imap->buffer, '\0', f_imap->buflen); 214 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
237 f_imap->state = IMAP_LOGIN_ACK; 215 (_("IMAP authentication: %s"),
238 216 mu_strerror (rc)));
239 case IMAP_LOGIN_ACK: 217 mu_folder_close (folder);
240 /* Get the login ack. */
241 status = imap_parse (f_imap);
242 if (status)
243 return status;
244 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
245 f_imap->state = IMAP_AUTH_DONE;
246
247 default:
248 break; /* We're outta here. */
249 } 218 }
250 CLEAR_STATE (f_imap); 219 }
251 return 0; 220
221 return rc;
252 } 222 }
253 223
254 /* 224 /* Close existing connection */
255 The anonymous SASL mechanism is defined in rfc2245.txt as a single 225 static int
256 message from client to server: 226 _mu_imap_folder_close (mu_folder_t folder)
227 {
228 mu_imap_t imap = folder->data;
257 229
258 message = [email / token] 230 if (mu_imap_session_state (imap) > MU_IMAP_SESSION_INIT)
231 {
232 mu_imap_clearerr (imap);
233 mu_imap_logout (imap);
234 mu_imap_disconnect (imap);
235 }
259 236
260 So the message is optional. 237 return 0;
238 }
261 239
262 The command is: 240 struct enumerate_closure
241 {
242 mu_folder_t folder;
243 mu_folder_enumerate_fp fun;
244 void *data;
245 };
263 246
264 C: <tag> authenticate anonymous 247 static int
248 _enumerate_helper (void *item, void *data)
249 {
250 struct mu_list_response *rp = item;
251 struct enumerate_closure *clos = data;
265 252
266 The server responds with a request for continuation data (the "message" 253 return clos->fun (clos->folder, rp, clos->data);
267 in the SASL syntax). We respond with no data, which is legal. 254 }
268 255
269 S: + 256 static int
270 C: 257 _mu_imap_folder_list (mu_folder_t folder, const char *ref, void *name,
258 int flags, size_t max_level,
259 mu_list_t flist,
260 mu_folder_enumerate_fp efp, void *edp)
261 {
262 mu_imap_t imap = folder->data;
263 mu_list_t list;
264 int rc = mu_imap_list_new (imap, ref, name, &list);
271 265
272 The server should then respond with OK on success, or else a failure 266 if (rc)
273 code (NO or BAD). 267 return rc;
274 268
275 If OK, then we are authenticated! 269 if (max_level ||
270 (flags & MU_FOLDER_ATTRIBUTE_ALL) != MU_FOLDER_ATTRIBUTE_ALL)
271 {
272 /* Filter out the list, eliminating non-matching entries */
273 mu_iterator_t itr;
276 274
277 So, states are: 275 rc = mu_list_get_iterator (list, &itr);
276 if (rc)
277 {
278 mu_list_destroy (&list);
279 return rc;
280 }
278 281
279 AUTH_ANON_REQ 282 for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
283 mu_iterator_next (itr))
284 {
285 struct mu_list_response *rp;
280 286
281 > g%u AUTHENTICATE ANONYMOUS 287 mu_iterator_current (itr, (void**) &rp);
288 if (!(rp->type & flags) || (max_level && rp->level > max_level))
289 mu_iterator_ctl (itr, mu_itrctl_delete, NULL);
290 }
291 mu_iterator_destroy (&itr);
292 }
282 293
283 AUTH_ANON_WAIT_CONT 294 if (efp)
295 {
296 struct enumerate_closure clos;
284 297
285 < + 298 clos.folder = folder;
299 clos.fun = efp;
300 clos.data = edp;
286 301
287 AUTH_ANON_MSG 302 rc = mu_list_foreach (list, _enumerate_helper, &clos);
303 }
288 304
289 > 305 if (flist)
306 mu_list_append_list (flist, list);
290 307
291 AUTH_ANON_WAIT_RESP 308 mu_list_destroy (&list);
292 309
293 < NO/BAD/OK 310 return rc;
311 }
294 312
295 */ 313 static int
314 _mu_imap_folder_lsub (mu_folder_t folder, const char *ref, const char *name,
315 mu_list_t flist)
316 {
317 mu_imap_t imap = folder->data;
318 return mu_imap_lsub (imap, ref, name, flist);
319 }
296 320
321 /* Subscribe to the named mailbox. */
297 static int 322 static int
298 authenticate_imap_sasl_anon (mu_authority_t auth) 323 _mu_imap_folder_subscribe (mu_folder_t folder, const char *name)
299 { 324 {
300 mu_folder_t folder = mu_authority_get_owner (auth); 325 mu_imap_t imap = folder->data;
301 f_imap_t f_imap = folder->data; 326 return mu_imap_subscribe (imap, name);
302 int status = 0; 327 }
328 /* Unsubscribe from the mailbox. */
329 static int
330 _mu_imap_folder_unsubscribe (mu_folder_t folder, const char *name)
331 {
332 mu_imap_t imap = folder->data;
333 return mu_imap_unsubscribe (imap, name);
334 }
303 335
304 assert (f_imap->state == IMAP_AUTH); 336 /* Remove a mailbox. */
337 static int
338 _mu_imap_folder_delete (mu_folder_t folder, const char *name)
339 {
340 mu_imap_t imap = folder->data;
341 return mu_imap_delete (imap, name);
342 }
305 343
306 if (check_capa (f_imap, "AUTH=ANONYMOUS")) 344 /* Rename OLDPATH to NEWPATH */
307 { 345 static int
308 MU_DEBUG (folder->debug, MU_DEBUG_PROT, 346 _mu_imap_folder_rename (mu_folder_t folder, const char *oldpath,
309 "ANONYMOUS capability not present\n"); 347 const char *newpath)
310 return ENOSYS; 348 {
311 } 349 mu_imap_t imap = folder->data;
350 return mu_imap_rename (imap, oldpath, newpath);
351 }
312 352
313 /* FIXME: auth_state is never set explicitely before this function */ 353 typedef int (*auth_method_t) (mu_authority_t);
314 switch (f_imap->auth_state)
315 {
316 case IMAP_AUTH_ANON_REQ_WRITE:
317 {
318 MU_DEBUG1 (folder->debug, MU_DEBUG_PROT,
319 "g%lu AUTHENTICATE ANONYMOUS\n",
320 (unsigned long) f_imap->seq);
321 status = imap_writeline (f_imap, "g%lu AUTHENTICATE ANONYMOUS\r\n",
322 (unsigned long) f_imap->seq);
323 f_imap->seq++;
324 CHECK_ERROR_CLOSE (folder, f_imap, status);
325 f_imap->auth_state = IMAP_AUTH_ANON_REQ_SEND;
326 }
327 354
328 case IMAP_AUTH_ANON_REQ_SEND: 355 static int
329 status = imap_send (f_imap); 356 authenticate_imap_login (mu_authority_t auth)
330 CHECK_EAGAIN (f_imap, status); 357 {
331 f_imap->auth_state = IMAP_AUTH_ANON_WAIT_CONT; 358 mu_folder_t folder = mu_authority_get_owner (auth);
359 mu_imap_t imap = folder->data;
360 mu_ticket_t ticket;
361 char *user;
362 int rc;
363 mu_secret_t secret;
332 364
333 case IMAP_AUTH_ANON_WAIT_CONT: 365 rc = mu_imap_capability_test (imap, "LOGINDISABLED", NULL);
334 status = imap_parse (f_imap); 366 if (rc == 0)
335 CHECK_EAGAIN (f_imap, status);
336 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
337 if (strncmp ("+", f_imap->buffer, 2) == 0)
338 { 367 {
339 f_imap->auth_state = IMAP_AUTH_ANON_MSG; 368 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
369 (_("IMAP LOGIN disabled")));
370 return rc;
340 } 371 }
341 else 372 else if (rc != MU_ERR_NOENT)
342 { 373 {
343 /* Something is wrong! */ 374 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
375 (_("cannot test server capabilities (%s), continuing anyway"),
376 mu_strerror (rc)));
377 return rc;
344 } 378 }
345 f_imap->auth_state = IMAP_AUTH_ANON_MSG;
346
347 case IMAP_AUTH_ANON_MSG:
348 MU_DEBUG (folder->debug, MU_DEBUG_PROT, "\n");
349 status = imap_writeline (f_imap, "\r\n");
350 CHECK_ERROR_CLOSE (folder, f_imap, status);
351 f_imap->auth_state = IMAP_AUTH_ANON_MSG_SEND;
352 379
353 case IMAP_AUTH_ANON_MSG_SEND: 380 /* Grab the User and Passwd information. */
354 status = imap_send (f_imap); 381 mu_authority_get_ticket (auth, &ticket);
355 CHECK_EAGAIN (f_imap, status); 382 /* Was it in the URL? */
356 383 rc = mu_url_aget_user (folder->url, &user);
357 f_imap->auth_state = IMAP_AUTH_ANON_WAIT_RESP; 384 if (rc == MU_ERR_NOENT)
385 rc = mu_ticket_get_cred (ticket, folder->url, "Imap User: ", &user, NULL);
386 if (rc == MU_ERR_NOENT || user == NULL)
387 return MU_ERR_NOUSERNAME;
388 else if (rc)
389 return rc;
358 390
359 case IMAP_AUTH_ANON_WAIT_RESP: 391 rc = mu_url_get_secret (folder->url, &secret);
360 status = imap_parse (f_imap); 392 if (rc == MU_ERR_NOENT)
361 CHECK_EAGAIN (f_imap, status); 393 rc = mu_ticket_get_cred (ticket, folder->url,
362 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer); 394 "Imap Passwd: ", NULL, &secret);
363 395
364 default: 396 if (rc == MU_ERR_NOENT || !secret)
365 break; /* We're outta here. */ 397 {
398 /* FIXME: Is this always right? The user might legitimately have
399 no password */
400 free (user);
401 return MU_ERR_NOPASSWORD;
366 } 402 }
367 CLEAR_STATE (f_imap); 403 else if (rc)
368 return 0; 404 {
405 free (user);
406 return rc;
407 }
408 rc = mu_imap_login_secret (imap, user, secret);
409 mu_secret_unref (secret);
410 return rc;
369 } 411 }
370 412
371 struct auth_tab 413 struct auth_tab
...@@ -379,7 +421,7 @@ struct auth_tab ...@@ -379,7 +421,7 @@ struct auth_tab
379 421
380 static struct auth_tab auth_tab[] = { 422 static struct auth_tab auth_tab[] = {
381 { "login", authenticate_imap_login }, 423 { "login", authenticate_imap_login },
382 { "anon", authenticate_imap_sasl_anon }, 424 /* { "anon", authenticate_imap_sasl_anon },*/
383 { NULL } 425 { NULL }
384 }; 426 };
385 427
...@@ -398,74 +440,13 @@ find_auth_method (const char *name) ...@@ -398,74 +440,13 @@ find_auth_method (const char *name)
398 static int 440 static int
399 authenticate_imap_select (mu_authority_t auth) 441 authenticate_imap_select (mu_authority_t auth)
400 { 442 {
401 mu_folder_t folder = mu_authority_get_owner (auth);
402 f_imap_t f_imap = folder->data;
403 struct auth_tab *p; 443 struct auth_tab *p;
404 int status = ENOSYS; 444 int rc = ENOSYS;
405
406 for (p = auth_tab; status == ENOSYS && p->name; p++)
407 {
408 f_imap->state = IMAP_AUTH;
409 status = p->method (auth);
410 }
411
412 return status;
413 }
414
415
416
417
418 /* Initialize the concrete IMAP mailbox: overload the folder functions */
419 int
420 _folder_imap_init (mu_folder_t folder)
421 {
422 int status;
423 f_imap_t f_imap;
424 445
425 /* Set the authority early: 446 for (p = auth_tab; rc == ENOSYS && p->name; p++)
426 (1) so we can check for errors. 447 rc = p->method (auth);
427 (2) allow the client to get the authority for setting the ticket
428 before the open. */
429 status = folder_imap_get_authority (folder, NULL);
430 if (status != 0)
431 return status;
432 448
433 f_imap = folder->data = calloc (1, sizeof (*f_imap)); 449 return rc;
434 if (f_imap == NULL)
435 return ENOMEM;
436
437 f_imap->folder = folder;
438 f_imap->state = IMAP_NO_STATE;
439
440 folder->_destroy = folder_imap_destroy;
441
442 folder->_open = folder_imap_open;
443 folder->_close = folder_imap_close;
444
445 folder->_list = folder_imap_list;
446 folder->_lsub = folder_imap_lsub;
447 folder->_subscribe = folder_imap_subscribe;
448 folder->_unsubscribe = folder_imap_unsubscribe;
449 folder->_delete = folder_imap_delete;
450 folder->_rename = folder_imap_rename;
451
452 return 0;
453 }
454
455 /* Destroy the folder resources. */
456 static void
457 folder_imap_destroy (mu_folder_t folder)
458 {
459 if (folder->data)
460 {
461 f_imap_t f_imap = folder->data;
462 if (f_imap->buffer)
463 free (f_imap->buffer);
464 if (f_imap->capav)
465 mu_argcv_free (f_imap->capac, f_imap->capav);
466 free (f_imap);
467 folder->data = NULL;
468 }
469 } 450 }
470 451
471 static int 452 static int
...@@ -473,2152 +454,139 @@ folder_set_auth_method (mu_folder_t folder, auth_method_t method) ...@@ -473,2152 +454,139 @@ folder_set_auth_method (mu_folder_t folder, auth_method_t method)
473 { 454 {
474 if (!folder->authority) 455 if (!folder->authority)
475 { 456 {
476 int status = mu_authority_create (&folder->authority, NULL, folder); 457 int rc = mu_authority_create (&folder->authority, NULL, folder);
477 if (status) 458 if (rc)
478 return status; 459 return rc;
479 } 460 }
480 return mu_authority_set_authenticate (folder->authority, method, folder); 461 return mu_authority_set_authenticate (folder->authority, method, folder);
481 } 462 }
482 463
483 static int 464 static int
484 folder_imap_get_authority (mu_folder_t folder, mu_authority_t *pauth) 465 _imap_folder_setup_authority (mu_folder_t folder)
485 { 466 {
486 int status = 0; 467 int rc = 0;
487 if (folder->authority == NULL) 468
469 if (!folder->authority)
488 { 470 {
489 /* assert (folder->url); */ 471 const char *auth;
490 if (folder->url == NULL) 472 if (folder->url == NULL)
491 return EINVAL; 473 return EINVAL;
492 474
493 if (folder->url->auth == NULL 475 if (mu_url_sget_auth (folder->url, &auth))
494 || strcmp (folder->url->auth, "*") == 0) 476 rc = folder_set_auth_method (folder, authenticate_imap_select);
477 else if (strcmp (auth, "*") == 0)
478 rc = folder_set_auth_method (folder, authenticate_imap_select);
479 else
480 {
481 struct mu_wordsplit ws;
482
483 ws.ws_delim = ",";
484 if (mu_wordsplit (auth, &ws,
485 MU_WRDSF_NOVAR | MU_WRDSF_NOCMD |
486 MU_WRDSF_DELIM))
495 { 487 {
496 status = folder_set_auth_method (folder, authenticate_imap_select); 488 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
489 (_("cannot split out auth part: %s"),
490 mu_wordsplit_strerror (&ws)));
491 rc = MU_ERR_FAILURE;
497 } 492 }
498 else 493 else
499 { 494 {
500 char *p, *sp; 495 int i;
501 496
502 for (p = strtok_r (folder->url->auth, ",", &sp); 497 for (i = 0; i < ws.ws_wordc; i++)
503 status == 0 && p;
504 p = strtok_r (NULL, ",", &sp))
505 { 498 {
506 auth_method_t method = find_auth_method (p); 499 auth_method_t method = find_auth_method (ws.ws_wordv[i]);
507 if (method) 500 if (method)
508 status = folder_set_auth_method (folder, method); 501 rc = folder_set_auth_method (folder, method);
509 else 502 else
510 status = MU_ERR_BAD_AUTH_SCHEME; 503 {
504 mu_debug (MU_DEBCAT_FOLDER, MU_DEBUG_ERROR,
505 (_("unrecognized AUTH scheme %s"),
506 ws.ws_wordv[i]));
507 rc = MU_ERR_BAD_AUTH_SCHEME;
508 }
509 }
510 mu_wordsplit_free (&ws);
511 } 511 }
512 } 512 }
513 } 513 }
514 514
515 if (status) 515 return rc;
516 return status;
517
518 if (pauth)
519 *pauth = folder->authority;
520 return status;
521 } 516 }
522 517
523 518 int
524 /* Capability handling */ 519 _mu_imap_folder_init (mu_folder_t folder)
525 static int
526 parse_capa (f_imap_t f_imap, char *str)
527 { 520 {
528 if (f_imap->capav) 521 mu_imap_t imap;
529 mu_argcv_free (f_imap->capac, f_imap->capav); 522 int rc;
530 return mu_argcv_get (str, "", NULL, &f_imap->capac, &f_imap->capav);
531 }
532 523
533 static int 524 rc = _imap_folder_setup_authority (folder);
534 read_capa (f_imap_t f_imap, int force) 525 if (rc)
535 { 526 return rc;
536 int status = 0;
537 527
538 if (force) 528 rc = mu_imap_create (&imap);
539 { 529 if (rc)
540 mu_argcv_free (f_imap->capac, f_imap->capav); 530 return rc;
541 f_imap->capac = 0;
542 f_imap->capav = NULL;
543 }
544 531
545 if (!f_imap->capav) 532 folder->data = imap;
546 {
547 status = imap_writeline (f_imap, "g%lu CAPABILITY\r\n",
548 (unsigned long) f_imap->seq++);
549 status = imap_send (f_imap);
550 status = imap_parse (f_imap);
551 }
552 return status;
553 }
554 533
555 static int 534 folder->_destroy = _mu_imap_folder_destroy;
556 check_capa (f_imap_t f_imap, char *capa)
557 {
558 int i;
559 535
560 read_capa (f_imap, 0); 536 folder->_open = _mu_imap_folder_open;
561 for (i = 0; i < f_imap->capac; i++) 537 folder->_close = _mu_imap_folder_close;
562 if (mu_c_strcasecmp (f_imap->capav[i], capa) == 0)
563 return 0;
564 return 1;
565 }
566 538
539 folder->_list = _mu_imap_folder_list;
540 folder->_lsub = _mu_imap_folder_lsub;
567 541
568 static int 542 folder->_subscribe = _mu_imap_folder_subscribe;
569 imap_reader (void *iodata) 543 folder->_unsubscribe = _mu_imap_folder_unsubscribe;
570 { 544 folder->_delete = _mu_imap_folder_delete;
571 f_imap_t iop = iodata; 545 folder->_rename = _mu_imap_folder_rename;
572 int status = imap_parse (iop); 546 return 0;
573 CHECK_EAGAIN (iop, status);
574 MU_DEBUG (iop->folder->debug, MU_DEBUG_PROT, iop->buffer);
575 return status;
576 } 547 }
577 548
578 static int 549 static struct _mu_record _imap_record =
579 imap_writer (void *iodata, char *buf)
580 { 550 {
581 f_imap_t iop = iodata; 551 MU_IMAP_PRIO,
582 int status; 552 MU_IMAP_SCHEME,
583 553 MU_RECORD_DEFAULT,
584 MU_DEBUG2 (iop->folder->debug, MU_DEBUG_PROT, "g%lu %s\n", 554 MU_URL_SCHEME | MU_URL_CRED | MU_URL_INET | MU_URL_PATH,
585 (unsigned long)iop->seq, buf); 555 MU_URL_HOST,
586 status = imap_writeline (iop, "g%lu %s\r\n", 556 _mu_imap_url_init, /* url entry. */
587 (unsigned long)iop->seq++, buf); 557 _mu_imap_mailbox_init, /* Mailbox entry. */
588 CHECK_ERROR (iop, status); 558 NULL, /* Mailer entry. */
589 status = imap_send (iop); 559 _mu_imap_folder_init, /* Folder entry. */
590 CHECK_ERROR (iop, status); 560 NULL, /* No need for a back pointer. */
591 return status; 561 NULL, /* _is_scheme method. */
592 } 562 NULL, /* _get_url method. */
563 NULL, /* _get_mailbox method. */
564 NULL, /* _get_mailer method. */
565 NULL /* _get_folder method. */
566 };
593 567
594 static void 568 mu_record_t mu_imap_record = &_imap_record;
595 imap_stream_ctl (void *iodata, mu_stream_t *pold, mu_stream_t new)
596 {
597 f_imap_t iop = iodata;
598 if (pold)
599 *pold = iop->folder->stream;
600 if (new)
601 iop->folder->stream = new;
602 }
603 569
604 static int
605 tls (mu_folder_t folder)
606 {
607 #ifdef WITH_TLS 570 #ifdef WITH_TLS
608 int status; 571 static struct _mu_record _imaps_record =
609 f_imap_t f_imap = folder->data;
610 char *keywords[] = { "STARTTLS", "CAPABILITY", NULL };
611
612 if (!mu_tls_enable || check_capa (f_imap, "STARTTLS"))
613 return -1;
614
615 status = mu_tls_begin (f_imap, imap_reader, imap_writer,
616 imap_stream_ctl, keywords);
617
618 MU_DEBUG1 (folder->debug, MU_DEBUG_PROT, "TLS negotiation %s\n",
619 status == 0 ? "succeeded" : "failed");
620 return status;
621 #else
622 return -1;
623 #endif /* WITH_TLS */
624 }
625
626 /* Create/Open the stream for IMAP. */
627 static int
628 folder_imap_open (mu_folder_t folder, int flags)
629 { 572 {
630 f_imap_t f_imap = folder->data; 573 MU_IMAP_PRIO,
631 const char *host; 574 MU_IMAPS_SCHEME,
632 unsigned port = f_imap->imaps ? MU_IMAPS_PORT : MU_IMAP_PORT; 575 MU_RECORD_DEFAULT,
633 int status = 0; 576 MU_URL_SCHEME | MU_URL_CRED | MU_URL_INET | MU_URL_PATH | MU_URL_PARAM,
634 577 MU_URL_HOST,
635 /* If we are already open for business, noop. */ 578 _mu_imaps_url_init, /* url entry. */
636 mu_monitor_wrlock (folder->monitor); 579 _mu_imaps_mailbox_init, /* Mailbox entry. */
637 if (f_imap->isopen) 580 NULL, /* Mailer entry. */
638 { 581 _mu_imap_folder_init, /* Folder entry. */
639 mu_monitor_unlock (folder->monitor); 582 NULL, /* No need for a back pointer. */
640 return 0; 583 NULL, /* _is_scheme method. */
641 } 584 NULL, /* _get_url method. */
642 mu_monitor_unlock (folder->monitor); 585 NULL, /* _get_mailbox method. */
643 586 NULL, /* _get_mailer method. */
644 /* Fetch the server name and the port in the mu_url_t. */ 587 NULL /* _get_folder method. */
645 status = mu_url_sget_host (folder->url, &host); 588 };
646 if (status != 0) 589 mu_record_t mu_imaps_record = &_imaps_record;
647 return status;
648 mu_url_get_port (folder->url, &port);
649
650 folder->flags = flags;
651
652 switch (f_imap->state)
653 {
654 case IMAP_NO_STATE:
655 /* allocate working io buffer. */
656 if (f_imap->buffer == NULL)
657 {
658 /* There is no particular limit on the length of a command/response
659 in IMAP. We start with 255, which is quite reasonnable and grow
660 as we go along. */
661 f_imap->buflen = 255;
662 f_imap->buffer = calloc (f_imap->buflen + 1, 1);
663 if (f_imap->buffer == NULL)
664 {
665 CHECK_ERROR (f_imap, ENOMEM);
666 }
667 status = mu_memory_stream_create (&f_imap->string.stream, NULL, MU_STREAM_RDWR);
668 CHECK_ERROR (f_imap, status);
669 }
670 else
671 {
672 /* Clear from any residue. */
673 memset (f_imap->buffer, '\0', f_imap->buflen);
674 mu_stream_truncate (f_imap->string.stream, 0);
675 f_imap->string.offset = 0;
676 f_imap->string.nleft = 0;
677 }
678 f_imap->ptr = f_imap->buffer;
679
680 /* Create the networking stack. */
681 if (folder->stream == NULL)
682 {
683 status = mu_tcp_stream_create (&folder->stream, host, port, folder->flags);
684 CHECK_ERROR (f_imap, status);
685
686 #ifdef WITH_TLS
687 if (f_imap->imaps)
688 {
689 mu_stream_t newstr;
690
691 status = mu_tls_client_stream_create (&newstr,
692 folder->stream,
693 folder->stream, 0);
694 if (status != 0)
695 {
696 mu_error ("folder_imap_open: mu_tls_client_stream_create: %s",
697 mu_strerror (status));
698 return status;
699 }
700 folder->stream = newstr;
701 }
702 #endif /* WITH_TLS */
703
704 /* Ask for the stream internal buffering mechanism scheme. */
705 mu_stream_setbufsiz (folder->stream, BUFSIZ);
706 }
707 else
708 mu_stream_close (folder->stream);
709 MU_DEBUG2 (folder->debug, MU_DEBUG_PROT, "imap_open (%s:%ld)\n",
710 host, port);
711 f_imap->state = IMAP_OPEN_CONNECTION;
712
713 case IMAP_OPEN_CONNECTION:
714 /* Establish the connection. */
715 if (!mu_stream_is_open (folder->stream))
716 {
717 status = mu_stream_open (folder->stream);
718 CHECK_EAGAIN (f_imap, status);
719 /* Can't recover bailout. */
720 CHECK_ERROR_CLOSE (folder, f_imap, status);
721 }
722 f_imap->state = IMAP_GREETINGS;
723
724 case IMAP_GREETINGS:
725 {
726 /* Swallow the greetings. */
727 status = imap_readline (f_imap);
728 CHECK_EAGAIN (f_imap, status);
729 f_imap->ptr = f_imap->buffer;
730 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
731 /* Are they open for business ? The server send an untagged response
732 for greeting. Tecnically it can be OK/PREAUTH/BYE. The BYE is
733 the one that we do not want, server being unfriendly. */
734 if (mu_c_strncasecmp (f_imap->buffer, "* PREAUTH", 9) == 0)
735 {
736 f_imap->state = IMAP_AUTH_DONE;
737 }
738 else
739 {
740 if (mu_c_strncasecmp (f_imap->buffer, "* OK", 4) != 0)
741 CHECK_ERROR_CLOSE (folder, f_imap, EACCES);
742 f_imap->state = IMAP_AUTH;
743 }
744 }
745 if (!f_imap->imaps)
746 tls (folder);
747
748 case IMAP_AUTH:
749 case IMAP_LOGIN:
750 case IMAP_LOGIN_ACK:
751 assert (folder->authority);
752 {
753 status = mu_authority_authenticate (folder->authority);
754 if (status)
755 {
756 /* Fake folder_imap_close into closing the folder.
757 FIXME: The entire state machine should probably
758 be revised... */
759 f_imap->isopen++;
760 f_imap->state = IMAP_NO_STATE;
761 folder_imap_close (folder);
762 return status;
763 }
764 }
765
766 case IMAP_AUTH_DONE:
767 default:
768 break;
769 }
770 f_imap->state = IMAP_NO_STATE;
771 mu_monitor_wrlock (folder->monitor);
772 f_imap->isopen++;
773 mu_monitor_unlock (folder->monitor);
774 return 0;
775 }
776
777
778 /* Shutdown the connection. */
779 static int
780 folder_imap_close (mu_folder_t folder)
781 {
782 f_imap_t f_imap = folder->data;
783 int status = 0;
784
785 mu_monitor_wrlock (folder->monitor);
786 f_imap->isopen--;
787 if (f_imap->isopen)
788 {
789 mu_monitor_unlock (folder->monitor);
790 return 0;
791 }
792 mu_monitor_unlock (folder->monitor);
793
794 switch (f_imap->state)
795 {
796 case IMAP_NO_STATE:
797 status = imap_writeline (f_imap, "g%lu LOGOUT\r\n",
798 (unsigned long) f_imap->seq++);
799 CHECK_ERROR (f_imap, status);
800 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
801 f_imap->state = IMAP_LOGOUT;
802
803 case IMAP_LOGOUT:
804 status = imap_send (f_imap);
805 CHECK_EAGAIN (f_imap, status);
806 f_imap->state = IMAP_LOGOUT_ACK;
807
808 case IMAP_LOGOUT_ACK:
809 /* Check for "* Bye" from the imap server. */
810 status = imap_parse (f_imap);
811 CHECK_EAGAIN (f_imap, status);
812 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
813 /* This is done when we received the BYE in the parser code. */
814 /* mu_stream_close (folder->stream); */
815 /* f_imap->isopen = 0 ; */
816
817 default:
818 break;
819 }
820 f_imap->state = IMAP_NO_STATE;
821 f_imap->selected = NULL;
822 return 0;
823 }
824
825 /* Remove a mailbox. */
826 static int
827 folder_imap_delete (mu_folder_t folder, const char *name)
828 {
829 f_imap_t f_imap = folder->data;
830 int status = 0;
831
832 if (name == NULL)
833 return EINVAL;
834
835 status = mu_folder_open (folder, folder->flags);
836 if (status != 0)
837 return status;
838
839 switch (f_imap->state)
840 {
841 case IMAP_NO_STATE:
842 status = imap_writeline (f_imap, "g%lu DELETE %s\r\n",
843 (unsigned long) f_imap->seq++,
844 name);
845 CHECK_ERROR (f_imap, status);
846 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
847 f_imap->state = IMAP_DELETE;
848
849 case IMAP_DELETE:
850 status = imap_send (f_imap);
851 CHECK_EAGAIN (f_imap, status);
852 f_imap->state = IMAP_DELETE_ACK;
853
854 case IMAP_DELETE_ACK:
855 status = imap_parse (f_imap);
856 CHECK_EAGAIN (f_imap, status);
857 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
858
859 default:
860 break;
861 }
862 f_imap->state = IMAP_NO_STATE;
863 return status;
864 }
865
866 void
867 guess_level (struct mu_list_response *resp, size_t prefix_len)
868 {
869 size_t lev = 0;
870
871 if (!resp->separator)
872 lev = 0;
873 else
874 {
875 char *p = resp->name + prefix_len;
876 if (p[0] == resp->separator)
877 for ( ; p; p = strchr (p + 1, resp->separator))
878 lev++;
879 }
880 resp->level = lev;
881 }
882
883 /* Moves all matching items from list DST to SRC.
884 Items are moved verbatim (i.e. pointers are moved). Non-matching
885 items are deleted. After calling this function, SRC must be
886 destroyed.
887
888 While moving, this function also computes the recursion level.
889
890 Matching is determined based on PATTERN, by NAMECMP function,
891 and MAX_LEVEL. Both can be zero. */
892
893 static void
894 list_copy (mu_list_t dst, mu_list_t src,
895 size_t prefix_len,
896 int (*namecmp) (const char* pattern, const char* mailbox),
897 const char *pattern, size_t max_level)
898 {
899 mu_iterator_t itr;
900
901 if (!src)
902 return;
903
904 mu_list_get_iterator (src, &itr);
905 for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
906 mu_iterator_next (itr))
907 {
908 char *name;
909 struct mu_list_response *p;
910 mu_iterator_current (itr, (void **)&p);
911 guess_level (p, prefix_len);
912 name = p->name + prefix_len;
913 if (name[0] == p->separator && pattern[0] != p->separator)
914 name++;
915 if ((max_level == 0 || p->level <= max_level)
916 && (!namecmp || namecmp (pattern, name) == 0))
917 mu_list_append (dst, p);
918 else
919 free (p);
920 }
921 mu_iterator_destroy (&itr);
922 mu_list_set_destroy_item (src, NULL);
923 }
924
925 /* Convert glob(3)-style pattern to IMAP one
926 Rules:
927 Wildcard Replace with
928 -------- ------------
929 * * for recursive searches, % otherwise
930 ? %
931 [..] %
932
933 NOTE:
934 1. The '*' can be made more selective by taking into account the
935 required maximum recursion level and counting directory separators
936 ('/') in the input pattern.
937 2. The resulting pattern matches, in general, a wider set of strings, so
938 each matched string should be additionally compared against the
939 original pattern.
940 */
941 char *
942 glob_to_imap (const char *pat, int recursive)
943 {
944 char *p, *q;
945 char *ret = strdup (pat);
946
947 if (!ret)
948 return NULL;
949
950 for (p = q = ret; *q; )
951 {
952 switch (*q)
953 {
954 case '?':
955 *p++ = '%';
956 q++;
957 break;
958
959 case '*':
960 *p++ = recursive ? '*' : '%';
961 q++;
962 break;
963
964 case '[':
965 for (; *q; q++)
966 if (*q == '\\')
967 q++;
968 else if (*q == ']')
969 {
970 q++;
971 break;
972 }
973 *p++ = '%';
974 break;
975
976 case '\\':
977 q++;
978 if (*q)
979 *p++ = *q++;
980 break;
981
982 default:
983 *p++ = *q++;
984 break;
985 }
986 }
987 *p = 0;
988 return ret;
989 }
990
991 /* FIXME: Flags unused */
992 static int
993 folder_imap_list (mu_folder_t folder, const char *ref, void *name,
994 int flags, size_t max_level,
995 mu_list_t flist,
996 mu_folder_enumerate_fp efp, void *edp)
997 {
998 f_imap_t f_imap = folder->data;
999 int status = 0;
1000 char *path = NULL;
1001
1002 status = mu_folder_open (folder, folder->flags);
1003 if (status != 0)
1004 return status;
1005
1006 if (ref == NULL)
1007 ref = "";
1008 if (name == NULL)
1009 name = "";
1010
1011 f_imap->enum_fun = efp;
1012 f_imap->enum_stop = 0;
1013 f_imap->enum_data = edp;
1014
1015 switch (f_imap->state)
1016 {
1017 case IMAP_NO_STATE:
1018 path = glob_to_imap (name, max_level != 1);
1019 status = imap_writeline (f_imap, "g%lu LIST \"%s\" \"%s\"\r\n",
1020 (unsigned long) f_imap->seq++, ref, path);
1021 free (path);
1022 CHECK_ERROR (f_imap, status);
1023 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
1024 f_imap->state = IMAP_LIST;
1025
1026 case IMAP_LIST:
1027 status = imap_send (f_imap);
1028 CHECK_EAGAIN (f_imap, status);
1029 f_imap->state = IMAP_LIST_ACK;
1030
1031 case IMAP_LIST_ACK:
1032 status = imap_parse (f_imap);
1033 CHECK_EAGAIN (f_imap, status);
1034 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
1035
1036 default:
1037 break;
1038 }
1039
1040 f_imap->enum_fun = NULL;
1041 f_imap->enum_stop = 0;
1042 f_imap->enum_data = NULL;
1043
1044 list_copy (flist, f_imap->flist, strlen (ref),
1045 imap_mailbox_name_match, name, max_level);
1046
1047 mu_list_destroy (&f_imap->flist);
1048 f_imap->state = IMAP_NO_STATE;
1049 return status;
1050 }
1051
1052 static int
1053 folder_imap_lsub (mu_folder_t folder, const char *ref, const char *name,
1054 mu_list_t flist)
1055 {
1056 f_imap_t f_imap = folder->data;
1057 int status = 0;
1058
1059 status = mu_folder_open (folder, folder->flags);
1060 if (status != 0)
1061 return status;
1062
1063 if (ref == NULL)
1064 ref = "";
1065 if (name == NULL)
1066 name = "";
1067
1068 f_imap->enum_fun = NULL;
1069 f_imap->enum_stop = 0;
1070 f_imap->enum_data = NULL;
1071
1072 switch (f_imap->state)
1073 {
1074 case IMAP_NO_STATE:
1075 status = imap_writeline (f_imap, "g%lu LSUB \"%s\" \"%s\"\r\n",
1076 (unsigned long) f_imap->seq++, ref, name);
1077 CHECK_ERROR (f_imap, status);
1078 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
1079 f_imap->state = IMAP_LSUB;
1080
1081 case IMAP_LSUB:
1082 status = imap_send (f_imap);
1083 CHECK_EAGAIN (f_imap, status);
1084 f_imap->state = IMAP_LSUB_ACK;
1085
1086 case IMAP_LSUB_ACK:
1087 status = imap_parse (f_imap);
1088 CHECK_EAGAIN (f_imap, status);
1089 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
1090
1091 default:
1092 break;
1093 }
1094
1095 /* Build the folder list. */
1096 list_copy (flist, f_imap->flist, strlen (ref), NULL, NULL, 0);
1097 mu_list_destroy (&f_imap->flist);
1098
1099 f_imap->state = IMAP_NO_STATE;
1100 return 0;
1101 }
1102
1103 static int
1104 folder_imap_rename (mu_folder_t folder, const char *oldpath,
1105 const char *newpath)
1106 {
1107 f_imap_t f_imap = folder->data;
1108 int status = 0;
1109
1110 if (oldpath == NULL || newpath == NULL)
1111 return EINVAL;
1112
1113 status = mu_folder_open (folder, folder->flags);
1114 if (status != 0)
1115 return status;
1116
1117 switch (f_imap->state)
1118 {
1119 case IMAP_NO_STATE:
1120 status = imap_writeline (f_imap, "g%lu RENAME %s %s\r\n",
1121 (unsigned long) f_imap->seq++,
1122 oldpath, newpath);
1123 CHECK_ERROR (f_imap, status);
1124 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
1125 f_imap->state = IMAP_RENAME;
1126
1127 case IMAP_RENAME:
1128 status = imap_send (f_imap);
1129 CHECK_EAGAIN (f_imap, status);
1130 f_imap->state = IMAP_RENAME_ACK;
1131
1132 case IMAP_RENAME_ACK:
1133 status = imap_parse (f_imap);
1134 CHECK_EAGAIN (f_imap, status);
1135 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
1136
1137 default:
1138 break;
1139 }
1140 f_imap->state = IMAP_NO_STATE;
1141 return status;
1142 }
1143
1144 static int
1145 folder_imap_subscribe (mu_folder_t folder, const char *name)
1146 {
1147 f_imap_t f_imap = folder->data;
1148 int status = 0;
1149
1150 status = mu_folder_open (folder, folder->flags);
1151 if (status != 0)
1152 return status;
1153
1154 if (name == NULL)
1155 return EINVAL;
1156 switch (f_imap->state)
1157 {
1158 case IMAP_NO_STATE:
1159 status = imap_writeline (f_imap, "g%lu SUBSCRIBE %s\r\n",
1160 (unsigned long) f_imap->seq++, name);
1161 CHECK_ERROR (f_imap, status);
1162 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
1163 f_imap->state = IMAP_SUBSCRIBE;
1164
1165 case IMAP_SUBSCRIBE:
1166 status = imap_send (f_imap);
1167 CHECK_EAGAIN (f_imap, status);
1168 f_imap->state = IMAP_SUBSCRIBE_ACK;
1169
1170 case IMAP_SUBSCRIBE_ACK:
1171 status = imap_parse (f_imap);
1172 CHECK_EAGAIN (f_imap, status);
1173 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
1174
1175 default:
1176 break;
1177 }
1178 f_imap->state = IMAP_NO_STATE;
1179 return status;
1180 }
1181
1182 static int
1183 folder_imap_unsubscribe (mu_folder_t folder, const char *name)
1184 {
1185 f_imap_t f_imap = folder->data;
1186 int status = 0;
1187
1188 status = mu_folder_open (folder, folder->flags);
1189 if (status != 0)
1190 return status;
1191
1192 if (name == NULL)
1193 return EINVAL;
1194 switch (f_imap->state)
1195 {
1196 case IMAP_NO_STATE:
1197 status = imap_writeline (f_imap, "g%lu UNSUBSCRIBE %s\r\n",
1198 (unsigned long) f_imap->seq++, name);
1199 CHECK_ERROR (f_imap, status);
1200 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
1201 f_imap->state = IMAP_UNSUBSCRIBE;
1202
1203 case IMAP_UNSUBSCRIBE:
1204 status = imap_send (f_imap);
1205 CHECK_EAGAIN (f_imap, status);
1206 f_imap->state = IMAP_UNSUBSCRIBE_ACK;
1207
1208 case IMAP_UNSUBSCRIBE_ACK:
1209 status = imap_parse (f_imap);
1210 CHECK_EAGAIN (f_imap, status);
1211 MU_DEBUG (folder->debug, MU_DEBUG_PROT, f_imap->buffer);
1212
1213 default:
1214 break;
1215 }
1216 f_imap->state = IMAP_NO_STATE;
1217 return status;
1218 }
1219
1220 /* A literal is a sequence of zero or more octets (including CR and LF),
1221 prefix-quoted with an octet count in the form of an open brace ("{"),
1222 the number of octets, close brace ("}"), and CRLF. The sequence is read
1223 and put in the string buffer. */
1224 static int
1225 imap_literal_string (f_imap_t f_imap, char **ptr)
1226 {
1227 size_t len, len0, total;
1228 int status = 0;
1229 int nl;
1230
1231 if (f_imap->string.nleft==0)
1232 {
1233 status = imap_readline (f_imap);
1234 *ptr = f_imap->buffer;
1235 return status;
1236 }
1237
1238 /* The (len + 1) in the for is to count the strip '\r' by imap_readline. */
1239 for (len0 = len = total = 0; total < f_imap->string.nleft; total += len + 1)
1240 {
1241 status = imap_readline (f_imap);
1242 if (DEBUG_SHOW_DATA)
1243 fprintf (stderr, "%s", f_imap->buffer);
1244 if (status != 0)
1245 {
1246 /* Return what we got so far. */
1247 break;
1248 }
1249 f_imap->ptr = f_imap->buffer;
1250
1251 /* How much ? */
1252 len0 = len = f_imap->nl - f_imap->buffer;
1253 /* Check if the last read did not finish on a line, if yes do not copy in
1254 string buffer the terminating sequence ")\r\n". We are doing this
1255 by checking if the amount(total) we got so far + the len of the line
1256 +1 (taking to account the strip '\r') goes behond the request. */
1257 if ((total + len + 1) > f_imap->string.nleft)
1258 {
1259 len0 = len = f_imap->string.nleft - total;
1260 /* ALERT: if we ask for a substring, for example we have :
1261 "123456\n", and ask for body[]<0.7> the server will send
1262 body[] {7} --> "123456\r". There was not enough space
1263 to fit the nl .. annoying. Take care of this here. */
1264 if (f_imap->buffer[len - 1] == '\r')
1265 len0--;
1266 }
1267
1268 mu_stream_write (f_imap->string.stream, f_imap->buffer,
1269 len0, f_imap->string.offset, NULL);
1270 f_imap->string.offset += len0;
1271
1272 /* Depending on the type of request we incremente the xxxx_lines
1273 and xxxx_sizes. */
1274 nl = (memchr (f_imap->buffer, '\n', len0)) ? 1 : 0;
1275 if (f_imap->string.msg_imap)
1276 {
1277 switch (f_imap->string.type)
1278 {
1279 case IMAP_HEADER:
1280 f_imap->string.msg_imap->header_lines += nl;
1281 f_imap->string.msg_imap->header_size += len0;
1282 break;
1283
1284 case IMAP_BODY:
1285 f_imap->string.msg_imap->body_lines += nl;
1286 f_imap->string.msg_imap->body_size += len0;
1287 break;
1288
1289 case IMAP_MESSAGE:
1290 f_imap->string.msg_imap->mu_message_lines += nl;
1291 /* The message size is known by sending RFC822.SIZE. */
1292
1293 default:
1294 break;
1295 }
1296 }
1297 }
1298 f_imap->string.nleft -= total;
1299 /* We may have trailing junk like the closing ")\r\n" from a literal string
1300 glob it by moving the command buffer, or doing a full readline. */
1301 if (len == (size_t)(f_imap->nl - f_imap->buffer))
1302 {
1303 len = 0;
1304 status = imap_readline (f_imap);
1305 }
1306 *ptr = f_imap->buffer + len;
1307 return status;
1308 }
1309
1310 /* A quoted string is a sequence of zero or more 7-bit characters,
1311 excluding CR and LF, with double quote (<">) characters at each end.
1312 Same thing as the literal, diferent format the result is put in the
1313 string buffer for the mailbox/callee. */
1314 static int
1315 imap_quoted_string (f_imap_t f_imap, char **ptr)
1316 {
1317 char *bquote;
1318 int escaped = 0;
1319 int len;
1320
1321 (*ptr)++;
1322 bquote = *ptr;
1323 while (**ptr && (**ptr != '"' || escaped))
1324 {
1325 escaped = (**ptr == '\\') ? 1 : 0;
1326 (*ptr)++;
1327 }
1328
1329 len = *ptr - bquote;
1330 mu_stream_write (f_imap->string.stream, bquote, len,
1331 f_imap->string.offset, NULL);
1332 f_imap->string.offset += len;
1333 if (**ptr == '"')
1334 (*ptr)++;
1335 if (DEBUG_SHOW_DATA)
1336 fprintf (stderr, "%.*s", len, bquote);
1337 return 0;
1338 }
1339
1340 /* A number consists of one or more digit characters, and represents a
1341 numeric value. */
1342
1343 static int
1344 imap_digits (f_imap_t f_imap, char **ptr)
1345 {
1346 char *start = *ptr;
1347 int len;
1348
1349 for (++*ptr; **ptr && mu_isdigit(**ptr); ++*ptr)
1350 ;
1351 len = *ptr - start;
1352 mu_stream_write (f_imap->string.stream, start, len,
1353 f_imap->string.offset, NULL);
1354 f_imap->string.offset += len;
1355 return 0;
1356 }
1357
1358 /* Find which type of string the response is: literal or quoted and let the
1359 function fill the string buffer. */
1360 static int
1361 imap_string (f_imap_t f_imap, char **ptr)
1362 {
1363 int status = 0;
1364
1365 /* Skip whites. */
1366 while (**ptr == ' ')
1367 (*ptr)++;
1368 switch (**ptr)
1369 {
1370 case '{':
1371 f_imap->string.nleft = strtol ((*ptr) + 1, ptr, 10);
1372 if (**ptr == '}')
1373 {
1374 (*ptr)++;
1375 /* Reset the buffer to the beginning. */
1376 f_imap->ptr = f_imap->buffer;
1377 status = imap_literal_string (f_imap, ptr);
1378 }
1379 break;
1380
1381 case '"':
1382 status = imap_quoted_string (f_imap, ptr);
1383 break;
1384
1385 /* NIL */
1386 case 'N':
1387 case 'n':
1388 (*ptr)++; /* N|n */
1389 (*ptr)++; /* I|i */
1390 (*ptr)++; /* L|l */
1391 break;
1392
1393 default:
1394 if (mu_isdigit (**ptr))
1395 status = imap_digits (f_imap, ptr);
1396 else
1397 /* Problem. FIXME: Return a more appropriate error code */
1398 status = MU_ERR_FAILURE;
1399 break;
1400 }
1401 return status;
1402 }
1403
1404 /* FIXME: does not work for nonblocking. */
1405 static int
1406 imap_list (f_imap_t f_imap)
1407 {
1408 char *tok;
1409 char *sp = NULL;
1410 size_t len = f_imap->nl - f_imap->buffer - 1;
1411 char *buffer;
1412 struct mu_list_response *lr;
1413 int status = 0;
1414 int argc;
1415 char **argv;
1416
1417 if (f_imap->enum_stop)
1418 return 0;
1419
1420 buffer = malloc (len + 1);
1421 if (!buffer)
1422 return ENOMEM;
1423 memcpy (buffer, f_imap->buffer, len);
1424 buffer[len] = '\0';
1425
1426 lr = calloc (1, sizeof (*lr));
1427 if (!lr)
1428 return ENOMEM;
1429
1430 if (!f_imap->flist)
1431 {
1432 mu_list_create (&f_imap->flist);
1433 mu_list_set_destroy_item (f_imap->flist, mu_list_response_free);
1434 }
1435
1436 /* Glob untag. */
1437 tok = strtok_r (buffer, " ", &sp);
1438 /* Glob LIST. */
1439 tok = strtok_r (NULL, " ", &sp);
1440 /* Get the attibutes. */
1441 tok = strtok_r (NULL, ")", &sp);
1442 if (tok)
1443 {
1444 char *s = NULL;
1445 char *p = tok;
1446 while ((tok = strtok_r (p, " ()", &s)) != NULL)
1447 {
1448 if (mu_c_strcasecmp (tok, "\\Noselect") == 0)
1449 lr->type |= MU_FOLDER_ATTRIBUTE_DIRECTORY;
1450 else if (mu_c_strcasecmp (tok, "\\Noinferiors") == 0)
1451 lr->type |= MU_FOLDER_ATTRIBUTE_FILE;
1452 else if (mu_c_strcasecmp (tok, "\\Marked") == 0
1453 || mu_c_strcasecmp (tok, "\\Unmarked") == 0)
1454 /* nothing */;
1455 else
1456 lr->type |= MU_FOLDER_ATTRIBUTE_DIRECTORY;
1457 p = NULL;
1458 }
1459 }
1460
1461 status = mu_argcv_get (sp, "", NULL, &argc, &argv);
1462 if (status == 0)
1463 {
1464 char *s;
1465
1466 /* Hiearchy delimeter. */
1467 tok = argv[0];
1468 if (tok && tok[1] == 0 && mu_c_strcasecmp (tok, "NIL"))
1469 lr->separator = tok[0];
1470 /* The path. */
1471 tok = argv[1];
1472 s = strchr (tok, '{');
1473 if (s)
1474 {
1475 size_t n = strtoul (s + 1, NULL, 10);
1476 lr->name = calloc (n + 1, 1);
1477 if (!lr->name)
1478 status = ENOMEM;
1479 else
1480 {
1481 f_imap->ptr = f_imap->buffer;
1482 imap_readline (f_imap);
1483 memcpy (lr->name, f_imap->buffer, n);
1484 }
1485 }
1486 else if ((status = imap_string (f_imap, &tok)) == 0)
1487 {
1488 mu_off_t sz = 0;
1489
1490 mu_stream_size (f_imap->string.stream, &sz);
1491 lr->name = calloc (sz + 1, 1);
1492 if (!lr->name)
1493 status = ENOMEM;
1494 else
1495 mu_stream_read (f_imap->string.stream, lr->name, sz, 0, NULL);
1496 mu_stream_truncate (f_imap->string.stream, 0);
1497 f_imap->string.offset = 0;
1498 f_imap->string.nleft = 0;
1499 }
1500 else
1501 {
1502 lr->name = strdup (tok);
1503 if (!lr->name)
1504 status = ENOMEM;
1505 }
1506 if (lr->separator)
1507 {
1508 size_t off;
1509 char delim[2];
1510 size_t n = 0;
1511
1512 delim[0] = lr->separator;
1513 delim[1] = 0;
1514 s = lr->name;
1515 while (off = strcspn (s, delim), s[off])
1516 {
1517 n++;
1518 off++;
1519 s += off;
1520 }
1521 lr->level = n;
1522 }
1523 }
1524 mu_argcv_free (argc, argv);
1525 free (buffer);
1526
1527 if (f_imap->enum_fun)
1528 f_imap->enum_stop = f_imap->enum_fun (f_imap->folder, lr,
1529 f_imap->enum_data);
1530 mu_list_append (f_imap->flist, lr);
1531
1532 return status;
1533 }
1534
1535 /* Helping function to figure out the section name of the message: for example
1536 a 2 part message with the first part being sub in two will be:
1537 {1}, {1,1} {1,2} The first subpart of the message and its sub parts
1538 {2} The second subpar of the message. */
1539 char *
1540 section_name (msg_imap_t msg_imap)
1541 {
1542 size_t sectionlen = 0;
1543 char *section = strdup ("");
1544
1545 /* Build the section name, but it is in reverse. */
1546 for (; msg_imap; msg_imap = msg_imap->parent)
1547 {
1548 if (msg_imap->part != 0)
1549 {
1550 char *tmp;
1551 char part[64];
1552 size_t partlen;
1553 snprintf (part, sizeof part, "%lu", (unsigned long) msg_imap->part);
1554 partlen = strlen (part);
1555 tmp = realloc (section, sectionlen + partlen + 2);
1556 if (tmp == NULL)
1557 break;
1558 section = tmp;
1559 memset (section + sectionlen, '\0', partlen + 2);
1560 if (sectionlen != 0)
1561 strcat (section, ".");
1562 strcat (section, part);
1563 sectionlen = strlen (section);
1564 }
1565 }
1566
1567 /* Reverse the string. */
1568 if (section)
1569 {
1570 char *begin, *last;
1571 char c;
1572 for (begin = section, last = section + sectionlen - 1; begin < last;
1573 begin++, last--)
1574 {
1575 c = *begin;
1576 *begin = *last;
1577 *last = c;
1578 }
1579 }
1580 return section;
1581 }
1582
1583 /* We do not pay particular attention to the content of the bodystructure
1584 but rather to the paremetized list layout to discover how many messages
1585 the originial message is composed of. The information is later retrieve
1586 when needed via the body[header.fields] command. Note that this function
1587 is recursive. */
1588 static int
1589 imap_bodystructure0 (msg_imap_t msg_imap, char **ptr)
1590 {
1591 int paren = 0;
1592 int no_arg = 0;
1593 int status = 0;
1594 int have_size = 0;
1595
1596 /* Skip space. */
1597 while (**ptr == ' ')
1598 (*ptr)++;
1599 /* Pass lparen. */
1600 if (**ptr == '(')
1601 {
1602 ++(*ptr);
1603 paren++;
1604 no_arg++;
1605 }
1606 /* NOTE : this loop has side effects in strtol() and imap_string(), the
1607 order of the if's are important. */
1608 while (**ptr)
1609 {
1610 /* Skip the string argument. */
1611 if (**ptr != '(' && **ptr != ')')
1612 {
1613 char *start = *ptr;
1614 /* FIXME: set the command callback if EAGAIN to resume. */
1615 status = imap_string (msg_imap->m_imap->f_imap, ptr);
1616 if (status != 0)
1617 return status;
1618 if (start != *ptr)
1619 no_arg = 0;
1620 }
1621
1622 if (mu_isdigit ((unsigned)**ptr))
1623 {
1624 char *start = *ptr;
1625 size_t size = strtoul (*ptr, ptr, 10);
1626 if (start != *ptr)
1627 {
1628 if (!have_size && msg_imap && msg_imap->parent)
1629 msg_imap->mu_message_size = size;
1630 have_size = 1;
1631 no_arg = 0;
1632 }
1633 }
1634
1635 if (**ptr == '(')
1636 {
1637 if (no_arg)
1638 {
1639 msg_imap_t new_part;
1640 msg_imap_t *tmp;
1641 tmp = realloc (msg_imap->parts,
1642 ((msg_imap->num_parts + 1) * sizeof (*tmp)));
1643 if (tmp)
1644 {
1645 new_part = calloc (1, sizeof (*new_part));
1646 if (new_part)
1647 {
1648 msg_imap->parts = tmp;
1649 msg_imap->parts[msg_imap->num_parts] = new_part;
1650 new_part->part = ++(msg_imap->num_parts);
1651 new_part->parent = msg_imap;
1652 new_part->num = msg_imap->num;
1653 new_part->m_imap = msg_imap->m_imap;
1654 new_part->flags = msg_imap->flags;
1655 status = imap_bodystructure0 (new_part, ptr);
1656 /* Jump up, the rparen been swallen already. */
1657 continue;
1658 }
1659 else
1660 {
1661 status = ENOMEM;
1662 free (tmp);
1663 break;
1664 }
1665 }
1666 else
1667 {
1668 status = ENOMEM;
1669 break;
1670 }
1671 }
1672 paren++;
1673 }
1674
1675 if (**ptr == ')')
1676 {
1677 no_arg = 1;
1678 paren--;
1679 /* Did we reach the same number of close paren ? */
1680 if (paren <= 0)
1681 {
1682 /* Swallow the rparen. */
1683 (*ptr)++;
1684 break;
1685 }
1686 }
1687
1688 if (**ptr == '\0')
1689 break;
1690
1691 (*ptr)++;
1692 }
1693 return status;
1694 }
1695
1696 static int
1697 imap_bodystructure (f_imap_t f_imap, char **ptr)
1698 {
1699 return imap_bodystructure0 (f_imap->string.msg_imap, ptr);
1700 }
1701
1702 /* The Format for a FLAG response is :
1703 mailbox_data ::= "FLAGS" SPACE flag_list
1704 flag_list ::= "(" #flag ")"
1705 flag ::= "\Answered" / "\Flagged" / "\Deleted" /
1706 "\Seen" / "\Draft" / flag_keyword / flag_extension
1707 flag_extension ::= "\" atom
1708 ;; Future expansion. Client implementations
1709 ;; MUST accept flag_extension flags. Server
1710 ;; implementations MUST NOT generate
1711 ;; flag_extension flags except as defined by
1712 ;; future standard or standards-track
1713 ;; revisions of this specification.
1714 flag_keyword ::= atom
1715
1716 S: * 14 FETCH (FLAGS (\Seen \Deleted))
1717 S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
1718
1719 We assume that the '*' or the FLAGS keyword are strip.
1720
1721 FIXME: User flags are not take to account. */
1722 static int
1723 imap_fetch_flags (f_imap_t f_imap, char **ptr)
1724 {
1725 msg_imap_t msg_imap = f_imap->string.msg_imap;
1726 if (msg_imap)
1727 imap_flags (ptr, &msg_imap->flags);
1728 return 0;
1729 }
1730
1731 static int
1732 imap_permanentflags (f_imap_t f_imap, char **ptr)
1733 {
1734 imap_flags (ptr, &f_imap->flags);
1735 return 0;
1736 }
1737
1738 static int
1739 imap_flags (char **ptr, int *pflags)
1740 {
1741 char *start;
1742 char *end;
1743 int flags = 0;
1744
1745 /* Skip space. */
1746 while (**ptr == ' ')
1747 (*ptr)++;
1748
1749 /* Skip LPAREN. */
1750 if (**ptr == '(')
1751 (*ptr)++;
1752
1753 /* Go through the list and break on ')' */
1754 do
1755 {
1756 /* Skip space before next word. */
1757 while (**ptr == ' ')
1758 (*ptr)++;
1759
1760 /* Save the beginning of the word. */
1761 start = *ptr;
1762 /* Get the next word boundary. */
1763 while (**ptr && **ptr != ' ' && **ptr != ')')
1764 ++(*ptr);
1765
1766 /* Save the end for the mu_c_strcasecmp. */
1767 end = *ptr;
1768
1769 /* Bail out. */
1770 if (*start == '\0')
1771 break;
1772
1773 /* Guess the flag. */
1774 if (end == start)
1775 flags |= MU_ATTRIBUTE_SEEN;
1776 else
1777 {
1778 if (mu_c_strncasecmp (start, "\\Seen", end - start) == 0)
1779 {
1780 flags |= MU_ATTRIBUTE_READ;
1781 }
1782 else if (mu_c_strncasecmp (start, "\\Answered", end - start) == 0)
1783 {
1784 flags |= MU_ATTRIBUTE_ANSWERED;
1785 }
1786 else if (mu_c_strncasecmp (start, "\\Flagged", end - start) == 0)
1787 {
1788 flags |= MU_ATTRIBUTE_FLAGGED;
1789 }
1790 else if (mu_c_strncasecmp (start, "\\Deleted", end - start) == 0)
1791 {
1792 flags |= MU_ATTRIBUTE_DELETED;
1793 }
1794 else if (mu_c_strncasecmp (start, "\\Draft", end - start) == 0)
1795 {
1796 flags |= MU_ATTRIBUTE_DRAFT;
1797 }
1798 else if (mu_c_strncasecmp (start, "\\Recent", end - start))
1799 flags |= MU_ATTRIBUTE_SEEN;
1800 }
1801 }
1802 while (**ptr && **ptr != ')'); /* do {} */
1803
1804 /* Skip the last rparen. */
1805 if (**ptr == ')')
1806 (*ptr)++;
1807
1808 if (pflags)
1809 *pflags = flags;
1810 return 0;
1811 }
1812
1813 static int
1814 imap_body (f_imap_t f_imap, char **ptr)
1815 {
1816 int status;
1817
1818 /* Skip leading spaces. */
1819 while (**ptr && **ptr == ' ')
1820 (*ptr)++;
1821
1822 if (**ptr == '[')
1823 {
1824 char *sep = strchr (*ptr, ']');
1825 (*ptr)++; /* Move past the '[' */
1826 if (sep)
1827 {
1828 size_t len = sep - *ptr;
1829 char *section = malloc (len + 1);
1830
1831 if (!section)
1832 return ENOMEM;
1833
1834 strncpy (section, *ptr, len);
1835 section[len] = '\0';
1836 /* strupper. */
1837 mu_strupper (section);
1838
1839 /* Set the string type to update the correct line count. */
1840 /*if (!strstr (section, "FIELD"))*/
1841 {
1842 if (strstr (section, "MIME") || (strstr (section, "HEADER")))
1843 {
1844 f_imap->string.type = IMAP_HEADER;
1845 }
1846 else if (strstr (section, "TEXT") || len > 0)
1847 {
1848 f_imap->string.type = IMAP_BODY;
1849 }
1850 else if (len == 0) /* body[] */
1851 {
1852 f_imap->string.type = IMAP_MESSAGE;
1853 }
1854 }
1855 free (section);
1856 sep++; /* Move past the ']' */
1857 *ptr = sep;
1858 }
1859 }
1860 while (**ptr && **ptr == ' ')
1861 (*ptr)++;
1862 if (**ptr == '<')
1863 {
1864 char *sep = strchr (*ptr, '>');
1865 if (sep)
1866 {
1867 sep++;
1868 *ptr = sep;
1869 }
1870 }
1871 status = imap_string (f_imap, ptr);
1872
1873 /* If the state scan. Catch it here. */
1874 if (f_imap->state == IMAP_SCAN_ACK)
1875 {
1876 char *buffer;
1877 mu_off_t total = 0;
1878 if (f_imap->string.msg_imap && f_imap->string.msg_imap->fheader)
1879 mu_header_destroy (&f_imap->string.msg_imap->fheader, NULL);
1880 mu_stream_size (f_imap->string.stream, &total);
1881 buffer = malloc (total + 1);
1882 mu_stream_read (f_imap->string.stream, buffer, total, 0, NULL);
1883 status = mu_header_create (&f_imap->string.msg_imap->fheader,
1884 buffer, total, NULL);
1885 free (buffer);
1886 mu_stream_truncate (f_imap->string.stream, 0);
1887 f_imap->string.offset = 0;
1888 f_imap->string.nleft = 0;
1889 }
1890 return status;
1891 }
1892
1893 static int
1894 imap_internaldate (f_imap_t f_imap, char **ptr)
1895 {
1896 return imap_string (f_imap, ptr);
1897 }
1898
1899 static int
1900 imap_uid (f_imap_t f_imap, char **ptr)
1901 {
1902 char token[128];
1903 imap_token (token, sizeof token, ptr);
1904 if (f_imap->string.msg_imap)
1905 f_imap->string.msg_imap->uid = strtoul (token, NULL, 10);
1906 return 0;
1907 }
1908
1909 static int
1910 imap_rfc822 (f_imap_t f_imap, char **ptr)
1911 {
1912 int status;
1913 f_imap->string.type = IMAP_MESSAGE;
1914 status = imap_body (f_imap, ptr);
1915 f_imap->string.type = 0;
1916 return status;
1917 }
1918
1919 static int
1920 imap_rfc822_size (f_imap_t f_imap, char **ptr)
1921 {
1922 char token[128];
1923 imap_token (token, sizeof token, ptr);
1924 if (f_imap->string.msg_imap)
1925 f_imap->string.msg_imap->mu_message_size = strtoul (token, NULL, 10);
1926 return 0;
1927 }
1928
1929 static int
1930 imap_rfc822_header (f_imap_t f_imap, char **ptr)
1931 {
1932 int status;
1933 f_imap->string.type = IMAP_HEADER;
1934 status = imap_string (f_imap, ptr);
1935 f_imap->string.type = 0;
1936 return status;
1937 }
1938
1939 static int
1940 imap_rfc822_text (f_imap_t f_imap, char **ptr)
1941 {
1942 int status;
1943 f_imap->string.type = IMAP_HEADER;
1944 status = imap_string (f_imap, ptr);
1945 f_imap->string.type = 0;
1946 return status;
1947 }
1948
1949 /* Parse imap unfortunately FETCH is use as response for many commands.
1950 We just use a small set an ignore the other ones :
1951 not use : ALL
1952 use : BODY
1953 not use : BODY[<section>]<<partial>>
1954 use : BODY.PEEK[<section>]<<partial>>
1955 not use : BODYSTRUCTURE
1956 not use : ENVELOPE
1957 not use : FAST
1958 use : FLAGS
1959 not use : FULL
1960 use : INTERNALDATE
1961 not use : RFC822
1962 not use : RFC822.HEADER
1963 use : RFC822.SIZE
1964 not use : RFC822.TEXT
1965 not use : UID
1966 */
1967 static int
1968 imap_fetch (f_imap_t f_imap)
1969 {
1970 char token[128];
1971 size_t msgno = 0;
1972 m_imap_t m_imap = f_imap->selected;
1973 int status = 0;
1974 char *sp = NULL;
1975
1976 /* We should have a mailbox selected. */
1977 assert (m_imap != NULL);
1978
1979 /* Parse the FETCH respones to extract the pieces. */
1980 sp = f_imap->buffer;
1981
1982 /* Skip untag '*'. */
1983 imap_token (token, sizeof token, &sp);
1984 /* Get msgno. */
1985 imap_token (token, sizeof token, &sp);
1986 msgno = strtol (token, NULL, 10);
1987 /* Skip FETCH . */
1988 imap_token (token, sizeof token, &sp);
1989
1990 /* It is actually possible, but higly unlikely that we do not have the
1991 message yet, for example a "FETCH (FLAGS (\Recent))" notification
1992 for a newly messsage. */
1993 if (f_imap->string.msg_imap == NULL
1994 || f_imap->string.msg_imap->num != msgno)
1995 {
1996 /* Find the imap mesg struct. */
1997 size_t i;
1998 mu_message_t msg = NULL;
1999 mu_mailbox_get_message (m_imap->mailbox, msgno, &msg);
2000 for (i = 0; i < m_imap->imessages_count; i++)
2001 {
2002 if (m_imap->imessages[i] && m_imap->imessages[i]->num == msgno)
2003 {
2004 f_imap->string.msg_imap = m_imap->imessages[i];
2005 break;
2006 }
2007 }
2008 /* mu_message_destroy (&msg); */
2009 }
2010
2011 while (*sp && *sp != ')')
2012 {
2013 /* Get the token. */
2014 imap_token (token, sizeof token, &sp);
2015
2016 if (strncmp (token, "FLAGS", 5) == 0)
2017 {
2018 status = imap_fetch_flags (f_imap, &sp);
2019 }
2020 else if (mu_c_strcasecmp (token, "BODY") == 0)
2021 {
2022 if (*sp == '[')
2023 status = imap_body (f_imap, &sp);
2024 else
2025 status = imap_bodystructure (f_imap, &sp);
2026 }
2027 else if (mu_c_strcasecmp (token, "BODYSTRUCTURE") == 0)
2028 {
2029 status = imap_bodystructure (f_imap, &sp);
2030 }
2031 else if (strncmp (token, "INTERNALDATE", 12) == 0)
2032 {
2033 status = imap_internaldate (f_imap, &sp);
2034 }
2035 else if (strncmp (token, "RFC822", 10) == 0)
2036 {
2037 if (*sp == '.')
2038 {
2039 sp++;
2040 imap_token (token, sizeof token, &sp);
2041 if (mu_c_strcasecmp (token, "SIZE") == 0)
2042 {
2043 status = imap_rfc822_size (f_imap, &sp);
2044 }
2045 else if (mu_c_strcasecmp (token, "TEXT") == 0)
2046 {
2047 status = imap_rfc822_text (f_imap, &sp);
2048 }
2049 else if (mu_c_strcasecmp (token, "HEADER") == 0)
2050 {
2051 status = imap_rfc822_header (f_imap, &sp);
2052 }
2053 /* else mu_error (_("not supported RFC822 option")); */
2054 }
2055 else
2056 {
2057 status = imap_rfc822 (f_imap, &sp);
2058 }
2059 }
2060 else if (strncmp (token, "UID", 3) == 0)
2061 {
2062 status = imap_uid (f_imap, &sp);
2063 }
2064 /* else mu_error (_("not supported FETCH command")); */
2065 }
2066 return status;
2067 }
2068
2069 static int
2070 imap_search (f_imap_t f_imap MU_ARG_UNUSED)
2071 {
2072 /* Not implemented. No provision for this in the API, yet. */
2073 return 0;
2074 }
2075
2076 static int
2077 imap_status (f_imap_t f_imap MU_ARG_UNUSED)
2078 {
2079 /* Not implemented. No provision for this in the API, yet. */
2080 return 0;
2081 }
2082
2083 static int
2084 imap_expunge (f_imap_t f_imap MU_ARG_UNUSED, unsigned msgno MU_ARG_UNUSED)
2085 {
2086 /* We should not have this, since do not send the expunge, but rather
2087 use SELECT/CLOSE. */
2088 return 0;
2089 }
2090
2091
2092 /* This function will advance ptr to the next character that IMAP
2093 recognise as special: " .()[]<>" and put the result in buf which
2094 is of size len. */
2095 static int
2096 imap_token (char *buf, size_t len, char **ptr)
2097 {
2098 char *start = *ptr;
2099 size_t i;
2100 /* Skip leading space. */
2101 while (**ptr && **ptr == ' ')
2102 (*ptr)++;
2103 /* Break the string by token, when we recognise Atoms we stop. */
2104 for (i = 1; **ptr && i < len; (*ptr)++, buf++, i++)
2105 {
2106 if (**ptr == ' ' || **ptr == '.'
2107 || **ptr == '(' || **ptr == ')'
2108 || **ptr == '[' || **ptr == ']'
2109 || **ptr == '<' || **ptr == '>')
2110 {
2111 /* Advance. */
2112 if (start == (*ptr))
2113 (*ptr)++;
2114 break;
2115 }
2116 *buf = **ptr;
2117 }
2118 *buf = '\0';
2119 /* Skip trailing space. */
2120 while (**ptr && **ptr == ' ')
2121 (*ptr)++;
2122 return *ptr - start;;
2123 }
2124
2125 /* Checks to see if a mailbox name matches a pattern, treating
2126 INBOX case insensitively, as required (INBOX is a special
2127 name no matter what the case is).
2128 */
2129 static int
2130 imap_mailbox_name_match (const char* pattern, const char* mailbox)
2131 {
2132 if (mu_c_strcasecmp (pattern, "inbox") == 0)
2133 {
2134 return mu_c_strcasecmp (pattern, mailbox);
2135 }
2136 return fnmatch (pattern, mailbox, 0);
2137 }
2138
2139 /* C99 says that a conforming implementations of snprintf () should return the
2140 number of char that would have been call but many GNU/Linux && BSD
2141 implementations return -1 on error. Worse QnX/Neutrino actually does not
2142 put the terminal null char. So let's try to cope. */
2143 int
2144 imap_writeline (f_imap_t f_imap, const char *format, ...)
2145 {
2146 int len;
2147 va_list ap;
2148 int done = 1;
2149
2150 va_start(ap, format);
2151 do
2152 {
2153 va_list aq;
2154 va_copy(aq, ap);
2155 len = vsnprintf (f_imap->buffer, f_imap->buflen, format, aq);
2156 va_end(aq);
2157 if (len < 0 || len >= (int)f_imap->buflen
2158 || !memchr (f_imap->buffer, '\0', len + 1))
2159 {
2160 f_imap->buflen *= 2;
2161 f_imap->buffer = realloc (f_imap->buffer, f_imap->buflen);
2162 if (f_imap->buffer == NULL)
2163 return ENOMEM;
2164 done = 0;
2165 }
2166 else
2167 done = 1;
2168 }
2169 while (!done);
2170 va_end(ap);
2171 f_imap->ptr = f_imap->buffer + len;
2172
2173 if (DEBUG_SHOW_COMMAND)
2174 fprintf (stderr, "%s", f_imap->buffer);
2175 return 0;
2176 }
2177
2178 /* Cover to send requests. */
2179 int
2180 imap_send (f_imap_t f_imap)
2181 {
2182 int status = 0;
2183 if (f_imap->ptr > f_imap->buffer)
2184 {
2185 size_t len;
2186 size_t n = 0;
2187 len = f_imap->ptr - f_imap->buffer;
2188 status = mu_stream_write (f_imap->folder->stream, f_imap->buffer, len,
2189 0, &n);
2190 if (status == 0)
2191 {
2192 memmove (f_imap->buffer, f_imap->buffer + n, len - n);
2193 f_imap->ptr -= n;
2194 }
2195 }
2196 else
2197 f_imap->ptr = f_imap->buffer;
2198 return status;
2199 }
2200
2201 /* Read a complete line form the imap server. Transform CRLF to LF, put a null
2202 in the buffer when done. Note f_imap->offset is not really of any use
2203 but rather to keep the stream internal buffer scheme happy, so we have to
2204 be in sync with the stream. */
2205 int
2206 imap_readline (f_imap_t f_imap)
2207 {
2208 size_t n = 0;
2209 size_t total = f_imap->ptr - f_imap->buffer;
2210 int status;
2211
2212 /* Must get a full line before bailing out. */
2213 do
2214 {
2215 status = mu_stream_readline (f_imap->folder->stream, f_imap->buffer + total,
2216 f_imap->buflen - total, f_imap->offset, &n);
2217 if (status != 0)
2218 return status;
2219
2220 /* The server went away: It maybe a timeout and some imap server
2221 does not send the BYE. Consider like an error. */
2222 if (n == 0)
2223 return EIO;
2224
2225 total += n;
2226 f_imap->offset += n;
2227 f_imap->nl = memchr (f_imap->buffer, '\n', total);
2228 if (f_imap->nl == NULL) /* Do we have a full line. */
2229 {
2230 /* Allocate a bigger buffer ? */
2231 if (total >= f_imap->buflen -1)
2232 {
2233 f_imap->buflen *= 2;
2234 f_imap->buffer = realloc (f_imap->buffer, f_imap->buflen + 1);
2235 if (f_imap->buffer == NULL)
2236 return ENOMEM;
2237 }
2238 }
2239 f_imap->ptr = f_imap->buffer + total;
2240 }
2241 while (f_imap->nl == NULL);
2242
2243 /* Conversion \r\n --> \n\0 */
2244 /* FIXME: This should be done transparently by the TCP stream */
2245 if (f_imap->nl > f_imap->buffer && f_imap->nl[-1] == '\r')
2246 {
2247 *(f_imap->nl - 1) = '\n';
2248 *(f_imap->nl) = '\0';
2249 f_imap->ptr = f_imap->nl;
2250 }
2251 return 0;
2252 }
2253
2254 /*
2255 The parsing was inspired by this article form the BeNews channel: "BE
2256 ENGINEERING INSIGHTS: IMAP for the Masses." By Adam Haberlach adam@be.com
2257
2258 The server responses are in three forms: status responses, server data,
2259 and command continuation request, ...
2260 An untagged response is indicated by the token "*" instead of a tag.
2261 Untagged status responses indicate server greeting, or server status
2262 that does not indicate the completion of a command (for example, an
2263 impending system shutdown alert).
2264 ....
2265 The client MUST be prepared to accept any response at all times.
2266
2267 Status responses are OK, NO, BAD, PREAUTH and BYE. OK, NO, and BAD
2268 may be tagged or untagged. PREAUTH and BYE are always untagged.
2269
2270 Server Responses - Status Responses
2271 BAD *|tag
2272 BYE *
2273 NO *|tag
2274 OK *|tag
2275 PREAUTH *
2276
2277 The code for status responses are
2278 ALERT
2279 BADCHARSET(IMAPV)
2280 CAPABILITY(IMAPV)
2281 NEWNAME
2282 PARSE
2283 PERMANENTFLAGS
2284 READ-ONLY
2285 READ-WRITE
2286 TRYCREATE
2287 UIDNEXT
2288 UIDVALIDITY
2289 UNSEEN
2290
2291 Server Responses - Server and Mailbox Status.
2292 These responses are always untagged.
2293 CAPABILITY *
2294 EXISTS *
2295 EXPUNGE *
2296 FLAGS *
2297 FETCH *
2298 LIST *
2299 LSUB *
2300 RECENT *
2301 SEARCH *
2302 STATUS *
2303
2304 Server Responses - Command Continuation Request
2305 +
2306
2307 */
2308 int
2309 imap_parse (f_imap_t f_imap)
2310 {
2311 int done = 0;
2312 int status = 0;
2313 char empty[2];
2314 char *buffer = NULL;
2315 mu_folder_t folder = f_imap->folder;
2316
2317 /* We use that moronic hack to not check null for the tockenize strings. */
2318 empty[0] = '\0';
2319 empty[1] = '\0';
2320 while (! done)
2321 {
2322 char *tag, *response, *remainder;
2323
2324 status = imap_readline (f_imap);
2325 if (status != 0)
2326 {
2327 break;
2328 }
2329 /* Comment out to see all reading traffic. */
2330 if (DEBUG_SHOW_RESPONSE)
2331 mu_error ("\t\t%s", f_imap->buffer);
2332
2333 /* We do not want to step over f_imap->buffer since it can be use
2334 further down the chain. */
2335 if (buffer)
2336 {
2337 free (buffer);
2338 buffer = NULL;
2339 }
2340 buffer = calloc ((f_imap->ptr - f_imap->buffer) + 1, 1);
2341 memcpy (buffer, f_imap->buffer, (f_imap->ptr - f_imap->buffer));
2342
2343 /* Tokenize the string. */
2344 {
2345 char *sp = NULL;
2346 tag = strtok_r (buffer, " ", &sp);
2347 response = strtok_r (NULL, " ", &sp);
2348 if (!response)
2349 response = empty;
2350 remainder = strtok_r (NULL, "\n", &sp);
2351 if (!remainder)
2352 remainder = empty;
2353 }
2354
2355 if (!tag)
2356 {
2357 /* Just in case */
2358 mu_error (_("no tag in response: %s %s"), response, remainder);
2359 status = MU_ERR_FAILURE;
2360 }
2361 /* Is the response untagged ? */
2362 else if (tag[0] == '*')
2363 {
2364 MU_DEBUG2 (folder->debug, MU_DEBUG_PROT, "* %s %s\n",
2365 response, remainder);
2366 /* Is it a Status Response. */
2367 if (mu_c_strcasecmp (response, "OK") == 0)
2368 {
2369 /* Check for status response [code]. */
2370 if (*remainder == '[')
2371 {
2372 char *cruft, *subtag;
2373 char *sp = NULL, *sp1;
2374 remainder++;
2375 cruft = strtok_r (remainder, "]", &sp);
2376 if (!cruft) cruft = empty;
2377 subtag = strtok_r (cruft, " ", &sp1);
2378 if (!subtag) subtag = empty;
2379
2380 if (mu_c_strcasecmp (subtag, "ALERT") == 0)
2381 {
2382 /* The human-readable text contains a special alert that
2383 MUST be presented to the user in a fashion that calls
2384 the user's attention to the message. */
2385 mu_error (_("ALERT: %s"), (sp) ? sp : "");
2386 }
2387 else if (mu_c_strcasecmp (subtag, "BADCHARSET") == 0)
2388 {
2389 /* Optionally followed by a parenthesized list of
2390 charsets. A SEARCH failed because the given charset
2391 is not supported by this implementation. If the
2392 optional list of charsets is given, this lists the
2393 charsets that are supported by this implementation. */
2394 mu_error (_("BAD CHARSET: %s"), (sp) ? sp : "");
2395 }
2396 else if (mu_c_strcasecmp (subtag, "CAPABILITY") == 0)
2397 {
2398 /* Followed by a list of capabilities. This can appear
2399 in the initial OK or PREAUTH response to transmit an
2400 initial capabilities list. This makes it unnecessary
2401 for a client to send a separate CAPABILITY command if
2402 it recognizes this response. */
2403 parse_capa (f_imap, cruft);
2404 }
2405 else if (mu_c_strcasecmp (subtag, "NEWNAME") == 0)
2406 {
2407 /* Followed by a mailbox name and a new mailbox name. A
2408 SELECT or EXAMINE failed because the target mailbox
2409 name (which once existed) was renamed to the new
2410 mailbox name. This is a hint to the client that the
2411 operation can succeed if the SELECT or EXAMINE is
2412 reissued with the new mailbox name. */
2413 mu_error ("NEWNAME: %s", (sp) ? sp : "");
2414 }
2415 else if (mu_c_strcasecmp (subtag, "PARSE") == 0)
2416 {
2417 /* The human-readable text represents an error in
2418 parsing the [RFC-822] header or [MIME-IMB] headers
2419 of a message in the mailbox. */
2420 mu_error ("PARSE: %s", (sp) ? sp : "");
2421 }
2422 else if (mu_c_strcasecmp (subtag, "PERMANENTFLAGS") == 0)
2423 {
2424 /* Followed by a parenthesized list of flags, indicates
2425 which of the known flags that the client can change
2426 permanently. Any flags that are in the FLAGS
2427 untagged response, but not the PERMANENTFLAGS list,
2428 can not be set permanently. If the client attempts
2429 to STORE a flag that is not in the PERMANENTFLAGS
2430 list, the server will either ignore the change or
2431 store the state change for the remainder of the
2432 current session only. The PERMANENTFLAGS list can
2433 also include the special flag \*, which indicates
2434 that it is possible to create new keywords by
2435 attempting to store those flags in the mailbox. */
2436 }
2437 else if (mu_c_strcasecmp (subtag, "READ-ONLY") == 0)
2438 {
2439 /* The mailbox is selected read-only, or its access
2440 while selected has changed from read-write to
2441 read-only. */
2442 }
2443 else if (mu_c_strcasecmp (subtag, "READ-WRITE") == 0)
2444 {
2445 /* The mailbox is selected read-write, or its access
2446 while selected has changed from read-only to
2447 read-write. */
2448 }
2449 else if (mu_c_strcasecmp (subtag, "TRYCREATE") == 0)
2450 {
2451 /* An APPEND or COPY attempt is failing because the
2452 target mailbox does not exist (as opposed to some
2453 other reason). This is a hint to the client that
2454 the operation can succeed if the mailbox is first
2455 created by the CREATE command. */
2456 mu_error ("TRYCREATE: %s", (sp) ? sp : "");
2457 }
2458 else if (mu_c_strcasecmp (subtag, "UIDNEXT") == 0)
2459 {
2460 /* Followed by a decimal number, indicates the next
2461 unique identifier value. Refer to section 2.3.1.1
2462 for more information. */
2463 char *value = strtok_r (NULL, " ", &sp1);
2464 if (value)
2465 f_imap->selected->uidnext = strtol (value, NULL, 10);
2466 }
2467 else if (mu_c_strcasecmp (subtag, "UIDVALIDITY") == 0)
2468 {
2469 /* Followed by a decimal number, indicates the unique
2470 identifier validity value. Refer to section 2.3.1.1
2471 for more information. */
2472 char *value = strtok_r (NULL, " ", &sp1);
2473 if (value)
2474 f_imap->selected->uidvalidity = strtol (value,
2475 NULL, 10);
2476 }
2477 else if (mu_c_strcasecmp (subtag, "UNSEEN") == 0)
2478 {
2479 /* Followed by a decimal number, indicates the number of
2480 the first message without the \Seen flag set. */
2481 char *value = strtok_r (NULL, " ", &sp1);
2482 if (value)
2483 f_imap->selected->unseen = strtol (value, NULL, 10);
2484 }
2485 else
2486 {
2487 /* Additional response codes defined by particular
2488 client or server implementations SHOULD be prefixed
2489 with an "X" until they are added to a revision of
2490 this protocol. Client implementations SHOULD ignore
2491 response codes that they do not recognize. */
2492 }
2493 } /* End of code. */
2494 else
2495 {
2496 /* Not sure why we would get an untagged ok...but we do... */
2497 /* Still should we be verbose about is ? */
2498 mu_error (_("untagged OK response: %s"), remainder);
2499 }
2500 }
2501 else if (mu_c_strcasecmp (response, "NO") == 0)
2502 {
2503 /* This does not mean failure but rather a strong warning. */
2504 mu_error (_("untagged NO response: %s"), remainder);
2505 }
2506 else if (mu_c_strcasecmp (response, "BAD") == 0)
2507 {
2508 /* We're dead, protocol/syntax error. */
2509 mu_error (_("untagged BAD response: %s"), remainder);
2510 }
2511 else if (mu_c_strcasecmp (response, "PREAUTH") == 0)
2512 {
2513 /* Should we be dealing with this? */
2514 }
2515 else if (mu_c_strcasecmp (response, "BYE") == 0)
2516 {
2517 /* We should close the stream. This is not recoverable. */
2518 done = 1;
2519 mu_monitor_wrlock (f_imap->folder->monitor);
2520 f_imap->isopen = 0;
2521 f_imap->selected = NULL;
2522 mu_monitor_unlock (f_imap->folder->monitor);
2523 mu_stream_close (f_imap->folder->stream);
2524 }
2525 else if (mu_c_strcasecmp (response, "CAPABILITY") == 0)
2526 {
2527 parse_capa (f_imap, remainder);
2528 }
2529 else if (mu_c_strcasecmp (remainder, "EXISTS") == 0)
2530 {
2531 f_imap->selected->messages_count = strtol (response, NULL, 10);
2532 }
2533 else if (mu_c_strcasecmp (remainder, "EXPUNGE") == 0)
2534 {
2535 unsigned int msgno = strtol (response, NULL, 10);
2536 status = imap_expunge (f_imap, msgno);
2537 }
2538 else if (mu_c_strncasecmp (remainder, "FETCH", 5) == 0)
2539 {
2540 status = imap_fetch (f_imap);
2541 if (status != 0)
2542 break;
2543 }
2544 else if (mu_c_strcasecmp (response, "FLAGS") == 0)
2545 {
2546 /* Flags define on the mailbox not a message flags. */
2547 status = imap_permanentflags (f_imap, &remainder);
2548 }
2549 else if (mu_c_strcasecmp (response, "LIST") == 0)
2550 {
2551 status = imap_list (f_imap);
2552 }
2553 else if (mu_c_strcasecmp (response, "LSUB") == 0)
2554 {
2555 status = imap_list (f_imap);
2556 }
2557 else if (mu_c_strcasecmp (remainder, "RECENT") == 0)
2558 {
2559 f_imap->selected->recent = strtol (response, NULL, 10);
2560 }
2561 else if (mu_c_strcasecmp (response, "SEARCH") == 0)
2562 {
2563 status = imap_search (f_imap);
2564 }
2565 else if (mu_c_strcasecmp (response, "STATUS") == 0)
2566 {
2567 status = imap_status (f_imap);
2568 }
2569 else
2570 {
2571 /* Once again, check for something strange. */
2572 mu_error (_("unknown untagged response: \"%s\" %s"),
2573 response, remainder);
2574 }
2575 }
2576 /* Continuation token ???. */
2577 else if (tag[0] == '+')
2578 {
2579 done = 1;
2580 }
2581 else
2582 {
2583 /* Every transaction ends with a tagged response. */
2584 done = 1;
2585 if (mu_c_strcasecmp (response, "OK") == 0)
2586 {
2587 /* Cool we are doing ok. */
2588 }
2589 else if (mu_c_strcasecmp (response, "NO") == 0)
2590 {
2591 if (mu_c_strncasecmp (remainder, "LOGIN", 5) == 0)
2592 {
2593 mu_observable_t observable = NULL;
2594 mu_folder_get_observable (f_imap->folder, &observable);
2595 mu_observable_notify (observable, MU_EVT_FOLDER_AUTHORITY_FAILED,
2596 NULL);
2597 status = MU_ERR_AUTH_FAILURE;
2598 }
2599 else if (mu_c_strncasecmp (remainder, "LIST", 4) == 0)
2600 status = MU_ERR_NOENT;
2601 else
2602 status = MU_ERR_FAILURE;
2603 }
2604 else /* NO and BAD */
2605 {
2606 status = EINVAL;
2607 mu_error (_("NO or BAD tagged response: %s %s %s"),
2608 tag, response, remainder);
2609 }
2610 }
2611 f_imap->ptr = f_imap->buffer;
2612 }
2613
2614 if (buffer)
2615 free (buffer);
2616 return status;
2617 }
2618
2619 #else 590 #else
2620 #include <stdio.h>
2621 #include <mailutils/sys/registrar.h>
2622 mu_record_t mu_imap_record = NULL;
2623 mu_record_t mu_imaps_record = NULL; 591 mu_record_t mu_imaps_record = NULL;
2624 #endif /* ENABLE_IMAP */ 592 #endif /* WITH_TLS */
......
...@@ -104,13 +104,15 @@ list_untagged_handler (mu_imap_t imap, mu_list_t resp, void *data) ...@@ -104,13 +104,15 @@ list_untagged_handler (mu_imap_t imap, mu_list_t resp, void *data)
104 } 104 }
105 105
106 elt = _mu_imap_list_at (resp, 2); 106 elt = _mu_imap_list_at (resp, 2);
107 if (!(elt && elt->type == imap_eltype_string)) 107 if (!elt)
108 return; 108 return;
109 if (mu_c_strcasecmp (elt->v.string, "NIL") == 0) 109 if (_mu_imap_list_element_is_nil (elt))
110 { 110 {
111 rp->separator = 0; 111 rp->separator = 0;
112 rp->level = 0; 112 rp->level = 0;
113 } 113 }
114 else if (elt->type != imap_eltype_string)
115 return;
114 else 116 else
115 { 117 {
116 rp->separator = elt->v.string[0]; 118 rp->separator = elt->v.string[0];
...@@ -131,6 +133,9 @@ mu_imap_genlist (mu_imap_t imap, int lsub, ...@@ -131,6 +133,9 @@ mu_imap_genlist (mu_imap_t imap, int lsub,
131 struct list_closure clos; 133 struct list_closure clos;
132 int rc; 134 int rc;
133 135
136 if (!refname || !mboxname)
137 return EINVAL;
138
134 argv[0] = lsub ? "LSUB" : "LIST"; 139 argv[0] = lsub ? "LSUB" : "LIST";
135 argv[1] = refname; 140 argv[1] = refname;
136 argv[2] = mboxname; 141 argv[2] = mboxname;
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
22 #include <string.h> 22 #include <string.h>
23 #include <mailutils/errno.h> 23 #include <mailutils/errno.h>
24 #include <mailutils/stream.h> 24 #include <mailutils/stream.h>
25 #include <mailutils/secret.h>
25 #include <mailutils/sys/imap.h> 26 #include <mailutils/sys/imap.h>
26 27
27 int 28 int
...@@ -77,3 +78,12 @@ mu_imap_login (mu_imap_t imap, const char *user, const char *pass) ...@@ -77,3 +78,12 @@ mu_imap_login (mu_imap_t imap, const char *user, const char *pass)
77 return status; 78 return status;
78 } 79 }
79 80
81 int
82 mu_imap_login_secret (mu_imap_t imap, const char *user, mu_secret_t secret)
83 {
84 int rc = mu_imap_login (imap, user, mu_secret_password (secret));
85 mu_secret_password_unref (secret);
86 return rc;
87 }
88
89
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2009,
3 2010, 2011 Free Software Foundation, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 3 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General
16 Public License along with this library. If not, see
17 <http://www.gnu.org/licenses/>. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #ifdef ENABLE_IMAP
24
25 #include <errno.h>
26 #include <string.h>
27 #ifdef HAVE_STRINGS_H
28 #include <strings.h>
29 #endif
30 #include <stdlib.h>
31 #include <assert.h>
32 #include <time.h>
33
34 #include <mailutils/address.h>
35 #include <mailutils/attribute.h>
36 #include <mailutils/body.h>
37 #include <mailutils/debug.h>
38 #include <mailutils/envelope.h>
39 #include <mailutils/error.h>
40 #include <mailutils/errno.h>
41 #include <mailutils/header.h>
42 #include <mailutils/message.h>
43 #include <mailutils/util.h>
44 #include <mailutils/observer.h>
45 #include <mailutils/property.h>
46 #include <mailutils/stream.h>
47 #include <mailutils/io.h>
48
49 #include <mailutils/sys/imap.h>
50 #include <mailutils/sys/mailbox.h>
51 #include <mailutils/sys/registrar.h>
52 #include <mailutils/sys/url.h>
53
54 #undef min
55 #define min(a,b) ((a) < (b) ? (a) : (b))
56
57 #define MU_IMAP_CACHE_HEADERS "Bcc Cc Content-Language Content-Transfer-Encoding Content-Type Date From In-Reply-To Message-ID Reference Reply-To Sender Subject To X-UIDL"
58
59 /* mu_mailbox_t API. */
60 static void mailbox_imap_destroy (mu_mailbox_t);
61 static int mailbox_imap_open (mu_mailbox_t, int);
62 static int mailbox_imap_close (mu_mailbox_t);
63 static int imap_uidvalidity (mu_mailbox_t, unsigned long *);
64 static int imap_uidnext (mu_mailbox_t, size_t *);
65 static int imap_expunge (mu_mailbox_t);
66 static int imap_get_message (mu_mailbox_t, size_t, mu_message_t *);
67 static int imap_messages_count (mu_mailbox_t, size_t *);
68 static int imap_messages_recent (mu_mailbox_t, size_t *);
69 static int imap_message_unseen (mu_mailbox_t, size_t *);
70 static int imap_scan (mu_mailbox_t, size_t, size_t *);
71 static int imap_scan0 (mu_mailbox_t, size_t, size_t *, int);
72 static int imap_is_updated (mu_mailbox_t);
73 static int imap_append_message (mu_mailbox_t, mu_message_t);
74 static int imap_append_message0 (mu_mailbox_t, mu_message_t);
75 static int imap_copy_message (mu_mailbox_t, mu_message_t);
76
77 /* mu_message_t API. */
78 static int imap_submessage_size (msg_imap_t, size_t *);
79 static int imap_message_size (mu_message_t, size_t *);
80 static int imap_message_lines (mu_message_t, size_t *);
81 static int imap_message_get_transport2 (mu_stream_t, mu_transport_t *pin,
82 mu_transport_t *pout);
83 static int imap_message_read (mu_stream_t , char *, size_t, mu_off_t, size_t *);
84 static int imap_message_uid (mu_message_t, size_t *);
85
86 /* mu_mime_t API. */
87 static int imap_is_multipart (mu_message_t, int *);
88 static int imap_get_num_parts (mu_message_t, size_t *);
89 static int imap_get_part (mu_message_t, size_t, mu_message_t *);
90
91 /* mu_envelope_t API */
92 static int imap_envelope_sender (mu_envelope_t, char *, size_t, size_t *);
93 static int imap_envelope_date (mu_envelope_t, char *, size_t, size_t *);
94
95 /* mu_attribute_t API */
96 static int imap_attr_get_flags (mu_attribute_t, int *);
97 static int imap_attr_set_flags (mu_attribute_t, int);
98 static int imap_attr_unset_flags (mu_attribute_t, int);
99
100 /* mu_header_t API. */
101 static int imap_header_read (mu_header_t, char*, size_t, mu_off_t, size_t *);
102
103 /* mu_body_t API. */
104 static int imap_body_read (mu_stream_t, char *, size_t, mu_off_t, size_t *);
105 static int imap_body_size (mu_body_t, size_t *);
106 static int imap_body_lines (mu_body_t, size_t *);
107 static int imap_body_get_transport2 (mu_stream_t, mu_transport_t *pin, mu_transport_t *pout);
108
109 /* Helpers. */
110 static int imap_get_transport2 (msg_imap_t msg_imap,
111 mu_transport_t *pin,
112 mu_transport_t *pout);
113 static int imap_get_message0 (msg_imap_t, mu_message_t *);
114 static int fetch_operation (f_imap_t, msg_imap_t, char *, size_t, size_t *);
115 static void free_subparts (msg_imap_t);
116 static int flags_to_string (char **, int);
117 static int delete_to_string (m_imap_t, char **);
118 static int is_same_folder (mu_mailbox_t, mu_message_t);
119
120 #define MBX_WRITABLE(mbx) ((mbx)->flags & (MU_STREAM_WRITE|MU_STREAM_RDWR|MU_STREAM_CREAT))
121
122 /* Initialize the concrete object mu_mailbox_t by overloading the function of the
123 structure. */
124 int
125 _mailbox_imap_and_imaps_init (mu_mailbox_t mailbox, int imaps)
126 {
127 int status;
128 m_imap_t m_imap;
129
130 if (!mailbox)
131 return EINVAL;
132 if (mailbox->folder == NULL)
133 return EINVAL;
134
135 m_imap = mailbox->data = calloc (1, sizeof (*m_imap));
136 if (m_imap == NULL)
137 return ENOMEM;
138
139 /* Retrieve the name of the mailbox from the URL. */
140 status = mu_url_aget_path (mailbox->url, &m_imap->name);
141 if (status == MU_ERR_NOENT)
142 {
143 m_imap->name = strdup ("INBOX");
144 if (!m_imap->name)
145 return ENOMEM;
146 }
147 else if (status)
148 return status;
149
150 /* Overload the functions. */
151 mailbox->_destroy = mailbox_imap_destroy;
152
153 mailbox->_open = mailbox_imap_open;
154 mailbox->_close = mailbox_imap_close;
155
156 /* Messages. */
157 mailbox->_get_message = imap_get_message;
158 mailbox->_append_message = imap_append_message;
159 mailbox->_messages_count = imap_messages_count;
160 mailbox->_messages_recent = imap_messages_recent;
161 mailbox->_message_unseen = imap_message_unseen;
162 mailbox->_expunge = imap_expunge;
163 mailbox->_uidvalidity = imap_uidvalidity;
164 mailbox->_uidnext = imap_uidnext;
165
166 mailbox->_scan = imap_scan;
167 mailbox->_is_updated = imap_is_updated;
168
169 /* Get the back pointer of the concrete folder. */
170 m_imap->f_imap = mailbox->folder->data;
171 m_imap->f_imap->imaps = imaps;
172
173 /* maibox back pointer. */
174 m_imap->mailbox = mailbox;
175
176 /* Set our properties. */
177 {
178 mu_property_t property = NULL;
179 mu_mailbox_get_property (mailbox, &property);
180 mu_property_set_value (property, "TYPE", "IMAP4", 1);
181 }
182
183 return 0;
184 }
185
186 int
187 _mailbox_imap_init (mu_mailbox_t mailbox)
188 {
189 return _mailbox_imap_and_imaps_init (mailbox, 0);
190 }
191
192 int
193 _mailbox_imaps_init (mu_mailbox_t mailbox)
194 {
195 return _mailbox_imap_and_imaps_init (mailbox, 1);
196 }
197
198
199 /* Recursive call to free all the subparts of a message. */
200 static void
201 free_subparts (msg_imap_t msg_imap)
202 {
203 size_t i;
204 for (i = 0; i < msg_imap->num_parts; i++)
205 {
206 if (msg_imap->parts[i])
207 free_subparts (msg_imap->parts[i]);
208 }
209
210 if (msg_imap->message)
211 mu_message_destroy (&(msg_imap->message), msg_imap);
212 if (msg_imap->parts)
213 free (msg_imap->parts);
214 if (msg_imap->fheader)
215 mu_header_destroy (&msg_imap->fheader, NULL);
216 if (msg_imap->internal_date)
217 free (msg_imap->internal_date);
218 free(msg_imap);
219 }
220
221 /* Give back all the resources. But it does not mean to shutdown the channel
222 this is done on the folder. */
223 static void
224 mailbox_imap_destroy (mu_mailbox_t mailbox)
225 {
226 if (mailbox->data)
227 {
228 m_imap_t m_imap = mailbox->data;
229 f_imap_t f_imap = m_imap->f_imap;
230 size_t i;
231
232 /* Deselect. */
233 if (m_imap != f_imap->selected)
234 f_imap->selected = NULL;
235
236 mu_monitor_wrlock (mailbox->monitor);
237 /* Destroy the imap messages and ressources associated to them. */
238 for (i = 0; i < m_imap->imessages_count; i++)
239 {
240 if (m_imap->imessages[i])
241 free_subparts (m_imap->imessages[i]);
242 }
243 if (m_imap->imessages)
244 free (m_imap->imessages);
245 if (m_imap->name)
246 free (m_imap->name);
247 free (m_imap);
248 mailbox->data = NULL;
249 mu_monitor_unlock (mailbox->monitor);
250 }
251 }
252
253 /* If the connection was not up it is open by the folder since the stream
254 socket is actually created by the folder. It is not necessary
255 to set select the mailbox right away, there are maybe on going operations.
256 But on any operation by a particular mailbox, it will be selected first. */
257 static int
258 mailbox_imap_open (mu_mailbox_t mailbox, int flags)
259 {
260 int status = 0;
261 m_imap_t m_imap = mailbox->data;
262 f_imap_t f_imap = m_imap->f_imap;
263 mu_folder_t folder = f_imap->folder;
264 mu_list_t folders = NULL;
265 size_t count;
266
267 /* m_imap must have been created during mailbox initialization. */
268 assert (mailbox->data);
269 assert (m_imap->name);
270
271 mailbox->flags = flags;
272
273 if ((status = mu_folder_open (mailbox->folder, flags)))
274 return status;
275
276 /* We might not have to SELECT the mailbox, but we need to know it
277 exists, and CREATE it if it doesn't, and CREATE is specified in
278 the flags.
279 */
280
281 switch (m_imap->state)
282 {
283 case IMAP_NO_STATE:
284 m_imap->state = IMAP_LIST;
285
286 case IMAP_LIST:
287 status = mu_folder_list (folder, NULL, m_imap->name, 0, &folders);
288 if (status != 0)
289 {
290 if (status != EAGAIN && status != EINPROGRESS && status != EINTR)
291 m_imap->state = IMAP_NO_STATE;
292
293 return status;
294 }
295 m_imap->state = IMAP_NO_STATE;
296 status = mu_list_count (folders, &count);
297 mu_list_destroy (&folders);
298 if (status || count)
299 return 0;
300
301 if ((flags & MU_STREAM_CREAT) == 0)
302 return ENOENT;
303
304 m_imap->state = IMAP_CREATE;
305
306 case IMAP_CREATE:
307 switch (f_imap->state)
308 {
309 case IMAP_NO_STATE:
310 {
311 const char *path;
312 status = mu_url_sget_path (folder->url, &path);
313 if (status == MU_ERR_NOENT)
314 return 0;
315 else if (status)
316 return status;
317 status = imap_writeline (f_imap, "g%lu CREATE %s\r\n",
318 (unsigned long) f_imap->seq, path);
319 MU_DEBUG2 (folder->debug, MU_DEBUG_PROT, "g%lu CREATE %s\n",
320 (unsigned long) f_imap->seq, path);
321 f_imap->seq++;
322 if (status != 0)
323 {
324 m_imap->state = f_imap->state = IMAP_NO_STATE;
325 return status;
326 }
327 f_imap->state = IMAP_CREATE;
328 }
329
330 case IMAP_CREATE:
331 status = imap_send (f_imap);
332 if (status != 0)
333 {
334 if (status != EAGAIN && status != EINPROGRESS
335 && status != EINTR)
336 m_imap->state = f_imap->state = IMAP_NO_STATE;
337
338 return status;
339 }
340 f_imap->state = IMAP_CREATE_ACK;
341
342 case IMAP_CREATE_ACK:
343 status = imap_parse (f_imap);
344 if (status != 0)
345 {
346 if (status == EINVAL)
347 status = EACCES;
348
349 if (status != EAGAIN && status != EINPROGRESS
350 && status != EINTR)
351 m_imap->state = f_imap->state = IMAP_NO_STATE;
352
353 return status;
354 }
355 f_imap->state = IMAP_NO_STATE;
356 break;
357
358 default:
359 status = EINVAL;
360 break;
361 }
362 m_imap->state = IMAP_NO_STATE;
363 break;
364
365 default:
366 status = EINVAL;
367 break;
368 }
369
370 return status;
371 }
372
373 /* We can not close the folder in term of shuting down the connection but if
374 we were the selected mailbox we send the close and deselect ourself.
375 The CLOSE is also use to expunge instead of sending expunge. */
376 static int
377 mailbox_imap_close (mu_mailbox_t mailbox)
378 {
379 m_imap_t m_imap = mailbox->data;
380 f_imap_t f_imap = m_imap->f_imap;
381 int status = 0;
382
383 /* If we are not the selected mailbox, just close the stream. */
384 if (m_imap != f_imap->selected)
385 return mu_folder_close (mailbox->folder);
386
387 /* Select first. */
388 status = imap_messages_count (mailbox, NULL);
389 if (status != 0)
390 return status;
391
392 switch (f_imap->state)
393 {
394 case IMAP_NO_STATE:
395 status = imap_writeline (f_imap, "g%lu CLOSE\r\n",
396 (unsigned long) f_imap->seq++);
397 CHECK_ERROR (f_imap, status);
398 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
399 f_imap->state = IMAP_CLOSE;
400
401 case IMAP_CLOSE:
402 status = imap_send (f_imap);
403 CHECK_EAGAIN (f_imap, status);
404 f_imap->state = IMAP_CLOSE_ACK;
405
406 case IMAP_CLOSE_ACK:
407 {
408 size_t i;
409 status = imap_parse (f_imap);
410 CHECK_EAGAIN (f_imap, status);
411 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
412
413 mu_monitor_wrlock (mailbox->monitor);
414 /* Destroy the imap messages and ressources associated to them. */
415 for (i = 0; i < m_imap->imessages_count; i++)
416 {
417 if (m_imap->imessages[i])
418 free_subparts (m_imap->imessages[i]);
419 }
420 if (m_imap->imessages)
421 free (m_imap->imessages);
422 m_imap->imessages = NULL;
423 m_imap->imessages_count = 0;
424 m_imap->messages_count = 0;
425 m_imap->recent = 0;
426 m_imap->unseen = 0;
427 /* Clear the callback string structure. */
428 mu_stream_truncate (f_imap->string.stream, 0);
429 f_imap->string.offset = 0;
430 f_imap->string.nleft = 0;
431 f_imap->string.type = IMAP_NO_STATE;
432 f_imap->string.msg_imap = NULL;
433 mu_monitor_unlock (mailbox->monitor);
434 }
435 break;
436
437 default:
438 /* mu_error ("imap_close unknown state: reconnect\n");*/
439 break;
440 }
441
442 /* Deselect. */
443 f_imap->selected = NULL;
444
445 f_imap->state = IMAP_NO_STATE;
446 return mu_folder_close (mailbox->folder);
447 }
448
449 /* Construction of the mu_message_t, nothing else is done then this setup. To
450 clarify this is different from say mu_message_get_part(). This call is for the
451 mailbox and we are setting up the mu_message_t structure. */
452 static int
453 imap_get_message (mu_mailbox_t mailbox, size_t msgno, mu_message_t *pmsg)
454 {
455 m_imap_t m_imap = mailbox->data;
456 msg_imap_t msg_imap;
457 int status = 0;
458
459 if (pmsg == NULL)
460 return MU_ERR_OUT_PTR_NULL;
461 if (msgno == 0 || msgno > m_imap->messages_count)
462 return EINVAL;
463
464 /* Check to see if we have already this message. */
465 mu_monitor_rdlock (mailbox->monitor);
466 {
467 size_t i;
468 for (i = 0; i < m_imap->imessages_count; i++)
469 {
470 if (m_imap->imessages[i])
471 {
472 if (m_imap->imessages[i]->num == msgno)
473 {
474 *pmsg = m_imap->imessages[i]->message;
475 mu_monitor_unlock (mailbox->monitor);
476 return 0;
477 }
478 }
479 }
480 }
481 mu_monitor_unlock (mailbox->monitor);
482
483 /* Allocate a concrete imap message. */
484 msg_imap = calloc (1, sizeof *msg_imap);
485 if (msg_imap == NULL)
486 return ENOMEM;
487 /* Back pointer. */
488 msg_imap->m_imap = m_imap;
489 msg_imap->num = msgno;
490 status = imap_get_message0 (msg_imap, pmsg);
491 if (status == 0)
492 {
493 /* Add it to the list. */
494 mu_monitor_wrlock (mailbox->monitor);
495 {
496 msg_imap_t *m ;
497 m = realloc (m_imap->imessages,
498 (m_imap->imessages_count + 1) * sizeof *m);
499 if (m == NULL)
500 {
501 mu_message_destroy (pmsg, msg_imap);
502 mu_monitor_unlock (mailbox->monitor);
503 return ENOMEM;
504 }
505 m_imap->imessages = m;
506 m_imap->imessages[m_imap->imessages_count] = msg_imap;
507 m_imap->imessages_count++;
508 }
509 mu_monitor_unlock (mailbox->monitor);
510
511 msg_imap->message = *pmsg;
512 }
513 else
514 free (msg_imap);
515 return status;
516 }
517
518 /* Set all the mu_message_t functions and parts. */
519 static int
520 imap_get_message0 (msg_imap_t msg_imap, mu_message_t *pmsg)
521 {
522 int status = 0;
523 mu_message_t msg = NULL;
524 mu_mailbox_t mailbox = msg_imap->m_imap->mailbox;
525
526 /* Create the message and its stream. */
527 {
528 mu_stream_t stream = NULL;
529 if ((status = mu_message_create (&msg, msg_imap)) != 0
530 || (status = mu_stream_create (&stream, mailbox->flags, msg)) != 0)
531 {
532 mu_stream_destroy (&stream, msg);
533 mu_message_destroy (&msg, msg_imap);
534 return status;
535 }
536 mu_stream_setbufsiz (stream, 128);
537 mu_stream_set_read (stream, imap_message_read, msg);
538 mu_stream_set_get_transport2 (stream, imap_message_get_transport2, msg);
539 mu_message_set_stream (msg, stream, msg_imap);
540 mu_message_set_size (msg, imap_message_size, msg_imap);
541 mu_message_set_lines (msg, imap_message_lines, msg_imap);
542 }
543
544 /* Create the header. */
545 {
546 mu_header_t header = NULL;
547 if ((status = mu_header_create (&header, NULL, 0, msg)) != 0)
548 {
549 mu_message_destroy (&msg, msg_imap);
550 return status;
551 }
552 mu_header_set_fill (header, imap_header_read, msg);
553 mu_message_set_header (msg, header, msg_imap);
554 }
555
556 /* Create the attribute. */
557 {
558 mu_attribute_t attribute;
559 status = mu_attribute_create (&attribute, msg);
560 if (status != 0)
561 {
562 mu_message_destroy (&msg, msg_imap);
563 return status;
564 }
565 mu_attribute_set_get_flags (attribute, imap_attr_get_flags, msg);
566 mu_attribute_set_set_flags (attribute, imap_attr_set_flags, msg);
567 mu_attribute_set_unset_flags (attribute, imap_attr_unset_flags, msg);
568 mu_message_set_attribute (msg, attribute, msg_imap);
569 }
570
571 /* Create the body and its stream. */
572 {
573 mu_body_t body = NULL;
574 mu_stream_t stream = NULL;
575 if ((status = mu_body_create (&body, msg)) != 0
576 || (status = mu_stream_create (&stream, mailbox->flags, body)) != 0)
577 {
578 mu_body_destroy (&body, msg);
579 mu_stream_destroy (&stream, body);
580 mu_message_destroy (&msg, msg_imap);
581 return status;
582 }
583 mu_stream_setbufsiz (stream, 128);
584 mu_stream_set_read (stream, imap_body_read, body);
585 mu_stream_set_get_transport2 (stream, imap_body_get_transport2, body);
586 mu_body_set_size (body, imap_body_size, msg);
587 mu_body_set_lines (body, imap_body_lines, msg);
588 mu_body_set_stream (body, stream, msg);
589 mu_message_set_body (msg, body, msg_imap);
590 }
591
592 /* Set the envelope. */
593 {
594 mu_envelope_t envelope= NULL;
595 status = mu_envelope_create (&envelope, msg);
596 if (status != 0)
597 {
598 mu_message_destroy (&msg, msg_imap);
599 return status;
600 }
601 mu_envelope_set_sender (envelope, imap_envelope_sender, msg);
602 mu_envelope_set_date (envelope, imap_envelope_date, msg);
603 mu_message_set_envelope (msg, envelope, msg_imap);
604 }
605
606 /* Set the mime handling. */
607 mu_message_set_is_multipart (msg, imap_is_multipart, msg_imap);
608 mu_message_set_get_num_parts (msg, imap_get_num_parts, msg_imap);
609 mu_message_set_get_part (msg, imap_get_part, msg_imap);
610
611 /* Set the UID on the message. */
612 mu_message_set_uid (msg, imap_message_uid, msg_imap);
613 mu_message_set_mailbox (msg, mailbox, msg_imap);
614
615 /* We are done here. */
616 *pmsg = msg;
617 return 0;
618 }
619
620 static int
621 imap_message_unseen (mu_mailbox_t mailbox, size_t *punseen)
622 {
623 m_imap_t m_imap = mailbox->data;
624 *punseen = m_imap->unseen;
625 return 0;
626 }
627
628 static int
629 imap_messages_recent (mu_mailbox_t mailbox, size_t *precent)
630 {
631 m_imap_t m_imap = mailbox->data;
632 *precent = m_imap->recent;
633 return 0;
634 }
635
636 static int
637 imap_uidvalidity (mu_mailbox_t mailbox, unsigned long *puidvalidity)
638 {
639 m_imap_t m_imap = mailbox->data;
640 *puidvalidity = m_imap->uidvalidity;
641 return 0;
642 }
643
644 static int
645 imap_uidnext (mu_mailbox_t mailbox, size_t *puidnext)
646 {
647 m_imap_t m_imap = mailbox->data;
648 *puidnext = m_imap->uidnext;
649 return 0;
650 }
651
652 /* There is no explicit call to get the message count. The count is send on
653 a SELECT/EXAMINE command it is also sent async, meaning it will be piggy
654 back on other server response as an untag "EXIST" response. The
655 function is also use as a way to select mailbox by other functions. */
656 static int
657 imap_messages_count (mu_mailbox_t mailbox, size_t *pnum)
658 {
659 m_imap_t m_imap = mailbox->data;
660 f_imap_t f_imap = m_imap->f_imap;
661 int status = 0;
662
663 /* FIXME: It is debatable if we should reconnect when the connection
664 timeout or die. Probably for timeout client should ping i.e. send
665 a NOOP via imap_is_updated() function to keep the connection alive. */
666 status = mu_folder_open (mailbox->folder, mailbox->flags);
667 if (status != 0)
668 return status;
669
670 /* Are we already selected ? */
671 if (m_imap == (f_imap->selected))
672 {
673 if (pnum)
674 *pnum = m_imap->messages_count;
675 return 0;
676 }
677
678 /* Put the mailbox as selected. */
679 f_imap->selected = m_imap;
680
681 switch (f_imap->state)
682 {
683 case IMAP_NO_STATE:
684 status = imap_writeline (f_imap, "g%lu %s %s\r\n",
685 (unsigned long) f_imap->seq++,
686 MBX_WRITABLE(mailbox) ? "SELECT" : "EXAMINE",
687 m_imap->name);
688 CHECK_ERROR (f_imap, status);
689 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
690 f_imap->state = IMAP_SELECT;
691
692 case IMAP_SELECT:
693 status = imap_send (f_imap);
694 CHECK_EAGAIN (f_imap, status);
695 f_imap->state = IMAP_SELECT_ACK;
696
697 case IMAP_SELECT_ACK:
698 status = imap_parse (f_imap);
699 CHECK_EAGAIN (f_imap, status);
700 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
701 break;
702
703 default:
704 /*mu_error ("imap_message_count unknown state: reconnect\n");*/
705 break;
706 }
707
708 if (pnum)
709 *pnum = m_imap->messages_count;
710
711 f_imap->state = IMAP_NO_STATE;
712 return status;
713 }
714
715 static int
716 imap_scan (mu_mailbox_t mailbox, size_t msgno, size_t *pcount)
717 {
718 return imap_scan0 (mailbox, msgno, pcount , 1);
719 }
720
721 /* Normally this function is called when an observer is trying to build
722 some sort of list/tree header as the scanning progresses. But doing
723 this for each message can be time consuming and inefficient. So we
724 bundle all requests into one and ask the server for everything:
725 "FETCH 1:*". The good side is that everything will be faster and we
726 do not do lots of small transcations, but rather a big one. The bad
727 side is that everything will be cached in the structure using a lot of
728 memory. */
729 static int
730 imap_scan0 (mu_mailbox_t mailbox, size_t msgno, size_t *pcount, int notif)
731 {
732 int status;
733 size_t i;
734 size_t count = 0;
735 m_imap_t m_imap = mailbox->data;
736 f_imap_t f_imap = m_imap->f_imap;
737
738 /* Selected. */
739 status = imap_messages_count (mailbox, &count);
740 if (pcount)
741 *pcount = count;
742 if (status != 0)
743 return status;
744
745 /* No need to scan, there is no messages. */
746 if (count == 0)
747 return 0;
748
749 switch (f_imap->state)
750 {
751 case IMAP_NO_STATE:
752 status = imap_writeline (f_imap,
753 "g%lu FETCH 1:* (FLAGS RFC822.SIZE BODY.PEEK[HEADER.FIELDS (%s)])\r\n",
754 (unsigned long) f_imap->seq++,
755 MU_IMAP_CACHE_HEADERS);
756 CHECK_ERROR (f_imap, status);
757 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
758 f_imap->state = IMAP_SCAN;
759
760 case IMAP_SCAN:
761 status = imap_send (f_imap);
762 CHECK_EAGAIN (f_imap, status);
763 f_imap->state = IMAP_SCAN_ACK;
764 /* Clear the callback string structure. */
765 mu_stream_truncate (f_imap->string.stream, 0);
766 f_imap->string.offset = 0;
767 f_imap->string.nleft = 0;
768 f_imap->string.type = IMAP_NO_STATE;
769 f_imap->string.msg_imap = NULL;
770
771 case IMAP_SCAN_ACK:
772 status = imap_parse (f_imap);
773 CHECK_EAGAIN (f_imap, status);
774 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
775 /* Clear the callback string structure. */
776 mu_stream_truncate (f_imap->string.stream, 0);
777 f_imap->string.offset = 0;
778 f_imap->string.nleft = 0;
779 f_imap->string.type = IMAP_NO_STATE;
780 f_imap->string.msg_imap = NULL;
781 break;
782
783 default:
784 /*mu_error ("imap_scan unknown state: reconnect\n");*/
785 return EINVAL;
786 }
787
788 f_imap->state = IMAP_NO_STATE;
789
790 /* Do not send notifications. */
791 if (!notif)
792 return 0;
793
794 /* If no callbacks bail out early. */
795 if (mailbox->observable == NULL)
796 return 0;
797
798 for (i = msgno; i <= count; i++)
799 {
800 size_t tmp = i;
801 if (mu_observable_notify (mailbox->observable, MU_EVT_MESSAGE_ADD,
802 &tmp) != 0)
803 break;
804 if ((i + 1) % 100 == 0)
805 mu_observable_notify (mailbox->observable, MU_EVT_MAILBOX_PROGRESS,
806 NULL);
807 }
808 return 0;
809 }
810
811 /* Send a NOOP and see if the count has changed. */
812 static int
813 imap_is_updated (mu_mailbox_t mailbox)
814 {
815 m_imap_t m_imap = mailbox->data;
816 size_t oldcount = m_imap->messages_count;
817 f_imap_t f_imap = m_imap->f_imap;
818 int status = 0;
819
820 /* Selected. */
821 status = imap_messages_count (mailbox, &oldcount);
822 if (status != 0)
823 return status;
824
825 /* Send a noop, and let imap piggy pack the information. */
826 switch (f_imap->state)
827 {
828 case IMAP_NO_STATE:
829 status = imap_writeline (f_imap, "g%lu NOOP\r\n",
830 (unsigned long) f_imap->seq++);
831 CHECK_ERROR (f_imap, status);
832 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
833 f_imap->state = IMAP_NOOP;
834
835 case IMAP_NOOP:
836 status = imap_send (f_imap);
837 CHECK_EAGAIN (f_imap, status);
838 f_imap->state = IMAP_NOOP_ACK;
839
840 case IMAP_NOOP_ACK:
841 status = imap_parse (f_imap);
842 CHECK_EAGAIN (f_imap, status);
843 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
844 break;
845
846 default:
847 /*mu_error ("imap_noop unknown state: reconnect\n"); */
848 break;
849 }
850 f_imap->state = IMAP_NO_STATE;
851 return (oldcount == m_imap->messages_count);
852 }
853
854
855 static int
856 imap_expunge (mu_mailbox_t mailbox)
857 {
858 int status;
859 m_imap_t m_imap = mailbox->data;
860 f_imap_t f_imap = m_imap->f_imap;
861
862 if (!MBX_WRITABLE(mailbox))
863 return EACCES;
864
865 /* Select first. */
866 status = imap_messages_count (mailbox, NULL);
867 if (status != 0)
868 return status;
869
870 switch (f_imap->state)
871 {
872 case IMAP_NO_STATE:
873 {
874 char *set = NULL;
875 status = delete_to_string (m_imap, &set);
876 CHECK_ERROR (f_imap, status);
877 if (set == NULL || *set == '\0')
878 {
879 if (set)
880 free (set);
881 return 0;
882 }
883 status = imap_writeline (f_imap,
884 "g%lu STORE %s +FLAGS.SILENT (\\Deleted)\r\n",
885 (unsigned long) f_imap->seq++,
886 set);
887 free (set);
888 CHECK_ERROR (f_imap, status);
889 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
890 f_imap->state = IMAP_STORE;
891 }
892
893 /* Send DELETE. */
894 case IMAP_STORE:
895 status = imap_send (f_imap);
896 CHECK_EAGAIN (f_imap, status);
897 f_imap->state = IMAP_STORE_ACK;
898
899 case IMAP_STORE_ACK:
900 status = imap_parse (f_imap);
901 CHECK_EAGAIN (f_imap, status);
902 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
903 f_imap->state = IMAP_NO_STATE;
904
905 case IMAP_EXPUNGE:
906 case IMAP_EXPUNGE_ACK:
907 status = imap_writeline (f_imap, "g%lu EXPUNGE\r\n",
908 (unsigned long) f_imap->seq++);
909 CHECK_ERROR (f_imap, status);
910 status = imap_send (f_imap);
911 CHECK_EAGAIN (f_imap, status);
912
913 /* Rescan after expunging but do not trigger the observers. */
914 case IMAP_SCAN:
915 case IMAP_SCAN_ACK:
916 status = imap_scan0 (mailbox, 1, NULL, 0);
917 CHECK_EAGAIN (f_imap, status);
918
919 default:
920 /* mu_error ("imap_expunge: unknown state\n"); */
921 break;
922 }
923
924 return status;
925 }
926
927 /* FIXME: Not ___Nonblocking___ safe. */
928 /* DANGER: The mu_message_t object makes no guaranty about the size and the lines
929 that it returns, if its pointing to non-local file messages, so we
930 make a local copy. */
931 static int
932 imap_append_message (mu_mailbox_t mailbox, mu_message_t msg)
933 {
934 int status = 0;
935 m_imap_t m_imap = mailbox->data;
936 f_imap_t f_imap = m_imap->f_imap;
937
938 /* FIXME: It is debatable if we should reconnect when the connection
939 timeout or die. For timeout client should ping i.e. send
940 a NOOP via imap_is_updated() function to keep the connection alive. */
941 status = mu_folder_open (mailbox->folder, mailbox->flags);
942 if (status != 0)
943 return status;
944
945 /* FIXME: Can we append to self. */
946
947 /* Check to see if we are selected. If the message was not modified
948 and came from the same imap folder. use COPY.*/
949 if (f_imap->selected != m_imap && !mu_message_is_modified (msg)
950 && is_same_folder (mailbox, msg))
951 return imap_copy_message (mailbox, msg);
952
953 /* copy the message to local disk by createing a floating message. */
954 {
955 mu_message_t message = NULL;
956
957 status = mu_message_create_copy(&message, msg);
958
959 if (status == 0)
960 status = imap_append_message0 (mailbox, message);
961 mu_message_destroy (&message, NULL);
962 }
963 return status;
964 }
965
966 /* Ok this mean that the message is coming from somewhere else. IMAP
967 is very susceptible on the size, example:
968 A003 APPEND saved-messages (\Seen) {310}
969 if the server does not get the right size advertise in the string literal
970 it will misbehave. Sine we are assuming that the message will be
971 in native file system format meaning ending with NEWLINE, we will have
972 to do the calculation. But what is worse; the value return
973 by mu_message_size () and mu_message_lines () are no mean exact but rather
974 a gross approximation for certain type of mailbox. So the sane
975 thing to do is to save the message in temporary file, this we say
976 we guarantee the size of the message. */
977 static int
978 imap_append_message0 (mu_mailbox_t mailbox, mu_message_t msg)
979 {
980 size_t total;
981 int status = 0;
982 m_imap_t m_imap = mailbox->data;
983 f_imap_t f_imap = m_imap->f_imap;
984
985 switch (f_imap->state)
986 {
987 case IMAP_NO_STATE:
988 {
989 size_t lines, size;
990 const char *path;
991 char *abuf = malloc (1);
992 /* Get the desired flags attribute. */
993 if (abuf == NULL)
994 return ENOMEM;
995 *abuf = '\0';
996 {
997 mu_attribute_t attribute = NULL;
998 int flags = 0;
999 mu_message_get_attribute (msg, &attribute);
1000 mu_attribute_get_flags (attribute, &flags);
1001 status = flags_to_string (&abuf, flags);
1002 if (status != 0)
1003 return status;
1004 /* Put the surrounding parenthesis, wu-IMAP is sensible to this. */
1005 {
1006 char *tmp = calloc (strlen (abuf) + 3, 1);
1007 if (tmp == NULL)
1008 {
1009 free (abuf);
1010 return ENOMEM;
1011 }
1012 sprintf (tmp, "(%s)", abuf);
1013 free (abuf);
1014 abuf = tmp;
1015 }
1016 }
1017
1018 /* Get the mailbox filepath. */
1019 status = mu_url_sget_path (mailbox->url, &path);
1020 if (status == MU_ERR_NOENT)
1021 path = "INBOX";
1022
1023 /* FIXME: we need to get the mu_envelope_date and use it.
1024 currently it is ignored. */
1025
1026 /* Get the total size, assuming that it is in UNIX format. */
1027 lines = size = 0;
1028 mu_message_size (msg, &size);
1029 mu_message_lines (msg, &lines);
1030 total = size + lines;
1031 status = imap_writeline (f_imap, "g%lu APPEND %s %s {%lu}\r\n",
1032 (unsigned long) f_imap->seq++,
1033 path,
1034 abuf,
1035 (unsigned long) (size + lines));
1036 free (abuf);
1037 CHECK_ERROR (f_imap, status);
1038 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1039 f_imap->state = IMAP_APPEND;
1040 }
1041
1042 case IMAP_APPEND:
1043 status = imap_send (f_imap);
1044 CHECK_EAGAIN (f_imap, status);
1045 f_imap->state = IMAP_APPEND_CONT;
1046
1047 case IMAP_APPEND_CONT:
1048 status = imap_parse (f_imap);
1049 CHECK_EAGAIN (f_imap, status);
1050 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1051 /* If we did not receive the continuation token, it is an error
1052 bail out. */
1053 if (f_imap->buffer[0] != '+')
1054 {
1055 status = EACCES;
1056 break;
1057 }
1058 f_imap->state = IMAP_APPEND_SEND;
1059
1060 case IMAP_APPEND_SEND:
1061 {
1062 mu_stream_t stream = NULL;
1063 mu_off_t off = 0;
1064 size_t n = 0;
1065 char buffer[255];
1066 mu_message_get_stream (msg, &stream);
1067 while (mu_stream_readline (stream, buffer, sizeof buffer, off, &n) == 0
1068 && n > 0)
1069 {
1070 if (buffer[n - 1] == '\n')
1071 {
1072 buffer[n - 1] = '\0';
1073 status = imap_writeline (f_imap, "%s\r\n", buffer);
1074 }
1075 else
1076 imap_writeline (f_imap, "%s", buffer);
1077 off += n;
1078 status = imap_send (f_imap);
1079 CHECK_EAGAIN (f_imap, status);
1080 }
1081 f_imap->state = IMAP_APPEND_ACK;
1082 }
1083 /* !@#%$ UW-IMAP and Gimap server hack: both insist on the last line. */
1084 imap_writeline (f_imap, "\r\n");
1085 status = imap_send (f_imap);
1086 CHECK_EAGAIN (f_imap, status);
1087
1088 case IMAP_APPEND_ACK:
1089 status = imap_parse (f_imap);
1090 CHECK_EAGAIN (f_imap, status);
1091 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1092
1093 default:
1094 /* mu_error ("imap_append: unknown state\n"); */
1095 break;
1096 }
1097 f_imap->state = IMAP_NO_STATE;
1098 return status;
1099 }
1100
1101 /* If the message is on the same server. Use the COPY command much more
1102 efficient. */
1103 static int
1104 imap_copy_message (mu_mailbox_t mailbox, mu_message_t msg)
1105 {
1106 m_imap_t m_imap = mailbox->data;
1107 f_imap_t f_imap = m_imap->f_imap;
1108 msg_imap_t msg_imap = mu_message_get_owner (msg);
1109 int status = 0;
1110
1111 /* FIXME: It is debatable if we should reconnect when the connection
1112 timeout or die. For timeout client should ping i.e. send
1113 a NOOP via imap_is_updated() function to keep the connection alive. */
1114 status = mu_folder_open (mailbox->folder, mailbox->flags);
1115 if (status != 0)
1116 return status;
1117
1118 switch (f_imap->state)
1119 {
1120 case IMAP_NO_STATE:
1121 {
1122 const char *path;
1123 /* Check for a valid mailbox name. */
1124 status = mu_url_sget_path (mailbox->url, &path);
1125 if (status == 0)
1126 status = imap_writeline (f_imap, "g%lu COPY %lu %s\r\n",
1127 (unsigned long) f_imap->seq++,
1128 (unsigned long) msg_imap->num,
1129 path);
1130 CHECK_ERROR (f_imap, status);
1131 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1132 f_imap->state = IMAP_COPY;
1133 }
1134
1135 case IMAP_COPY:
1136 status = imap_send (f_imap);
1137 CHECK_EAGAIN (f_imap, status);
1138 f_imap->state = IMAP_COPY_ACK;
1139
1140 case IMAP_COPY_ACK:
1141 status = imap_parse (f_imap);
1142 CHECK_EAGAIN (f_imap, status);
1143 MU_DEBUG (mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1144
1145 default:
1146 break;
1147 }
1148 f_imap->state = IMAP_NO_STATE;
1149 return status;
1150 }
1151
1152 /* Message read overload */
1153 static int
1154 imap_message_read (mu_stream_t stream, char *buffer, size_t buflen,
1155 mu_off_t offset, size_t *plen)
1156 {
1157 mu_message_t msg = mu_stream_get_owner (stream);
1158 msg_imap_t msg_imap = mu_message_get_owner (msg);
1159 m_imap_t m_imap = msg_imap->m_imap;
1160 f_imap_t f_imap = m_imap->f_imap;
1161 char *oldbuf = NULL;
1162 char newbuf[2];
1163 int status;
1164
1165 /* This is so annoying, a buffer len of 1 is a killer. If you have for
1166 example "\n" to retrieve from the server, IMAP will transform this to
1167 "\r\n" and since you ask for only 1, the server will send '\r' only.
1168 And ... '\r' will be stripped by (imap_readline()) the number of char
1169 read will be 0 which means we're done .... sigh ... So we guard by at
1170 least ask for 2 chars. */
1171 if (buflen == 1)
1172 {
1173 oldbuf = buffer;
1174 buffer = newbuf;
1175 buflen = 2;
1176 }
1177
1178 /* Start over. */
1179 if (offset == 0)
1180 msg_imap->mu_message_lines = 0;
1181
1182 status = imap_messages_count (m_imap->mailbox, NULL);
1183 if (status != 0)
1184 return status;
1185
1186 /* Select first. */
1187 if (f_imap->state == IMAP_NO_STATE)
1188 {
1189 char *section = NULL;
1190
1191 if (msg_imap->part)
1192 section = section_name (msg_imap);
1193
1194 /* We have strip the \r, but the offset on the imap server is with that
1195 octet(CFLF) so add it in the offset, it's the number of lines. */
1196 status = imap_writeline (f_imap,
1197 "g%lu FETCH %lu BODY.PEEK[%s]<%lu.%lu>\r\n",
1198 (unsigned long) f_imap->seq++,
1199 (unsigned long) msg_imap->num,
1200 (section) ? section : "",
1201 (unsigned long) (offset +
1202 msg_imap->mu_message_lines),
1203 (unsigned long) buflen);
1204 if (section)
1205 free (section);
1206 CHECK_ERROR (f_imap, status);
1207 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1208 f_imap->state = IMAP_FETCH;
1209 }
1210 status = fetch_operation (f_imap, msg_imap, buffer, buflen, plen);
1211
1212 if (oldbuf)
1213 oldbuf[0] = buffer[0];
1214 return status;
1215 }
1216
1217 static int
1218 imap_message_lines (mu_message_t msg, size_t *plines)
1219 {
1220 msg_imap_t msg_imap = mu_message_get_owner (msg);
1221 if (plines && msg_imap)
1222 {
1223 if (msg_imap->mu_message_lines == 0)
1224 *plines = msg_imap->body_lines + msg_imap->header_lines;
1225 else
1226 *plines = msg_imap->mu_message_lines;
1227 }
1228 return 0;
1229 }
1230
1231 /* Sometimes a message is just a place container for other sub parts.
1232 In those cases imap bodystructure does not set the mu_message_size aka
1233 the mu_body_size. But we can calculate it since the mu_message_size
1234 is the sum of its subparts. */
1235 static int
1236 imap_submessage_size (msg_imap_t msg_imap, size_t *psize)
1237 {
1238 if (psize)
1239 {
1240 *psize = 0;
1241 if (msg_imap->mu_message_size == 0)
1242 {
1243 size_t i, size;
1244 for (size = i = 0; i < msg_imap->num_parts; i++, size = 0)
1245 {
1246 if (msg_imap->parts[i])
1247 imap_submessage_size (msg_imap->parts[i], &size);
1248 *psize += size;
1249 }
1250 }
1251 else
1252 *psize = (msg_imap->mu_message_size + msg_imap->header_size)
1253 - msg_imap->mu_message_lines;
1254 }
1255 return 0;
1256 }
1257
1258 static int
1259 imap_message_size (mu_message_t msg, size_t *psize)
1260 {
1261 msg_imap_t msg_imap = mu_message_get_owner (msg);
1262 m_imap_t m_imap = msg_imap->m_imap;
1263 f_imap_t f_imap = m_imap->f_imap;
1264 int status = 0;;
1265
1266 status = imap_messages_count (m_imap->mailbox, NULL);
1267 if (status != 0)
1268 return status;
1269
1270 /* If there is a parent it means it is a sub message, IMAP does not give
1271 the full size of mime messages, so the mu_message_size retrieved from
1272 doing a bodystructure represents rather the mu_body_size. */
1273 if (msg_imap->parent)
1274 return imap_submessage_size (msg_imap, psize);
1275
1276 if (msg_imap->mu_message_size == 0)
1277 {
1278 /* Select first. */
1279 if (f_imap->state == IMAP_NO_STATE)
1280 {
1281 /* We strip the \r, but the offset/size on the imap server is with
1282 that octet so add it in the offset, since it's the number of
1283 lines. */
1284 status = imap_writeline (f_imap,
1285 "g%lu FETCH %lu RFC822.SIZE\r\n",
1286 (unsigned long) f_imap->seq++,
1287 (unsigned long) msg_imap->num);
1288 CHECK_ERROR (f_imap, status);
1289 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1290 f_imap->state = IMAP_FETCH;
1291 }
1292 status = fetch_operation (f_imap, msg_imap, 0, 0, 0);
1293 }
1294
1295 if (status == 0)
1296 {
1297 if (psize)
1298 *psize = msg_imap->mu_message_size - msg_imap->mu_message_lines;
1299 }
1300 return status;
1301 }
1302
1303 static int
1304 imap_message_uid (mu_message_t msg, size_t *puid)
1305 {
1306 msg_imap_t msg_imap = mu_message_get_owner (msg);
1307 m_imap_t m_imap = msg_imap->m_imap;
1308 f_imap_t f_imap = m_imap->f_imap;
1309 int status;
1310
1311 if (puid)
1312 return 0;
1313
1314 /* Select first. */
1315 status = imap_messages_count (m_imap->mailbox, NULL);
1316 if (status != 0)
1317 return status;
1318
1319 if (f_imap->state == IMAP_NO_STATE)
1320 {
1321 if (msg_imap->uid)
1322 {
1323 *puid = msg_imap->uid;
1324 return 0;
1325 }
1326 status = imap_writeline (f_imap, "g%lu FETCH %lu UID\r\n",
1327 (unsigned long) f_imap->seq++,
1328 (unsigned long) msg_imap->num);
1329 CHECK_ERROR (f_imap, status);
1330 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1331 f_imap->state = IMAP_FETCH;
1332 }
1333 status = fetch_operation (f_imap, msg_imap, 0, 0, 0);
1334 if (status != 0)
1335 return status;
1336 *puid = msg_imap->uid;
1337 return 0;
1338 }
1339
1340 static int
1341 imap_message_get_transport2 (mu_stream_t stream, mu_transport_t *pin, mu_transport_t *pout)
1342 {
1343 mu_message_t msg = mu_stream_get_owner (stream);
1344 msg_imap_t msg_imap = mu_message_get_owner (msg);
1345 return imap_get_transport2 (msg_imap, pin, pout);
1346 }
1347
1348 /* Mime. */
1349 static int
1350 imap_is_multipart (mu_message_t msg, int *ismulti)
1351 {
1352 msg_imap_t msg_imap = mu_message_get_owner (msg);
1353 m_imap_t m_imap = msg_imap->m_imap;
1354 f_imap_t f_imap = m_imap->f_imap;
1355 int status;
1356
1357 /* Select first. */
1358 status = imap_messages_count (m_imap->mailbox, NULL);
1359 if (status != 0)
1360 return status;
1361
1362 if (f_imap->state == IMAP_NO_STATE)
1363 {
1364 if (msg_imap->num_parts || msg_imap->part)
1365 {
1366 if (ismulti)
1367 *ismulti = (msg_imap->num_parts > 1);
1368 return 0;
1369 }
1370 status = imap_writeline (f_imap,
1371 "g%lu FETCH %lu BODYSTRUCTURE\r\n",
1372 (unsigned long) f_imap->seq++,
1373 (unsigned long) msg_imap->num);
1374 CHECK_ERROR (f_imap, status);
1375 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1376 f_imap->state = IMAP_FETCH;
1377 }
1378 status = fetch_operation (f_imap, msg_imap, 0, 0, 0);
1379 if (status != 0)
1380 return status;
1381 if (ismulti)
1382 *ismulti = (msg_imap->num_parts > 1);
1383 return 0;
1384 }
1385
1386 static int
1387 imap_get_num_parts (mu_message_t msg, size_t *nparts)
1388 {
1389 msg_imap_t msg_imap = mu_message_get_owner (msg);
1390 if (msg_imap)
1391 {
1392 if (msg_imap->num_parts == 0)
1393 {
1394 int status = imap_is_multipart (msg, NULL);
1395 if (status != 0)
1396 return status;
1397 }
1398 if (nparts)
1399 *nparts = (msg_imap->num_parts == 0) ? 1 : msg_imap->num_parts;
1400 }
1401 return 0;
1402 }
1403
1404 static int
1405 imap_get_part (mu_message_t msg, size_t partno, mu_message_t *pmsg)
1406 {
1407 msg_imap_t msg_imap = mu_message_get_owner (msg);
1408 int status = 0;
1409
1410 if (msg_imap->num_parts == 0)
1411 {
1412 status = imap_get_num_parts (msg, NULL);
1413 if (status != 0)
1414 return status;
1415 }
1416
1417 if (partno <= msg_imap->num_parts)
1418 {
1419 if (msg_imap->parts[partno - 1]->message)
1420 {
1421 if (pmsg)
1422 *pmsg = msg_imap->parts[partno - 1]->message;
1423 }
1424 else
1425 {
1426 mu_message_t message;
1427 status = imap_get_message0 (msg_imap->parts[partno - 1], &message);
1428 if (status == 0)
1429 {
1430 mu_header_t header;
1431 mu_message_get_header (message, &header);
1432 mu_message_set_stream (message, NULL, msg_imap->parts[partno - 1]);
1433 /* mu_message_set_size (message, NULL, msg_imap->parts[partno - 1]); */
1434 msg_imap->parts[partno - 1]->message = message;
1435 if (pmsg)
1436 *pmsg = message;
1437 }
1438 }
1439 }
1440 else
1441 {
1442 if (pmsg)
1443 *pmsg = msg_imap->message;
1444 }
1445 return status;
1446 }
1447
1448 /* Envelope. */
1449 static int
1450 imap_envelope_sender (mu_envelope_t envelope, char *buffer, size_t buflen,
1451 size_t *plen)
1452 {
1453 mu_message_t msg = mu_envelope_get_owner (envelope);
1454 mu_header_t header;
1455 const char *sender;
1456 int status;
1457
1458 mu_message_get_header (msg, &header);
1459 status = mu_header_sget_value (header, MU_HEADER_SENDER, &sender);
1460 if (status == EAGAIN)
1461 return status;
1462 else if (status != 0)
1463 status = mu_header_sget_value (header, MU_HEADER_FROM, &sender);
1464 if (status == 0)
1465 {
1466 const char *email = NULL;
1467 size_t len;
1468 mu_address_t address;
1469 if (mu_address_create (&address, sender) == 0)
1470 {
1471 if (mu_address_sget_email (address, 1, &email) == 0)
1472 len = mu_cpystr (buffer, email, buflen);
1473 mu_address_destroy (&address);
1474 }
1475
1476 if (!email)
1477 return MU_ERR_NOENT;
1478
1479 if (plen)
1480 *plen = len;
1481 }
1482 return status;
1483 }
1484
1485 static int
1486 imap_envelope_date (mu_envelope_t envelope, char *buffer, size_t buflen,
1487 size_t *plen)
1488 {
1489 mu_message_t msg = mu_envelope_get_owner (envelope);
1490 msg_imap_t msg_imap = mu_message_get_owner (msg);
1491 m_imap_t m_imap = msg_imap->m_imap;
1492 f_imap_t f_imap = m_imap->f_imap;
1493 struct tm tm;
1494 mu_timezone tz;
1495 time_t now;
1496 char datebuf[] = "mm-dd-yyyy hh:mm:ss +0000";
1497 const char* date = datebuf;
1498 const char** datep = &date;
1499 /* reserve as much space as we need for internal-date */
1500 int status;
1501
1502 /* Select first. */
1503 status = imap_messages_count (m_imap->mailbox, NULL);
1504 if (status != 0)
1505 return status;
1506 if (msg_imap->internal_date == NULL)
1507 {
1508 if (f_imap->state == IMAP_NO_STATE)
1509 {
1510 status = imap_writeline (f_imap,
1511 "g%lu FETCH %lu INTERNALDATE\r\n",
1512 (unsigned long) f_imap->seq++,
1513 (unsigned long) msg_imap->num);
1514 CHECK_ERROR (f_imap, status);
1515 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1516 f_imap->state = IMAP_FETCH;
1517 }
1518 status = fetch_operation (f_imap, msg_imap, datebuf,
1519 sizeof datebuf, NULL);
1520 if (status != 0)
1521 return status;
1522 msg_imap->internal_date = strdup (datebuf);
1523 }
1524 else
1525 {
1526 date = msg_imap->internal_date;
1527 datep = &date;
1528 }
1529
1530 if (mu_parse_imap_date_time(datep, &tm, &tz) != 0)
1531 now = (time_t)-1;
1532 else
1533 now = mu_datetime_to_utc (&tm, &tz);
1534
1535 /* if the time was unparseable, or mktime() didn't like what we
1536 parsed, use the calendar time. */
1537 if (now == (time_t)-1)
1538 {
1539 time (&now);
1540 tm = *gmtime (&now);
1541 }
1542
1543 {
1544 char tmpbuf[MU_ENVELOPE_DATE_LENGTH+1];
1545 size_t n = mu_strftime (tmpbuf, sizeof tmpbuf,
1546 MU_ENVELOPE_DATE_FORMAT, &tm);
1547 n = mu_cpystr (buffer, tmpbuf, buflen);
1548 if (plen)
1549 *plen = n;
1550 }
1551 return 0;
1552 }
1553
1554 /* Attributes. */
1555 static int
1556 imap_attr_get_flags (mu_attribute_t attribute, int *pflags)
1557 {
1558 mu_message_t msg = mu_attribute_get_owner (attribute);
1559 msg_imap_t msg_imap = mu_message_get_owner (msg);
1560 m_imap_t m_imap = msg_imap->m_imap;
1561 f_imap_t f_imap = m_imap->f_imap;
1562 int status = 0;
1563
1564 /* Select first. */
1565 status = imap_messages_count (m_imap->mailbox, NULL);
1566 if (status != 0)
1567 return status;
1568
1569 /* Did we retrieve it alread ? */
1570 if (msg_imap->flags != 0)
1571 {
1572 if (pflags)
1573 *pflags = msg_imap->flags;
1574 return 0;
1575 }
1576
1577 if (f_imap->state == IMAP_NO_STATE)
1578 {
1579 status = imap_writeline (f_imap, "g%lu FETCH %lu FLAGS\r\n",
1580 (unsigned long) f_imap->seq++,
1581 (unsigned long) msg_imap->num);
1582 CHECK_ERROR (f_imap, status);
1583 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1584 f_imap->state = IMAP_FETCH;
1585 }
1586 status = fetch_operation (f_imap, msg_imap, NULL, 0, NULL);
1587 if (status == 0)
1588 {
1589 if (pflags)
1590 *pflags = msg_imap->flags;
1591 }
1592 return status;
1593 }
1594
1595 static int
1596 imap_attr_set_flags (mu_attribute_t attribute, int flag)
1597 {
1598 mu_message_t msg = mu_attribute_get_owner (attribute);
1599 msg_imap_t msg_imap = mu_message_get_owner (msg);
1600 m_imap_t m_imap = msg_imap->m_imap;
1601 f_imap_t f_imap = m_imap->f_imap;
1602 int status = 0;
1603
1604 /* Select first. */
1605 status = imap_messages_count (m_imap->mailbox, NULL);
1606 if (status != 0)
1607 return status;
1608
1609 /* If already set don't bother. */
1610 if (msg_imap->flags & flag)
1611 return 0;
1612
1613 /* The delete FLAG is not pass yet but only on the expunge. */
1614 if (flag & MU_ATTRIBUTE_DELETED)
1615 {
1616 msg_imap->flags |= MU_ATTRIBUTE_DELETED;
1617 flag &= ~MU_ATTRIBUTE_DELETED;
1618 }
1619
1620 if (f_imap->state == IMAP_NO_STATE)
1621 {
1622 char *abuf = malloc (1);
1623 if (abuf == NULL)
1624 return ENOMEM;
1625 *abuf = '\0';
1626 status = flags_to_string (&abuf, flag);
1627 if (status != 0)
1628 return status;
1629 /* No flags to send?? */
1630 if (*abuf == '\0')
1631 {
1632 free (abuf);
1633 return 0;
1634 }
1635 status = imap_writeline (f_imap, "g%lu STORE %lu +FLAGS.SILENT (%s)\r\n",
1636 (unsigned long) f_imap->seq++,
1637 (unsigned long) msg_imap->num,
1638 abuf);
1639 free (abuf);
1640 CHECK_ERROR (f_imap, status);
1641 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1642 msg_imap->flags |= flag;
1643 f_imap->state = IMAP_FETCH;
1644 }
1645 return fetch_operation (f_imap, msg_imap, NULL, 0, NULL);
1646 }
1647
1648 static int
1649 imap_attr_unset_flags (mu_attribute_t attribute, int flag)
1650 {
1651 mu_message_t msg = mu_attribute_get_owner (attribute);
1652 msg_imap_t msg_imap = mu_message_get_owner (msg);
1653 m_imap_t m_imap = msg_imap->m_imap;
1654 f_imap_t f_imap = m_imap->f_imap;
1655 int status = 0;
1656
1657 /* Select first. */
1658 status = imap_messages_count (m_imap->mailbox, NULL);
1659 if (status != 0)
1660 return status;
1661
1662 /* The delete FLAG is not pass yet but only on the expunge. */
1663 if (flag & MU_ATTRIBUTE_DELETED)
1664 {
1665 msg_imap->flags &= ~MU_ATTRIBUTE_DELETED;
1666 flag &= ~MU_ATTRIBUTE_DELETED;
1667 }
1668
1669 if (f_imap->state == IMAP_NO_STATE)
1670 {
1671 char *abuf = malloc (1);
1672 if (abuf == NULL)
1673 return ENOMEM;
1674 *abuf = '\0';
1675 status = flags_to_string (&abuf, flag);
1676 if (status != 0)
1677 return status;
1678 /* No flags to send?? */
1679 if (*abuf == '\0')
1680 {
1681 free (abuf);
1682 return 0;
1683 }
1684 status = imap_writeline (f_imap, "g%lu STORE %lu -FLAGS.SILENT (%s)\r\n",
1685 (unsigned long) f_imap->seq++,
1686 (unsigned long) msg_imap->num,
1687 abuf);
1688 free (abuf);
1689 CHECK_ERROR (f_imap, status);
1690 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1691 msg_imap->flags &= ~flag;
1692 f_imap->state = IMAP_FETCH;
1693 }
1694 return fetch_operation (f_imap, msg_imap, NULL, 0, NULL);
1695 }
1696
1697 /* Header. */
1698 static int
1699 imap_header_read (mu_header_t header, char *buffer,
1700 size_t buflen, mu_off_t offset,
1701 size_t *plen)
1702 {
1703 mu_message_t msg = mu_header_get_owner (header);
1704 msg_imap_t msg_imap = mu_message_get_owner (msg);
1705 m_imap_t m_imap = msg_imap->m_imap;
1706 f_imap_t f_imap = m_imap->f_imap;
1707 char *oldbuf = NULL;
1708 char newbuf[2];
1709 int status;
1710
1711 /* This is so annoying, a buffer len of 1 is a killer. If you have for
1712 example "\n" to retrieve from the server, IMAP will transform this to
1713 "\r\n" and since you ask for only 1, the server will send '\r' only.
1714 And ... '\r' will be stripped by (imap_readline()) the number of char
1715 read will be 0 which means we're done .... sigh ... So we guard by at
1716 least ask for 2 chars. */
1717 if (buflen == 1)
1718 {
1719 oldbuf = buffer;
1720 buffer = newbuf;
1721 buflen = 2;
1722 }
1723
1724 /* Start over. */
1725 if (offset == 0)
1726 msg_imap->header_lines = 0;
1727
1728 /* Select first. */
1729 status = imap_messages_count (m_imap->mailbox, NULL);
1730 if (status != 0)
1731 return status;
1732
1733 if (f_imap->state == IMAP_NO_STATE)
1734 {
1735 /* We strip the \r, but the offset/size on the imap server is with that
1736 octet so add it in the offset, since it's the number of lines. */
1737 if (msg_imap->part)
1738 {
1739 char *section = section_name (msg_imap);
1740 status = imap_writeline (f_imap,
1741 "g%lu FETCH %lu BODY.PEEK[%s.MIME]<%lu.%lu>\r\n",
1742 (unsigned long) f_imap->seq++,
1743 (unsigned long) msg_imap->num,
1744 (section) ? section : "",
1745 (unsigned long) (offset +
1746 msg_imap->header_lines),
1747 (unsigned long) buflen);
1748 if (section)
1749 free (section);
1750 }
1751 else
1752 status = imap_writeline (f_imap,
1753 "g%lu FETCH %lu BODY.PEEK[HEADER]<%lu.%lu>\r\n",
1754 (unsigned long) f_imap->seq++,
1755 (unsigned long) msg_imap->num,
1756 (unsigned long) (offset +
1757 msg_imap->header_lines),
1758 (unsigned long) buflen);
1759 CHECK_ERROR (f_imap, status);
1760 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1761 f_imap->state = IMAP_FETCH;
1762
1763 }
1764 status = fetch_operation (f_imap, msg_imap, buffer, buflen, plen);
1765 if (oldbuf)
1766 oldbuf[0] = buffer[0];
1767 return status;
1768 }
1769
1770 /* Body. */
1771 static int
1772 imap_body_size (mu_body_t body, size_t *psize)
1773 {
1774 mu_message_t msg = mu_body_get_owner (body);
1775 msg_imap_t msg_imap = mu_message_get_owner (msg);
1776 if (psize && msg_imap)
1777 {
1778 /* If there is a parent it means it is a sub message, IMAP does not give
1779 the full size of mime messages, so the mu_message_size was retrieve from
1780 doing a bodystructure and represents rather the mu_body_size. */
1781 if (msg_imap->parent)
1782 {
1783 *psize = msg_imap->mu_message_size - msg_imap->mu_message_lines;
1784 }
1785 else
1786 {
1787 if (msg_imap->body_size)
1788 *psize = msg_imap->body_size;
1789 else if (msg_imap->mu_message_size)
1790 *psize = msg_imap->mu_message_size
1791 - (msg_imap->header_size + msg_imap->header_lines);
1792 else
1793 *psize = 0;
1794 }
1795 }
1796 return 0;
1797 }
1798
1799 static int
1800 imap_body_lines (mu_body_t body, size_t *plines)
1801 {
1802 mu_message_t msg = mu_body_get_owner (body);
1803 msg_imap_t msg_imap = mu_message_get_owner (msg);
1804 if (plines && msg_imap)
1805 *plines = msg_imap->body_lines;
1806 return 0;
1807 }
1808
1809 /* FIXME: Send EISPIPE if trying to seek back. */
1810 static int
1811 imap_body_read (mu_stream_t stream, char *buffer, size_t buflen,
1812 mu_off_t offset, size_t *plen)
1813 {
1814 mu_body_t body = mu_stream_get_owner (stream);
1815 mu_message_t msg = mu_body_get_owner (body);
1816 msg_imap_t msg_imap = mu_message_get_owner (msg);
1817 m_imap_t m_imap = msg_imap->m_imap;
1818 f_imap_t f_imap = m_imap->f_imap;
1819 char *oldbuf = NULL;
1820 char newbuf[2];
1821 int status;
1822
1823 /* This is so annoying, a buffer len of 1 is a killer. If you have for
1824 example "\n" to retrieve from the server, IMAP will transform this to
1825 "\r\n" and since you ask for only 1, the server will send '\r' only.
1826 And ... '\r' will be stripped by (imap_readline()) the number of char
1827 read will be 0 which means we're done .... sigh ... So we guard by at
1828 least ask for 2 chars. */
1829 if (buflen == 1)
1830 {
1831 oldbuf = buffer;
1832 buffer = newbuf;
1833 buflen = 2;
1834 }
1835
1836 /* Start over. */
1837 if (offset == 0)
1838 {
1839 msg_imap->body_lines = 0;
1840 msg_imap->body_size = 0;
1841 }
1842
1843 /* Select first. */
1844 status = imap_messages_count (m_imap->mailbox, NULL);
1845 if (status != 0)
1846 return status;
1847
1848 if (f_imap->state == IMAP_NO_STATE)
1849 {
1850 /* We strip the \r, but the offset/size on the imap server is with the
1851 octet, so add it since it's the number of lines. */
1852 if (msg_imap->part)
1853 {
1854 char *section = section_name (msg_imap);
1855 status = imap_writeline (f_imap,
1856 "g%lu FETCH %lu BODY.PEEK[%s]<%lu.%lu>\r\n",
1857 (unsigned long) f_imap->seq++,
1858 (unsigned long) msg_imap->num,
1859 (section) ? section: "",
1860 (unsigned long) (offset +
1861 msg_imap->body_lines),
1862 (unsigned long) buflen);
1863 if (section)
1864 free (section);
1865 }
1866 else
1867 status = imap_writeline (f_imap,
1868 "g%lu FETCH %lu BODY.PEEK[TEXT]<%lu.%lu>\r\n",
1869 (unsigned long) f_imap->seq++,
1870 (unsigned long) msg_imap->num,
1871 (unsigned long) (offset +
1872 msg_imap->body_lines),
1873 (unsigned long) buflen);
1874 CHECK_ERROR (f_imap, status);
1875 MU_DEBUG (m_imap->mailbox->debug, MU_DEBUG_PROT, f_imap->buffer);
1876 f_imap->state = IMAP_FETCH;
1877
1878 }
1879 status = fetch_operation (f_imap, msg_imap, buffer, buflen, plen);
1880 if (oldbuf)
1881 oldbuf[0] = buffer[0];
1882 return status;
1883 }
1884
1885 static int
1886 imap_body_get_transport2 (mu_stream_t stream, mu_transport_t *pin,
1887 mu_transport_t *pout)
1888 {
1889 mu_body_t body = mu_stream_get_owner (stream);
1890 mu_message_t msg = mu_body_get_owner (body);
1891 msg_imap_t msg_imap = mu_message_get_owner (msg);
1892 return imap_get_transport2 (msg_imap, pin, pout);
1893 }
1894
1895
1896 static int
1897 imap_get_transport2 (msg_imap_t msg_imap, mu_transport_t *pin, mu_transport_t *pout)
1898 {
1899 if ( msg_imap
1900 && msg_imap->m_imap
1901 && msg_imap->m_imap->f_imap
1902 && msg_imap->m_imap->f_imap->folder)
1903 return mu_stream_get_transport2 (msg_imap->m_imap->f_imap->folder->stream,
1904 pin, pout);
1905 return EINVAL;
1906 }
1907
1908 /* Since so many operations are fetch, we regoup this into one function. */
1909 static int
1910 fetch_operation (f_imap_t f_imap, msg_imap_t msg_imap, char *buffer,
1911 size_t buflen, size_t *plen)
1912 {
1913 int status = 0;
1914
1915 switch (f_imap->state)
1916 {
1917 case IMAP_FETCH:
1918 status = imap_send (f_imap);
1919 CHECK_EAGAIN (f_imap, status);
1920 mu_stream_truncate (f_imap->string.stream, 0);
1921 f_imap->string.offset = 0;
1922 f_imap->string.nleft = 0;
1923 f_imap->string.type = IMAP_NO_STATE;
1924 f_imap->string.msg_imap = msg_imap;
1925 f_imap->state = IMAP_FETCH_ACK;
1926
1927 case IMAP_FETCH_ACK:
1928 status = imap_parse (f_imap);
1929 CHECK_EAGAIN (f_imap, status);
1930 if (f_imap->selected)
1931 MU_DEBUG (f_imap->selected->mailbox->debug, MU_DEBUG_PROT,
1932 f_imap->buffer);
1933
1934 default:
1935 break;
1936 }
1937
1938 f_imap->state = IMAP_NO_STATE;
1939
1940 /* The server may have timeout any case connection is gone away. */
1941 if (status == 0 && f_imap->isopen == 0 && f_imap->string.offset == 0)
1942 status = MU_ERR_CONN_CLOSED;
1943
1944 if (buffer)
1945 mu_stream_read (f_imap->string.stream, buffer, buflen, 0, plen);
1946 else if (plen)
1947 *plen = 0;
1948 mu_stream_truncate (f_imap->string.stream, 0);
1949 f_imap->string.offset = 0;
1950 f_imap->string.nleft = 0;
1951 f_imap->string.type = IMAP_NO_STATE;
1952 f_imap->string.msg_imap = NULL;
1953 return status;
1954 }
1955
1956 /* Decide whether the message came from the same folder as the mailbox. */
1957 static int
1958 is_same_folder (mu_mailbox_t mailbox, mu_message_t msg)
1959 {
1960 mu_mailbox_t mbox = NULL;
1961 mu_message_get_mailbox (msg, &mbox);
1962 return (mbox != NULL && mbox->url != NULL
1963 && mu_url_is_same_scheme (mbox->url, mailbox->url)
1964 && mu_url_is_same_host (mbox->url, mailbox->url)
1965 && mu_url_is_same_port (mbox->url, mailbox->url));
1966 }
1967
1968 /* Convert flag attribute to IMAP String attributes. */
1969 static int
1970 flags_to_string (char **pbuf, int flag)
1971 {
1972 char *abuf = *pbuf;
1973 if (flag & MU_ATTRIBUTE_DELETED)
1974 {
1975 char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Deleted") + 2);
1976 if (tmp == NULL)
1977 {
1978 free (abuf);
1979 return ENOMEM;
1980 }
1981 abuf = tmp;
1982 if (*abuf)
1983 strcat (abuf, " ");
1984 strcat (abuf, "\\Deleted");
1985 }
1986 if (flag & MU_ATTRIBUTE_READ)
1987 {
1988 char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Seen") + 2);
1989 if (tmp == NULL)
1990 {
1991 free (abuf);
1992 return ENOMEM;
1993 }
1994 abuf = tmp;
1995 if (*abuf)
1996 strcat (abuf, " ");
1997 strcat (abuf, "\\Seen");
1998 }
1999 if (flag & MU_ATTRIBUTE_ANSWERED)
2000 {
2001 char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Answered") + 2);
2002 if (tmp == NULL)
2003 {
2004 free (abuf);
2005 return ENOMEM;
2006 }
2007 abuf = tmp;
2008 if (*abuf)
2009 strcat (abuf, " ");
2010 strcat (abuf, "\\Answered");
2011 }
2012 if (flag & MU_ATTRIBUTE_DRAFT)
2013 {
2014 char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Draft") + 2);
2015 if (tmp == NULL)
2016 {
2017 free (abuf);
2018 return ENOMEM;
2019 }
2020 abuf = tmp;
2021 if (*abuf)
2022 strcat (abuf, " ");
2023 strcat (abuf, "\\Draft");
2024 }
2025 if (flag & MU_ATTRIBUTE_FLAGGED)
2026 {
2027 char *tmp = realloc (abuf, strlen (abuf) + strlen ("\\Flagged") + 2);
2028 if (tmp == NULL)
2029 {
2030 free (abuf);
2031 return ENOMEM;
2032 }
2033 abuf = tmp;
2034 if (*abuf)
2035 strcat (abuf, " ");
2036 strcat (abuf, "\\Flagged");
2037 }
2038 *pbuf = abuf;
2039 return 0;
2040 }
2041
2042 /* Convert a suite of number to IMAP message number. */
2043 static int
2044 add_number (char **pset, size_t start, size_t end)
2045 {
2046 char *buf = NULL;
2047 char *set;
2048 char *tmp;
2049 size_t set_len = 0;
2050
2051 if (pset == NULL)
2052 return 0;
2053
2054 set = *pset;
2055
2056 if (set)
2057 set_len = strlen (set);
2058
2059 /* We had a previous seqence. */
2060 if (start == 0)
2061 /* nothing */;
2062 else if (start != end)
2063 mu_asprintf (&buf, "%lu:%lu",
2064 (unsigned long) start, (unsigned long) end);
2065 else
2066 mu_asprintf (&buf, "%lu", (unsigned long) start);
2067
2068 if (set_len)
2069 tmp = realloc (set, set_len + strlen (buf) + 2 /* null and comma */);
2070 else
2071 tmp = calloc (strlen (buf) + 1, 1);
2072
2073 if (tmp == NULL)
2074 {
2075 free (set);
2076 free (buf);
2077 return ENOMEM;
2078 }
2079 set = tmp;
2080
2081 /* If we had something add a comma separator. */
2082 if (set_len)
2083 strcat (set, ",");
2084 strcat (set, buf);
2085 free (buf);
2086
2087 *pset = set;
2088 return 0;
2089 }
2090
2091 static int
2092 delete_to_string (m_imap_t m_imap, char **pset)
2093 {
2094 int status;
2095 size_t i, prev = 0, is_range = 0;
2096 size_t start = 0, cur = 0;
2097 char *set = NULL;
2098
2099 /* Reformat the number for IMAP. */
2100 for (i = 0; i < m_imap->imessages_count; ++i)
2101 {
2102 if (m_imap->imessages[i]
2103 && (m_imap->imessages[i]->flags & MU_ATTRIBUTE_DELETED))
2104 {
2105 cur = m_imap->imessages[i]->num;
2106 /* The first number. */
2107 if (start == 0)
2108 {
2109 start = prev = cur;
2110 }
2111 /* Is it a sequence? */
2112 else if ((prev + 1) == cur)
2113 {
2114 prev = cur;
2115 is_range = 1;
2116 }
2117 continue;
2118 }
2119
2120 if (start)
2121 {
2122 status = add_number (&set, start, cur);
2123 if (status != 0)
2124 return status;
2125 start = 0;
2126 prev = 0;
2127 cur = 0;
2128 is_range = 0;
2129 }
2130 } /* for () */
2131
2132 if (start)
2133 {
2134 status = add_number (&set, start, cur);
2135 if (status != 0)
2136 return status;
2137 }
2138 *pset = set;
2139 return 0;
2140 }
2141
2142 #endif
...@@ -29,6 +29,9 @@ mu_imap_rename (mu_imap_t imap, const char *mailbox, const char *new_mailbox) ...@@ -29,6 +29,9 @@ mu_imap_rename (mu_imap_t imap, const char *mailbox, const char *new_mailbox)
29 char const *argv[3]; 29 char const *argv[3];
30 static struct imap_command com; 30 static struct imap_command com;
31 31
32 if (!mailbox || !new_mailbox)
33 return EINVAL;
34
32 argv[0] = "RENAME"; 35 argv[0] = "RENAME";
33 argv[1] = mailbox; 36 argv[1] = mailbox;
34 argv[2] = new_mailbox; 37 argv[2] = new_mailbox;
......
...@@ -57,6 +57,7 @@ _mu_imap_response_list_create (mu_imap_t imap, mu_list_t *plist) ...@@ -57,6 +57,7 @@ _mu_imap_response_list_create (mu_imap_t imap, mu_list_t *plist)
57 57
58 #define IS_LBRACE(p) ((p)[0] == '(') 58 #define IS_LBRACE(p) ((p)[0] == '(')
59 #define IS_RBRACE(p) ((p)[0] == ')') 59 #define IS_RBRACE(p) ((p)[0] == ')')
60 #define IS_NIL(p) (strcmp (p, "NIL") == 0)
60 61
61 static struct imap_list_element * 62 static struct imap_list_element *
62 _new_imap_list_element (mu_imap_t imap, enum imap_eltype type) 63 _new_imap_list_element (mu_imap_t imap, enum imap_eltype type)
...@@ -181,6 +182,7 @@ _parse_element (struct parsebuf *pb) ...@@ -181,6 +182,7 @@ _parse_element (struct parsebuf *pb)
181 182
182 if (IS_RBRACE (tok)) 183 if (IS_RBRACE (tok))
183 { 184 {
185 parsebuf_gettok (pb);
184 elt = _new_imap_list_element (pb->pb_imap, imap_eltype_list); 186 elt = _new_imap_list_element (pb->pb_imap, imap_eltype_list);
185 if (!elt) 187 if (!elt)
186 { 188 {
...@@ -203,6 +205,16 @@ _parse_element (struct parsebuf *pb) ...@@ -203,6 +205,16 @@ _parse_element (struct parsebuf *pb)
203 parsebuf_seterr (pb, MU_ERR_PARSE); 205 parsebuf_seterr (pb, MU_ERR_PARSE);
204 return NULL; 206 return NULL;
205 } 207 }
208 else if (IS_NIL (tok))
209 {
210 elt = _new_imap_list_element (pb->pb_imap, imap_eltype_list);
211 if (!elt)
212 {
213 parsebuf_seterr (pb, ENOMEM);
214 return NULL;
215 }
216 elt->v.list = NULL;
217 }
206 else 218 else
207 { 219 {
208 char *s; 220 char *s;
...@@ -255,6 +267,12 @@ _mu_imap_list_element_is_string (struct imap_list_element *elt, ...@@ -255,6 +267,12 @@ _mu_imap_list_element_is_string (struct imap_list_element *elt,
255 return strcmp (elt->v.string, str) == 0; 267 return strcmp (elt->v.string, str) == 0;
256 } 268 }
257 269
270 int
271 _mu_imap_list_element_is_nil (struct imap_list_element *elt)
272 {
273 return elt->type == imap_eltype_list && mu_list_is_empty (elt->v.list);
274 }
275
258 struct imap_list_element * 276 struct imap_list_element *
259 _mu_imap_list_at (mu_list_t list, int idx) 277 _mu_imap_list_at (mu_list_t list, int idx)
260 { 278 {
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
19 # include <config.h> 19 # include <config.h>
20 #endif 20 #endif
21 21
22 #include <errno.h>
22 #include <mailutils/imap.h> 23 #include <mailutils/imap.h>
23 #include <mailutils/sys/imap.h> 24 #include <mailutils/sys/imap.h>
24 25
...@@ -28,6 +29,9 @@ mu_imap_subscribe (mu_imap_t imap, const char *mailbox) ...@@ -28,6 +29,9 @@ mu_imap_subscribe (mu_imap_t imap, const char *mailbox)
28 char const *argv[2]; 29 char const *argv[2];
29 static struct imap_command com; 30 static struct imap_command com;
30 31
32 if (!mailbox)
33 return EINVAL;
34
31 argv[0] = "SUBSCRIBE"; 35 argv[0] = "SUBSCRIBE";
32 argv[1] = mailbox; 36 argv[1] = mailbox;
33 37
......
1 ## This file is part of GNU Mailutils.
2 ## Copyright (C) 2003, 2005, 2006, 2007, 2010, 2011 Free Software
3 ## Foundation, Inc.
4 ##
5 ## GNU Mailutils is free software; you can redistribute it and/or
6 ## modify it under the terms of the GNU General Public License as
7 ## published by the Free Software Foundation; either version 3, or (at
8 ## your option) any later version.
9 ##
10 ## GNU Mailutils is distributed in the hope that it will be useful, but
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ## General Public License for more details.
14 ##
15 ## You should have received a copy of the GNU General Public License
16 ## along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
17
18 noinst_PROGRAMS = \
19 imapfolder
20
21 INCLUDES = @MU_LIB_COMMON_INCLUDES@
22 LDADD = \
23 @MU_LIB_IMAP@\
24 @MU_LIB_AUTH@\
25 @MU_AUTHLIBS@\
26 @MU_LIB_MAILUTILS@
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2011 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <mailutils/mailutils.h>
23
24 struct command
25 {
26 char *verb;
27 int nargs;
28 char *args;
29 void (*handler) (mu_folder_t folder, char **argv);
30 };
31
32 static int
33 _print_list_entry (void *item, void *data)
34 {
35 struct mu_list_response *resp = item;
36 mu_printf ("%c%c %c %4d %s\n",
37 (resp->type & MU_FOLDER_ATTRIBUTE_DIRECTORY) ? 'd' : '-',
38 (resp->type & MU_FOLDER_ATTRIBUTE_FILE) ? 'f' : '-',
39 resp->separator ? resp->separator : ' ',
40 resp->level,
41 resp->name);
42 return 0;
43 }
44
45 static void
46 com_list (mu_folder_t folder, char **argv)
47 {
48 int rc;
49 mu_list_t list;
50
51 mu_printf ("listing %s %s\n", argv[0], argv[1]);
52 rc = mu_folder_list (folder, argv[0], argv[1], 0, &list);
53 if (rc)
54 mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_list", argv[0], rc);
55 else
56 {
57 mu_list_foreach (list, _print_list_entry, NULL);
58 mu_list_destroy (&list);
59 }
60 }
61
62 static void
63 com_lsub (mu_folder_t folder, char **argv)
64 {
65 int rc;
66 mu_list_t list;
67
68 mu_printf ("listing subscriptions for '%s' '%s'\n", argv[0], argv[1]);
69 rc = mu_folder_lsub (folder, argv[0], argv[1], &list);
70 if (rc)
71 mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_lsub", argv[0], rc);
72 else
73 {
74 mu_list_foreach (list, _print_list_entry, NULL);
75 mu_list_destroy (&list);
76 }
77 }
78
79 static void
80 com_delete (mu_folder_t folder, char **argv)
81 {
82 int rc;
83
84 mu_printf ("deleting %s\n", argv[0]);
85 rc = mu_folder_delete (folder, argv[0]);
86 if (rc)
87 mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_lsub", argv[0], rc);
88 else
89 mu_printf ("delete successful\n");
90 }
91
92 static void
93 com_rename (mu_folder_t folder, char **argv)
94 {
95 int rc;
96
97 mu_printf ("renaming %s to %s\n", argv[0], argv[1]);
98 rc = mu_folder_rename (folder, argv[0], argv[1]);
99 if (rc)
100 mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_rename", argv[0], rc);
101 else
102 mu_printf ("rename successful\n");
103 }
104
105 static void
106 com_subscribe (mu_folder_t folder, char **argv)
107 {
108 int rc;
109
110 mu_printf ("subscribing %s\n", argv[0]);
111 rc = mu_folder_subscribe (folder, argv[0]);
112 if (rc)
113 mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_subscribe", argv[0], rc);
114 else
115 mu_printf ("subscribe successful\n");
116 }
117
118 static void
119 com_unsubscribe (mu_folder_t folder, char **argv)
120 {
121 int rc;
122
123 mu_printf ("unsubscribing %s\n", argv[0]);
124 rc = mu_folder_unsubscribe (folder, argv[0]);
125 if (rc)
126 mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_unsubscribe", argv[0], rc);
127 else
128 mu_printf ("unsubscribe successful\n");
129 }
130
131 static struct command comtab[] = {
132 { "list", 2, "REF MBX", com_list },
133 { "lsub", 2, "REF MBX", com_lsub },
134 { "delete", 1, "MBX", com_delete },
135 { "rename", 2, "OLD NEW", com_rename },
136 { "delete", 1, "MBOX", com_delete },
137 { "subscribe", 1, "MBX", com_subscribe },
138 { "unsubscribe", 1, "MBX", com_unsubscribe },
139 { NULL }
140 };
141
142 static struct command *
143 find_command (const char *name)
144 {
145 struct command *cp;
146
147 for (cp = comtab; cp->verb; cp++)
148 if (strcmp (cp->verb, name) == 0)
149 return cp;
150 return NULL;
151 }
152
153 static void
154 usage ()
155 {
156 struct command *cp;
157
158 mu_printf (
159 "usage: %s [debug=SPEC] url=URL OP ARG [ARG...] [OP ARG [ARG...]...]\n",
160 mu_program_name);
161 mu_printf ("OPerations and corresponding ARGuments are:\n");
162 for (cp = comtab; cp->verb; cp++)
163 mu_printf (" %s %s\n", cp->verb, cp->args);
164 }
165
166 int
167 main (int argc, char **argv)
168 {
169 int i;
170 int rc;
171 mu_folder_t folder;
172 char *fname = NULL;
173
174 mu_set_program_name (argv[0]);
175 mu_registrar_record (mu_imap_record);
176 mu_registrar_record (mu_imaps_record);
177
178 if (argc == 1)
179 {
180 usage ();
181 exit (0);
182 }
183
184 for (i = 1; i < argc; i++)
185 {
186 if (strncmp (argv[i], "debug=", 6) == 0)
187 mu_debug_parse_spec (argv[i] + 6);
188 else if (strncmp (argv[i], "url=", 4) == 0)
189 fname = argv[i] + 4;
190 else
191 break;
192 }
193
194 if (!fname)
195 {
196 mu_error ("URL not specified");
197 exit (1);
198 }
199
200 rc = mu_folder_create (&folder, fname);
201 if (rc)
202 {
203 mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_create", fname, rc);
204 return 1;
205 }
206
207 rc = mu_folder_open (folder, MU_STREAM_READ);
208 if (rc)
209 {
210 mu_diag_funcall (MU_DIAG_ERROR, "mu_folder_open", fname, rc);
211 return 1;
212 }
213
214 while (i < argc)
215 {
216 char *comargs[2];
217 struct command *cmd;
218
219 cmd = find_command (argv[i]);
220 if (!cmd)
221 {
222 mu_error ("unknown command %s\n", argv[i]);
223 break;
224 }
225
226 i++;
227 if (i + cmd->nargs > argc)
228 {
229 mu_error ("not enough arguments for %s", cmd->verb);
230 break;
231 }
232 memcpy (comargs, argv + i, cmd->nargs * sizeof (comargs[0]));
233 i += cmd->nargs;
234
235 cmd->handler (folder, comargs);
236 }
237
238 mu_folder_close (folder);
239 mu_folder_destroy (&folder);
240
241 return 0;
242 }
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
19 # include <config.h> 19 # include <config.h>
20 #endif 20 #endif
21 21
22 #include <errno.h>
22 #include <mailutils/imap.h> 23 #include <mailutils/imap.h>
23 #include <mailutils/sys/imap.h> 24 #include <mailutils/sys/imap.h>
24 25
...@@ -28,6 +29,9 @@ mu_imap_unsubscribe (mu_imap_t imap, const char *mailbox) ...@@ -28,6 +29,9 @@ mu_imap_unsubscribe (mu_imap_t imap, const char *mailbox)
28 char const *argv[2]; 29 char const *argv[2];
29 static struct imap_command com; 30 static struct imap_command com;
30 31
32 if (!mailbox)
33 return EINVAL;
34
31 argv[0] = "UNSUBSCRIBE"; 35 argv[0] = "UNSUBSCRIBE";
32 argv[1] = mailbox; 36 argv[1] = mailbox;
33 37
......
...@@ -20,18 +20,15 @@ ...@@ -20,18 +20,15 @@
20 # include <config.h> 20 # include <config.h>
21 #endif 21 #endif
22 22
23 #ifdef ENABLE_IMAP
24
25 #include <errno.h> 23 #include <errno.h>
26 #include <stdlib.h> 24 #include <stdlib.h>
27 #include <string.h> 25 #include <string.h>
28 #include <errno.h> 26 #include <errno.h>
29 #ifdef HAVE_STRINGS_H 27 #include <string.h>
30 # include <strings.h>
31 #endif
32 28
33 #include <mailutils/sys/registrar.h> 29 #include <mailutils/sys/registrar.h>
34 #include <mailutils/sys/url.h> 30 #include <mailutils/sys/url.h>
31 #include <mailutils/sys/imap.h>
35 32
36 static void url_imap_destroy (mu_url_t url); 33 static void url_imap_destroy (mu_url_t url);
37 34
...@@ -47,7 +44,7 @@ url_imap_destroy (mu_url_t url MU_ARG_UNUSED) ...@@ -47,7 +44,7 @@ url_imap_destroy (mu_url_t url MU_ARG_UNUSED)
47 */ 44 */
48 45
49 int 46 int
50 _url_imap_init (mu_url_t url) 47 _mu_imap_url_init (mu_url_t url)
51 { 48 {
52 if (url->port == 0) 49 if (url->port == 0)
53 url->port = MU_IMAP_PORT; 50 url->port = MU_IMAP_PORT;
...@@ -78,7 +75,7 @@ _url_imap_init (mu_url_t url) ...@@ -78,7 +75,7 @@ _url_imap_init (mu_url_t url)
78 */ 75 */
79 76
80 int 77 int
81 _url_imaps_init (mu_url_t url) 78 _mu_imaps_url_init (mu_url_t url)
82 { 79 {
83 if (url->port == 0) 80 if (url->port == 0)
84 url->port = MU_IMAPS_PORT; 81 url->port = MU_IMAPS_PORT;
...@@ -102,4 +99,3 @@ _url_imaps_init (mu_url_t url) ...@@ -102,4 +99,3 @@ _url_imaps_init (mu_url_t url)
102 return 0; 99 return 0;
103 } 100 }
104 101
105 #endif /* ENABLE_IMAP */
......
...@@ -160,7 +160,7 @@ imap_prompt_env () ...@@ -160,7 +160,7 @@ imap_prompt_env ()
160 160
161 /* Callbacks */ 161 /* Callbacks */
162 static void 162 static void
163 imap_popauth_callback (void *data, int code, size_t sdat, void *pdat) 163 imap_preauth_callback (void *data, int code, size_t sdat, void *pdat)
164 { 164 {
165 const char *text = pdat; 165 const char *text = pdat;
166 if (text) 166 if (text)
...@@ -425,7 +425,7 @@ com_connect (int argc, char **argv) ...@@ -425,7 +425,7 @@ com_connect (int argc, char **argv)
425 425
426 /* Set callbacks */ 426 /* Set callbacks */
427 mu_imap_register_callback_function (imap, MU_IMAP_CB_PREAUTH, 427 mu_imap_register_callback_function (imap, MU_IMAP_CB_PREAUTH,
428 imap_popauth_callback, 428 imap_preauth_callback,
429 NULL); 429 NULL);
430 mu_imap_register_callback_function (imap, MU_IMAP_CB_BYE, 430 mu_imap_register_callback_function (imap, MU_IMAP_CB_BYE,
431 imap_bye_callback, 431 imap_bye_callback,
......