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.
Showing
23 changed files
with
813 additions
and
4636 deletions
... | @@ -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 | ... | ... |
libproto/imap/fake-folder.c
deleted
100644 → 0
... | @@ -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 | ... | ... |
libproto/imap/mbox.c
deleted
100644 → 0
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 | ... | ... |
libproto/imap/tests/.gitignore
0 → 100644
1 | imapfolder |
libproto/imap/tests/Makefile.am
0 → 100644
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@ |
libproto/imap/tests/imapfolder.c
0 → 100644
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, | ... | ... |
-
Please register or sign in to post a comment