imap client: initial implementation of fetch.
* include/mailutils/imap.h (mu_imap_fetch): New proto. (MU_IMAP_CB_FETCH): New callback code. (MU_IMAP_FETCH_BODY,MU_IMAP_FETCH_BODYSTRUCTURE) (MU_IMAP_FETCH_ENVELOPE,MU_IMAP_FETCH_FLAGS) (MU_IMAP_FETCH_INTERNALDATE,MU_IMAP_FETCH_RFC822_SIZE) (MU_IMAP_FETCH_UID): New constants. (mu_imap_fetch_body,mu_imap_fetch_bodystructure) (mu_imap_fetch_envelope,mu_imap_fetch_flags) (mu_imap_fetch_internaldate,mu_imap_fetch_rfc822_size) (mu_imap_fetch_uid,mu_imap_fetch_response): New structures. * include/mailutils/sys/imap.h (MU_IMAP_SET_XSCRIPT_MASK) (MU_IMAP_CLR_XSCRIPT_MASK): New macros. (mu_imap_client_state)<MU_IMAP_FETCH_RX>: New state. (_mu_imap_list_nth_element_is_string): New proto. * libproto/imap/fetch.c: New file. * libproto/imap/Makefile.am (libmu_imap_la_SOURCES): Add fetch.c * libproto/imap/carrier.c (mu_imap_set_carrier): Update call to mu_imapio_create. * libproto/imap/resplist.c (_mu_imap_list_nth_element_is_string): New function. * libproto/imap/resproc.c (_process_unsolicited_response): Handle FETCH response. * libproto/imap/trace.c (mu_imap_trace_mask): Call mu_imapio_trace_payload if needed. * mu/imap.c (imap_bad_callback): New function. (com_disconnect): Register imap_bad_callback. (com_fetch): New function. (imap_comtab): Add the "fetch" command. * mu/mu.h (mutool_command) <flags>: New member. * mu/pop.c (pop_comtab): Update initializers. * mu/shell.c (default_comtab): Update initializers. (execute_line): Rewrite using incremental wordsplit. This allows for optionally coalescing arguments with indices >= argmax into one argument. This is useful for such commands as "prompt" and "fetch".
Showing
13 changed files
with
957 additions
and
82 deletions
... | @@ -560,7 +560,6 @@ fetch_bodystructure0 (mu_message_t message, int extension) | ... | @@ -560,7 +560,6 @@ fetch_bodystructure0 (mu_message_t message, int extension) |
560 | 560 | ||
561 | mu_message_get_header (message, &header); | 561 | mu_message_get_header (message, &header); |
562 | 562 | ||
563 | |||
564 | /* The subtype. */ | 563 | /* The subtype. */ |
565 | if (mu_header_aget_value (header, MU_HEADER_CONTENT_TYPE, &buffer) == 0) | 564 | if (mu_header_aget_value (header, MU_HEADER_CONTENT_TYPE, &buffer) == 0) |
566 | { | 565 | { | ... | ... |
... | @@ -21,6 +21,7 @@ | ... | @@ -21,6 +21,7 @@ |
21 | #include <mailutils/iterator.h> | 21 | #include <mailutils/iterator.h> |
22 | #include <mailutils/debug.h> | 22 | #include <mailutils/debug.h> |
23 | #include <mailutils/stream.h> | 23 | #include <mailutils/stream.h> |
24 | #include <mailutils/util.h> | ||
24 | #include <mailutils/kwd.h> | 25 | #include <mailutils/kwd.h> |
25 | 26 | ||
26 | #ifdef __cplusplus | 27 | #ifdef __cplusplus |
... | @@ -58,6 +59,8 @@ int mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t *passoc); | ... | @@ -58,6 +59,8 @@ int mu_imap_id (mu_imap_t imap, char **idenv, mu_assoc_t *passoc); |
58 | 59 | ||
59 | int mu_imap_noop (mu_imap_t imap); | 60 | int mu_imap_noop (mu_imap_t imap); |
60 | 61 | ||
62 | int mu_imap_fetch (mu_imap_t imap, const char *msgset, const char *items); | ||
63 | |||
61 | int mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier); | 64 | int mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier); |
62 | int mu_imap_get_carrier (mu_imap_t imap, mu_stream_t *pcarrier); | 65 | int mu_imap_get_carrier (mu_imap_t imap, mu_stream_t *pcarrier); |
63 | 66 | ||
... | @@ -104,7 +107,7 @@ extern struct mu_kwd _mu_imap_status_name_table[]; | ... | @@ -104,7 +107,7 @@ extern struct mu_kwd _mu_imap_status_name_table[]; |
104 | 107 | ||
105 | /* The following five callbacks correspond to members of struct | 108 | /* The following five callbacks correspond to members of struct |
106 | mu_imap_stat and take a pointer to struct mu_imap_stat as their | 109 | mu_imap_stat and take a pointer to struct mu_imap_stat as their |
107 | only argument. */ | 110 | PDAT argument. SDAT is always 0. */ |
108 | #define MU_IMAP_CB_PERMANENT_FLAGS 0 | 111 | #define MU_IMAP_CB_PERMANENT_FLAGS 0 |
109 | #define MU_IMAP_CB_MESSAGE_COUNT 1 | 112 | #define MU_IMAP_CB_MESSAGE_COUNT 1 |
110 | #define MU_IMAP_CB_RECENT_COUNT 2 | 113 | #define MU_IMAP_CB_RECENT_COUNT 2 |
... | @@ -113,15 +116,20 @@ extern struct mu_kwd _mu_imap_status_name_table[]; | ... | @@ -113,15 +116,20 @@ extern struct mu_kwd _mu_imap_status_name_table[]; |
113 | #define MU_IMAP_CB_UIDVALIDITY 5 | 116 | #define MU_IMAP_CB_UIDVALIDITY 5 |
114 | 117 | ||
115 | /* The following callbacks correspond to server responses and take two | 118 | /* The following callbacks correspond to server responses and take two |
116 | argument: a response code (see MU_IMAP_RESPONSE, below), and | 119 | arguments: a response code (see MU_IMAP_RESPONSE, below) in SDAT, and |
117 | human-readable text string as returned by the server. The latter can | 120 | human-readable text string as returned by the server in PDAT. The |
118 | be NULL. */ | 121 | latter can be NULL. */ |
119 | #define MU_IMAP_CB_OK 6 | 122 | #define MU_IMAP_CB_OK 6 |
120 | #define MU_IMAP_CB_NO 7 | 123 | #define MU_IMAP_CB_NO 7 |
121 | #define MU_IMAP_CB_BAD 8 | 124 | #define MU_IMAP_CB_BAD 8 |
122 | #define MU_IMAP_CB_BYE 9 | 125 | #define MU_IMAP_CB_BYE 9 |
123 | #define MU_IMAP_CB_PREAUTH 10 | 126 | #define MU_IMAP_CB_PREAUTH 10 |
124 | #define _MU_IMAP_CB_MAX 11 | 127 | |
128 | /* FETCH callback. Arguments: SDAT - message sequence number, PDAT - a | ||
129 | list (mu_list_t) of union mu_imap_fetch_response (see below). */ | ||
130 | #define MU_IMAP_CB_FETCH 11 | ||
131 | |||
132 | #define _MU_IMAP_CB_MAX 12 | ||
125 | 133 | ||
126 | typedef void (*mu_imap_callback_t) (void *, int code, size_t sdat, void *pdat); | 134 | typedef void (*mu_imap_callback_t) (void *, int code, size_t sdat, void *pdat); |
127 | 135 | ||
... | @@ -144,7 +152,92 @@ void mu_imap_register_callback_function (mu_imap_t imap, int code, | ... | @@ -144,7 +152,92 @@ void mu_imap_register_callback_function (mu_imap_t imap, int code, |
144 | #define MU_IMAP_RESPONSE_UNSEEN 10 | 152 | #define MU_IMAP_RESPONSE_UNSEEN 10 |
145 | 153 | ||
146 | extern struct mu_kwd mu_imap_response_codes[]; | 154 | extern struct mu_kwd mu_imap_response_codes[]; |
155 | |||
156 | /* FETCH Response Codes */ | ||
157 | |||
158 | /* BODY[<section>]<<origin octet>> */ | ||
159 | #define MU_IMAP_FETCH_BODY 0 | ||
160 | /* BODY & BODYSTRUCTURE */ | ||
161 | #define MU_IMAP_FETCH_BODYSTRUCTURE 1 | ||
162 | /* ENVELOPE */ | ||
163 | #define MU_IMAP_FETCH_ENVELOPE 2 | ||
164 | /* FLAGS */ | ||
165 | #define MU_IMAP_FETCH_FLAGS 3 | ||
166 | /* INTERNALDATE */ | ||
167 | #define MU_IMAP_FETCH_INTERNALDATE 4 | ||
168 | /* RFC822.SIZE */ | ||
169 | #define MU_IMAP_FETCH_RFC822_SIZE 5 | ||
170 | /* UID */ | ||
171 | #define MU_IMAP_FETCH_UID 6 | ||
172 | |||
173 | struct mu_imap_fetch_body | ||
174 | { | ||
175 | int type; | ||
176 | size_t *partv; | ||
177 | size_t partc; | ||
178 | char *key; | ||
179 | char *text; | ||
180 | }; | ||
181 | |||
182 | struct mu_imap_fetch_bodystructure | ||
183 | { | ||
184 | int type; | ||
185 | //FIXME? | ||
186 | }; | ||
147 | 187 | ||
188 | struct mu_imap_fetch_envelope | ||
189 | { | ||
190 | int type; | ||
191 | struct tm date; | ||
192 | struct mu_timezone tz; | ||
193 | char *subject; | ||
194 | mu_address_t from; | ||
195 | mu_address_t sender; | ||
196 | mu_address_t reply_to; | ||
197 | mu_address_t to; | ||
198 | mu_address_t cc; | ||
199 | mu_address_t bcc; | ||
200 | char *in_reply_to; | ||
201 | char *message_id; | ||
202 | }; | ||
203 | |||
204 | struct mu_imap_fetch_flags | ||
205 | { | ||
206 | int type; | ||
207 | int flags; | ||
208 | }; | ||
209 | |||
210 | struct mu_imap_fetch_internaldate | ||
211 | { | ||
212 | int type; | ||
213 | struct tm tm; | ||
214 | struct mu_timezone tz; | ||
215 | }; | ||
216 | |||
217 | struct mu_imap_fetch_rfc822_size | ||
218 | { | ||
219 | int type; | ||
220 | size_t size; | ||
221 | }; | ||
222 | |||
223 | struct mu_imap_fetch_uid | ||
224 | { | ||
225 | int type; | ||
226 | size_t uid; | ||
227 | }; | ||
228 | |||
229 | union mu_imap_fetch_response | ||
230 | { | ||
231 | int type; | ||
232 | struct mu_imap_fetch_body body; | ||
233 | struct mu_imap_fetch_bodystructure bodystructure; | ||
234 | struct mu_imap_fetch_envelope envelope; | ||
235 | struct mu_imap_fetch_flags flags; | ||
236 | struct mu_imap_fetch_internaldate internaldate; | ||
237 | struct mu_imap_fetch_rfc822_size rfc822_size; | ||
238 | struct mu_imap_fetch_uid uid; | ||
239 | }; | ||
240 | |||
148 | #ifdef __cplusplus | 241 | #ifdef __cplusplus |
149 | } | 242 | } |
150 | #endif | 243 | #endif | ... | ... |
... | @@ -32,7 +32,17 @@ extern "C" { | ... | @@ -32,7 +32,17 @@ extern "C" { |
32 | #define MU_IMAP_RESP 0x01 | 32 | #define MU_IMAP_RESP 0x01 |
33 | #define MU_IMAP_TRACE 0x02 | 33 | #define MU_IMAP_TRACE 0x02 |
34 | #define MU_IMAP_XSCRIPT_MASK(n) (1<<((n)+1)) | 34 | #define MU_IMAP_XSCRIPT_MASK(n) (1<<((n)+1)) |
35 | 35 | #define MU_IMAP_SET_XSCRIPT_MASK(imap,n) \ | |
36 | do \ | ||
37 | (imap)->flags |= MU_IMAP_XSCRIPT_MASK (n); \ | ||
38 | while (0) | ||
39 | #define MU_IMAP_CLR_XSCRIPT_MASK(imap,n) \ | ||
40 | do \ | ||
41 | (imap)->flags &= ~MU_IMAP_XSCRIPT_MASK (n); \ | ||
42 | while (0) | ||
43 | #define MU_IMAP_IS_XSCRIPT_MASK(imap,n) \ | ||
44 | ((imap)->flags & MU_IMAP_XSCRIPT_MASK (n)) | ||
45 | |||
36 | enum mu_imap_client_state | 46 | enum mu_imap_client_state |
37 | { | 47 | { |
38 | MU_IMAP_NO_STATE, | 48 | MU_IMAP_NO_STATE, |
... | @@ -47,6 +57,7 @@ enum mu_imap_client_state | ... | @@ -47,6 +57,7 @@ enum mu_imap_client_state |
47 | MU_IMAP_SELECT_RX, | 57 | MU_IMAP_SELECT_RX, |
48 | MU_IMAP_STATUS_RX, | 58 | MU_IMAP_STATUS_RX, |
49 | MU_IMAP_NOOP_RX, | 59 | MU_IMAP_NOOP_RX, |
60 | MU_IMAP_FETCH_RX, | ||
50 | MU_IMAP_CLOSING | 61 | MU_IMAP_CLOSING |
51 | }; | 62 | }; |
52 | 63 | ||
... | @@ -174,10 +185,14 @@ int _mu_imap_response (mu_imap_t imap, mu_imap_response_action_t fun, | ... | @@ -174,10 +185,14 @@ int _mu_imap_response (mu_imap_t imap, mu_imap_response_action_t fun, |
174 | 185 | ||
175 | int _mu_imap_list_element_is_string (struct imap_list_element *elt, | 186 | int _mu_imap_list_element_is_string (struct imap_list_element *elt, |
176 | const char *str); | 187 | const char *str); |
188 | int _mu_imap_list_nth_element_is_string (mu_list_t list, size_t n, | ||
189 | const char *str); | ||
190 | |||
177 | int _mu_imap_collect_flags (struct imap_list_element *arg, int *res); | 191 | int _mu_imap_collect_flags (struct imap_list_element *arg, int *res); |
178 | 192 | ||
179 | struct imap_list_element *_mu_imap_list_at (mu_list_t list, int idx); | 193 | struct imap_list_element *_mu_imap_list_at (mu_list_t list, int idx); |
180 | 194 | ||
195 | int _mu_imap_parse_fetch_response (mu_list_t resp, mu_list_t *result_list); | ||
181 | 196 | ||
182 | # ifdef __cplusplus | 197 | # ifdef __cplusplus |
183 | } | 198 | } | ... | ... |
... | @@ -28,6 +28,7 @@ libmu_imap_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@ | ... | @@ -28,6 +28,7 @@ libmu_imap_la_LIBADD = ${MU_LIB_AUTH} ${MU_LIB_MAILUTILS} @INTLLIBS@ |
28 | # url.c | 28 | # url.c |
29 | libmu_imap_la_SOURCES = \ | 29 | libmu_imap_la_SOURCES = \ |
30 | fake-folder.c\ | 30 | fake-folder.c\ |
31 | fetch.c\ | ||
31 | callback.c\ | 32 | callback.c\ |
32 | capability.c\ | 33 | capability.c\ |
33 | capatst.c\ | 34 | capatst.c\ | ... | ... |
... | @@ -37,7 +37,7 @@ mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier) | ... | @@ -37,7 +37,7 @@ mu_imap_set_carrier (mu_imap_t imap, mu_stream_t carrier) |
37 | if (imap == NULL) | 37 | if (imap == NULL) |
38 | return EINVAL; | 38 | return EINVAL; |
39 | 39 | ||
40 | rc = mu_imapio_create (&io, carrier); | 40 | rc = mu_imapio_create (&io, carrier, MU_IMAPIO_CLIENT); |
41 | if (rc) | 41 | if (rc) |
42 | return rc; | 42 | return rc; |
43 | if (imap->io) | 43 | if (imap->io) | ... | ... |
libproto/imap/fetch.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 <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <mailutils/errno.h> | ||
25 | #include <mailutils/address.h> | ||
26 | #include <mailutils/cstr.h> | ||
27 | #include <mailutils/cctype.h> | ||
28 | #include <mailutils/imap.h> | ||
29 | #include <mailutils/sys/imap.h> | ||
30 | |||
31 | int | ||
32 | mu_imap_fetch (mu_imap_t imap, const char *msgset, const char *items) | ||
33 | { | ||
34 | int status; | ||
35 | |||
36 | if (imap == NULL) | ||
37 | return EINVAL; | ||
38 | if (!imap->io) | ||
39 | return MU_ERR_NO_TRANSPORT; | ||
40 | if (imap->state != MU_IMAP_CONNECTED) | ||
41 | return MU_ERR_SEQ; | ||
42 | |||
43 | if (imap->imap_state != MU_IMAP_STATE_SELECTED) | ||
44 | return MU_ERR_SEQ; | ||
45 | |||
46 | switch (imap->state) | ||
47 | { | ||
48 | case MU_IMAP_CONNECTED: | ||
49 | status = _mu_imap_tag_next (imap); | ||
50 | MU_IMAP_CHECK_EAGAIN (imap, status); | ||
51 | status = mu_imapio_printf (imap->io, "%s FETCH %s %s\r\n", | ||
52 | imap->tag_str, msgset, items); | ||
53 | MU_IMAP_CHECK_ERROR (imap, status); | ||
54 | MU_IMAP_FCLR (imap, MU_IMAP_RESP); | ||
55 | imap->state = MU_IMAP_FETCH_RX; | ||
56 | |||
57 | case MU_IMAP_FETCH_RX: | ||
58 | status = _mu_imap_response (imap, NULL, NULL); | ||
59 | MU_IMAP_CHECK_EAGAIN (imap, status); | ||
60 | switch (imap->resp_code) | ||
61 | { | ||
62 | case MU_IMAP_OK: | ||
63 | status = 0; | ||
64 | break; | ||
65 | |||
66 | case MU_IMAP_NO: | ||
67 | status = MU_ERR_FAILURE; | ||
68 | break; | ||
69 | |||
70 | case MU_IMAP_BAD: | ||
71 | status = MU_ERR_BADREPLY; | ||
72 | break; | ||
73 | } | ||
74 | imap->state = MU_IMAP_CONNECTED; | ||
75 | break; | ||
76 | |||
77 | default: | ||
78 | status = EINPROGRESS; | ||
79 | } | ||
80 | return status; | ||
81 | } | ||
82 | |||
83 | static void | ||
84 | _free_fetch_response (void *ptr) | ||
85 | { | ||
86 | union mu_imap_fetch_response *resp = ptr; | ||
87 | switch (resp->type) | ||
88 | { | ||
89 | case MU_IMAP_FETCH_BODY: | ||
90 | free (resp->body.partv); | ||
91 | free (resp->body.key); | ||
92 | free (resp->body.text); | ||
93 | break; | ||
94 | |||
95 | case MU_IMAP_FETCH_BODYSTRUCTURE: | ||
96 | /* FIXME */ | ||
97 | break; | ||
98 | |||
99 | case MU_IMAP_FETCH_ENVELOPE: | ||
100 | free (resp->envelope.subject); | ||
101 | mu_address_destroy (&resp->envelope.from); | ||
102 | mu_address_destroy (&resp->envelope.sender); | ||
103 | mu_address_destroy (&resp->envelope.reply_to); | ||
104 | mu_address_destroy (&resp->envelope.to); | ||
105 | mu_address_destroy (&resp->envelope.cc); | ||
106 | mu_address_destroy (&resp->envelope.bcc); | ||
107 | free (resp->envelope.in_reply_to); | ||
108 | free (resp->envelope.message_id); | ||
109 | break; | ||
110 | |||
111 | case MU_IMAP_FETCH_FLAGS: | ||
112 | case MU_IMAP_FETCH_INTERNALDATE: | ||
113 | case MU_IMAP_FETCH_RFC822_SIZE: | ||
114 | case MU_IMAP_FETCH_UID: | ||
115 | break; | ||
116 | } | ||
117 | free (resp); | ||
118 | } | ||
119 | |||
120 | static int | ||
121 | alloc_response (union mu_imap_fetch_response **resp, int type) | ||
122 | { | ||
123 | static size_t sizetab[] = { | ||
124 | sizeof (struct mu_imap_fetch_body), | ||
125 | sizeof (struct mu_imap_fetch_bodystructure), | ||
126 | sizeof (struct mu_imap_fetch_envelope), | ||
127 | sizeof (struct mu_imap_fetch_flags), | ||
128 | sizeof (struct mu_imap_fetch_internaldate), | ||
129 | sizeof (struct mu_imap_fetch_rfc822_size), | ||
130 | sizeof (struct mu_imap_fetch_uid) | ||
131 | }; | ||
132 | union mu_imap_fetch_response *p; | ||
133 | |||
134 | if (type < 0 || type >= MU_ARRAY_SIZE (sizetab)) | ||
135 | return EINVAL; | ||
136 | p = calloc (1, sizetab[type]); | ||
137 | if (!p) | ||
138 | return ENOMEM; | ||
139 | p->type = type; | ||
140 | *resp = p; | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int | ||
145 | _uid_mapper (struct imap_list_element **elt, | ||
146 | union mu_imap_fetch_response **return_resp) | ||
147 | { | ||
148 | union mu_imap_fetch_response *resp; | ||
149 | int rc; | ||
150 | char *p; | ||
151 | size_t uid; | ||
152 | |||
153 | if (elt[1]->type != imap_eltype_string) | ||
154 | return MU_ERR_FAILURE; | ||
155 | uid = strtoul (elt[1]->v.string, &p, 0); | ||
156 | if (*p) | ||
157 | return MU_ERR_FAILURE; | ||
158 | rc = alloc_response (&resp, MU_IMAP_FETCH_UID); | ||
159 | if (rc) | ||
160 | return rc; | ||
161 | resp->uid.uid = uid; | ||
162 | *return_resp = resp; | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | static int | ||
167 | _size_mapper (struct imap_list_element **elt, | ||
168 | union mu_imap_fetch_response **return_resp) | ||
169 | { | ||
170 | union mu_imap_fetch_response *resp; | ||
171 | int rc; | ||
172 | char *p; | ||
173 | size_t size; | ||
174 | |||
175 | if (elt[1]->type != imap_eltype_string) | ||
176 | return MU_ERR_FAILURE; | ||
177 | size = strtoul (elt[1]->v.string, &p, 0); | ||
178 | if (*p) | ||
179 | return MU_ERR_FAILURE; | ||
180 | rc = alloc_response (&resp, MU_IMAP_FETCH_RFC822_SIZE); | ||
181 | if (rc) | ||
182 | return rc; | ||
183 | resp->rfc822_size.size = size; | ||
184 | *return_resp = resp; | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static int | ||
189 | _body_mapper (struct imap_list_element **elt, | ||
190 | union mu_imap_fetch_response **return_resp) | ||
191 | { | ||
192 | union mu_imap_fetch_response *resp; | ||
193 | int rc; | ||
194 | char *section, *p; | ||
195 | size_t partc = 0; | ||
196 | size_t *partv = NULL; | ||
197 | |||
198 | if (elt[1]->type != imap_eltype_string) | ||
199 | return MU_ERR_FAILURE; | ||
200 | rc = alloc_response (&resp, MU_IMAP_FETCH_BODY); | ||
201 | if (rc) | ||
202 | return rc; | ||
203 | |||
204 | section = strchr (elt[0]->v.string, '['); | ||
205 | if (section) | ||
206 | { | ||
207 | p = section; | ||
208 | while (1) | ||
209 | { | ||
210 | p = strchr (p, '.'); | ||
211 | if (*p) | ||
212 | { | ||
213 | p++; | ||
214 | if (mu_isdigit (p[1])) | ||
215 | { | ||
216 | partc++; | ||
217 | continue; | ||
218 | } | ||
219 | } | ||
220 | |||
221 | break; | ||
222 | } | ||
223 | } | ||
224 | |||
225 | if (partc) | ||
226 | { | ||
227 | size_t i; | ||
228 | |||
229 | partv = calloc (partc, sizeof (partv[0])); | ||
230 | for (i = 0, p = section; i < partc; i++) | ||
231 | { | ||
232 | partv[i] = strtoul (p, &p, 10); | ||
233 | p++; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | resp->body.partc = partc; | ||
238 | resp->body.partv = partv; | ||
239 | |||
240 | if (p) | ||
241 | { | ||
242 | size_t len = strlen (p); | ||
243 | resp->body.key = malloc (len); | ||
244 | if (!resp->body.key) | ||
245 | { | ||
246 | free (resp->body.partv); | ||
247 | free (resp); | ||
248 | return ENOMEM; | ||
249 | } | ||
250 | len--; | ||
251 | memcpy (resp->body.key, p, len); | ||
252 | resp->body.key[len] = 0; | ||
253 | } | ||
254 | |||
255 | resp->body.text = strdup (elt[1]->v.string); | ||
256 | if (!resp->body.text) | ||
257 | { | ||
258 | free (resp->body.key); | ||
259 | free (resp->body.partv); | ||
260 | free (resp); | ||
261 | return ENOMEM; | ||
262 | } | ||
263 | *return_resp = resp; | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int | ||
268 | _rfc822_mapper (const char *key, struct imap_list_element *elt, | ||
269 | union mu_imap_fetch_response **return_resp) | ||
270 | { | ||
271 | union mu_imap_fetch_response *resp; | ||
272 | int rc; | ||
273 | |||
274 | if (elt->type != imap_eltype_string) | ||
275 | return MU_ERR_FAILURE; | ||
276 | rc = alloc_response (&resp, MU_IMAP_FETCH_BODY); | ||
277 | if (rc) | ||
278 | return rc; | ||
279 | |||
280 | resp->body.partc = 0; | ||
281 | resp->body.partv = NULL; | ||
282 | |||
283 | resp->body.key = strdup (key); | ||
284 | if (!resp->body.key) | ||
285 | { | ||
286 | free (resp); | ||
287 | return ENOMEM; | ||
288 | } | ||
289 | |||
290 | resp->body.text = strdup (elt->v.string); | ||
291 | if (!resp->body.text) | ||
292 | { | ||
293 | free (resp->body.key); | ||
294 | free (resp->body.partv); | ||
295 | free (resp); | ||
296 | return ENOMEM; | ||
297 | } | ||
298 | *return_resp = resp; | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | static int | ||
303 | _rfc822_header_mapper (struct imap_list_element **elt, | ||
304 | union mu_imap_fetch_response **return_resp) | ||
305 | { | ||
306 | return _rfc822_mapper ("HEADER", elt[1], return_resp); | ||
307 | } | ||
308 | |||
309 | static int | ||
310 | _rfc822_text_mapper (struct imap_list_element **elt, | ||
311 | union mu_imap_fetch_response **return_resp) | ||
312 | { | ||
313 | return _rfc822_mapper ("TEXT", elt[1], return_resp); | ||
314 | } | ||
315 | |||
316 | static int | ||
317 | _flags_mapper (struct imap_list_element **elt, | ||
318 | union mu_imap_fetch_response **return_resp) | ||
319 | { | ||
320 | union mu_imap_fetch_response *resp; | ||
321 | int rc; | ||
322 | int flags; | ||
323 | |||
324 | if (elt[1]->type != imap_eltype_list) | ||
325 | return MU_ERR_FAILURE; | ||
326 | if (_mu_imap_collect_flags (elt[1], &flags)) | ||
327 | return MU_ERR_FAILURE; | ||
328 | |||
329 | rc = alloc_response (&resp, MU_IMAP_FETCH_FLAGS); | ||
330 | if (rc) | ||
331 | return rc; | ||
332 | resp->flags.flags = flags; | ||
333 | *return_resp = resp; | ||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static int | ||
338 | _date_mapper (struct imap_list_element **elt, | ||
339 | union mu_imap_fetch_response **return_resp) | ||
340 | { | ||
341 | struct mu_imap_fetch_internaldate idate; | ||
342 | union mu_imap_fetch_response *resp; | ||
343 | int rc; | ||
344 | const char *p; | ||
345 | |||
346 | if (elt[1]->type != imap_eltype_string) | ||
347 | return MU_ERR_FAILURE; | ||
348 | p = elt[1]->v.string; | ||
349 | if (mu_parse_imap_date_time (&p, &idate.tm, &idate.tz)) | ||
350 | return MU_ERR_FAILURE; | ||
351 | rc = alloc_response (&resp, MU_IMAP_FETCH_INTERNALDATE); | ||
352 | if (rc) | ||
353 | return rc; | ||
354 | resp->internaldate = idate; | ||
355 | *return_resp = resp; | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | struct fill_env | ||
360 | { | ||
361 | struct mu_imap_fetch_envelope *envelope; | ||
362 | size_t n; | ||
363 | }; | ||
364 | |||
365 | enum env_index | ||
366 | { | ||
367 | env_date, | ||
368 | env_subject, | ||
369 | env_from, | ||
370 | env_sender, | ||
371 | env_reply_to, | ||
372 | env_to, | ||
373 | env_cc, | ||
374 | env_bcc, | ||
375 | env_in_reply_to, | ||
376 | env_message_id | ||
377 | }; | ||
378 | |||
379 | static int | ||
380 | elt_to_string (struct imap_list_element *elt, char **pstr) | ||
381 | { | ||
382 | char *p; | ||
383 | |||
384 | if (elt->type != imap_eltype_string) | ||
385 | return EINVAL; | ||
386 | if (mu_c_strcasecmp (elt->v.string, "NIL") == 0) | ||
387 | p = NULL; | ||
388 | else | ||
389 | { | ||
390 | p = strdup (elt->v.string); | ||
391 | if (!p) | ||
392 | return ENOMEM; | ||
393 | } | ||
394 | *pstr = p; | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | struct addr_env | ||
399 | { | ||
400 | mu_address_t addr; | ||
401 | size_t n; | ||
402 | }; | ||
403 | |||
404 | static int | ||
405 | _fill_subaddr (void *item, void *data) | ||
406 | { | ||
407 | struct addr_env *addr_env = data; | ||
408 | struct imap_list_element *elt = item, *arg; | ||
409 | const char *domain = NULL, *local = NULL, *personal = NULL; | ||
410 | |||
411 | if (elt->type != imap_eltype_list) | ||
412 | return 0; | ||
413 | |||
414 | arg = _mu_imap_list_at (elt->v.list, 0); | ||
415 | if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL")) | ||
416 | personal = arg->v.string; | ||
417 | arg = _mu_imap_list_at (elt->v.list, 2); | ||
418 | if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL")) | ||
419 | local = arg->v.string; | ||
420 | arg = _mu_imap_list_at (elt->v.list, 3); | ||
421 | if (arg && arg->type == imap_eltype_string && strcmp (arg->v.string, "NIL")) | ||
422 | domain = arg->v.string; | ||
423 | |||
424 | if (domain && local) | ||
425 | { | ||
426 | if (!addr_env->addr) | ||
427 | { | ||
428 | int rc = mu_address_create_null (&addr_env->addr); | ||
429 | if (rc) | ||
430 | return rc; | ||
431 | } | ||
432 | mu_address_set_local_part (addr_env->addr, addr_env->n, local); | ||
433 | mu_address_set_domain (addr_env->addr, addr_env->n, domain); | ||
434 | mu_address_set_personal (addr_env->addr, addr_env->n, personal); | ||
435 | addr_env->n++; | ||
436 | } | ||
437 | return 0; | ||
438 | } | ||
439 | |||
440 | static int | ||
441 | elt_to_address (struct imap_list_element *elt, mu_address_t *paddr) | ||
442 | { | ||
443 | if (elt->type != imap_eltype_list) | ||
444 | { | ||
445 | if (mu_c_strcasecmp (elt->v.string, "NIL") == 0) | ||
446 | *paddr = NULL; | ||
447 | else | ||
448 | return EINVAL; | ||
449 | } | ||
450 | else | ||
451 | { | ||
452 | struct addr_env addr_env; | ||
453 | addr_env.addr = NULL; | ||
454 | addr_env.n = 1; | ||
455 | mu_list_foreach (elt->v.list, _fill_subaddr, &addr_env); | ||
456 | *paddr = addr_env.addr; | ||
457 | } | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static int | ||
462 | _fill_response (void *item, void *data) | ||
463 | { | ||
464 | int rc; | ||
465 | struct imap_list_element *elt = item; | ||
466 | struct fill_env *env = data; | ||
467 | |||
468 | switch (env->n++) | ||
469 | { | ||
470 | case env_date: | ||
471 | if (elt->type != imap_eltype_string) | ||
472 | rc = MU_ERR_FAILURE; | ||
473 | else | ||
474 | { | ||
475 | char const *p = elt->v.string; | ||
476 | if (mu_parse_imap_date_time (&p, | ||
477 | &env->envelope->date, | ||
478 | &env->envelope->tz)) | ||
479 | rc = MU_ERR_FAILURE; | ||
480 | else | ||
481 | rc = 0; | ||
482 | } | ||
483 | break; | ||
484 | |||
485 | case env_subject: | ||
486 | rc = elt_to_string (elt, &env->envelope->subject); | ||
487 | break; | ||
488 | |||
489 | case env_from: | ||
490 | rc = elt_to_address (elt, &env->envelope->from); | ||
491 | break; | ||
492 | |||
493 | case env_sender: | ||
494 | rc = elt_to_address (elt, &env->envelope->sender); | ||
495 | break; | ||
496 | |||
497 | case env_reply_to: | ||
498 | rc = elt_to_address (elt, &env->envelope->reply_to); | ||
499 | break; | ||
500 | |||
501 | case env_to: | ||
502 | rc = elt_to_address (elt, &env->envelope->to); | ||
503 | break; | ||
504 | |||
505 | case env_cc: | ||
506 | rc = elt_to_address (elt, &env->envelope->cc); | ||
507 | break; | ||
508 | |||
509 | case env_bcc: | ||
510 | rc = elt_to_address (elt, &env->envelope->bcc); | ||
511 | break; | ||
512 | |||
513 | case env_in_reply_to: | ||
514 | rc = elt_to_string (elt, &env->envelope->in_reply_to); | ||
515 | break; | ||
516 | |||
517 | case env_message_id: | ||
518 | rc = elt_to_string (elt, &env->envelope->message_id); | ||
519 | break; | ||
520 | } | ||
521 | return rc; | ||
522 | } | ||
523 | |||
524 | static int | ||
525 | _envelope_mapper (struct imap_list_element **elt, | ||
526 | union mu_imap_fetch_response **return_resp) | ||
527 | { | ||
528 | union mu_imap_fetch_response *resp; | ||
529 | int rc; | ||
530 | struct fill_env env; | ||
531 | |||
532 | if (elt[1]->type != imap_eltype_list) | ||
533 | return MU_ERR_FAILURE; | ||
534 | rc = alloc_response (&resp, MU_IMAP_FETCH_ENVELOPE); | ||
535 | if (rc) | ||
536 | return rc; | ||
537 | env.envelope = &resp->envelope; | ||
538 | env.n = 0; | ||
539 | mu_list_foreach (elt[1]->v.list, _fill_response, &env); | ||
540 | |||
541 | *return_resp = resp; | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | struct mapper_tab | ||
546 | { | ||
547 | char *name; | ||
548 | size_t size; | ||
549 | int prefix; | ||
550 | int (*mapper) (struct imap_list_element **, union mu_imap_fetch_response **); | ||
551 | }; | ||
552 | |||
553 | static struct mapper_tab mapper_tab[] = { | ||
554 | #define S(s) s, (sizeof (s) - 1) | ||
555 | { S("BODYSTRUCTURE"), 0, }, | ||
556 | { S("BODY["), 1, _body_mapper }, | ||
557 | { S("BODY"), 0, _body_mapper }, | ||
558 | { S("ENVELOPE"), 0, _envelope_mapper }, | ||
559 | { S("FLAGS"), 0, _flags_mapper }, | ||
560 | { S("INTERNALDATE"), 0, _date_mapper }, | ||
561 | { S("RFC822"), 0, _body_mapper}, | ||
562 | { S("RFC822.HEADER"), 0, _rfc822_header_mapper }, | ||
563 | { S("RFC822.SIZE"), 0, _size_mapper }, | ||
564 | { S("RFC822.TEXT"), 0, _rfc822_text_mapper }, | ||
565 | { S("UID"), 0, _uid_mapper }, | ||
566 | #undef S | ||
567 | { NULL } | ||
568 | }; | ||
569 | |||
570 | static int | ||
571 | _fetch_mapper (void **itmv, size_t itmc, void *call_data) | ||
572 | { | ||
573 | int *status = call_data; | ||
574 | struct mapper_tab *mt; | ||
575 | struct imap_list_element *elt[2]; | ||
576 | char *kw; | ||
577 | size_t kwlen; | ||
578 | union mu_imap_fetch_response *resp; | ||
579 | |||
580 | elt[0] = itmv[0]; | ||
581 | elt[1] = itmv[1]; | ||
582 | |||
583 | if (elt[0]->type != imap_eltype_string) | ||
584 | { | ||
585 | *status = MU_ERR_FAILURE; | ||
586 | return MU_LIST_MAP_STOP|MU_LIST_MAP_SKIP; | ||
587 | } | ||
588 | kw = elt[0]->v.string; | ||
589 | kwlen = strlen (kw); | ||
590 | for (mt = mapper_tab; mt->name; mt++) | ||
591 | { | ||
592 | if (mt->prefix) | ||
593 | { | ||
594 | if (mt->size >= kwlen && memcmp (mt->name, kw, kwlen) == 0) | ||
595 | break; | ||
596 | } | ||
597 | else if (mt->size == kwlen && memcmp (mt->name, kw, kwlen) == 0) | ||
598 | break; | ||
599 | } | ||
600 | |||
601 | if (!mt->name) | ||
602 | { | ||
603 | mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE9, | ||
604 | ("ignoring unknown FETCH item '%s'", kw)); | ||
605 | return MU_LIST_MAP_SKIP; | ||
606 | } | ||
607 | |||
608 | if (mt->mapper) | ||
609 | { | ||
610 | int rc = mt->mapper (elt, &resp); | ||
611 | if (rc == 0) | ||
612 | { | ||
613 | itmv[0] = resp; | ||
614 | return MU_LIST_MAP_OK; | ||
615 | } | ||
616 | else | ||
617 | { | ||
618 | *status = rc; | ||
619 | return MU_LIST_MAP_STOP|MU_LIST_MAP_SKIP; | ||
620 | } | ||
621 | } | ||
622 | |||
623 | return MU_LIST_MAP_SKIP; | ||
624 | } | ||
625 | |||
626 | int | ||
627 | _mu_imap_parse_fetch_response (mu_list_t input, mu_list_t *result_list) | ||
628 | { | ||
629 | mu_list_t result; | ||
630 | int status; | ||
631 | |||
632 | status = mu_list_create (&result); | ||
633 | if (status) | ||
634 | { | ||
635 | mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR, | ||
636 | ("mu_list_create: %s", mu_strerror (status))); | ||
637 | return 1; | ||
638 | } | ||
639 | mu_list_set_destroy_item (result, _free_fetch_response); | ||
640 | mu_list_map (input, _fetch_mapper, &status, 2, &result); | ||
641 | if (status) | ||
642 | mu_list_destroy (&result); | ||
643 | else | ||
644 | *result_list = result; | ||
645 | |||
646 | return status; | ||
647 | } |
... | @@ -270,4 +270,13 @@ _mu_imap_list_at (mu_list_t list, int idx) | ... | @@ -270,4 +270,13 @@ _mu_imap_list_at (mu_list_t list, int idx) |
270 | return arg; | 270 | return arg; |
271 | } | 271 | } |
272 | 272 | ||
273 | int | ||
274 | _mu_imap_list_nth_element_is_string (mu_list_t list, size_t n, | ||
275 | const char *str) | ||
276 | { | ||
277 | struct imap_list_element *elt = _mu_imap_list_at (list, n); | ||
278 | return elt && elt->type == imap_eltype_string && | ||
279 | strcmp (elt->v.string, str) == 0; | ||
280 | } | ||
281 | |||
273 | 282 | ... | ... |
... | @@ -304,6 +304,34 @@ _process_unsolicited_response (mu_imap_t imap, mu_list_t resp) | ... | @@ -304,6 +304,34 @@ _process_unsolicited_response (mu_imap_t imap, mu_list_t resp) |
304 | return 0; | 304 | return 0; |
305 | } | 305 | } |
306 | } | 306 | } |
307 | else if (count == 3 && | ||
308 | _mu_imap_list_nth_element_is_string (resp, 1, "FETCH")) | ||
309 | { | ||
310 | size_t msgno; | ||
311 | |||
312 | arg = _mu_imap_list_at (resp, 0); | ||
313 | if (arg && arg->type == imap_eltype_string) | ||
314 | { | ||
315 | char *p; | ||
316 | msgno = strtoul (arg->v.string, &p, 10); | ||
317 | if (*p) | ||
318 | return 1; | ||
319 | |||
320 | arg = _mu_imap_list_at (resp, 2); | ||
321 | if (arg->type == imap_eltype_list) | ||
322 | { | ||
323 | mu_list_t list; | ||
324 | |||
325 | if (_mu_imap_parse_fetch_response (arg->v.list, &list) == 0) | ||
326 | { | ||
327 | mu_imap_callback (imap, MU_IMAP_CB_FETCH, msgno, list); | ||
328 | mu_list_destroy (&list); | ||
329 | } | ||
330 | return 0; | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | |||
307 | return 1; | 335 | return 1; |
308 | } | 336 | } |
309 | 337 | ||
... | @@ -317,8 +345,8 @@ _mu_imap_process_untagged_response (mu_imap_t imap, mu_list_t list, | ... | @@ -317,8 +345,8 @@ _mu_imap_process_untagged_response (mu_imap_t imap, mu_list_t list, |
317 | if (fun) | 345 | if (fun) |
318 | fun (imap, list, data); | 346 | fun (imap, list, data); |
319 | else | 347 | else |
320 | mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, | 348 | mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE9, |
321 | ("ignoring unexpected response")); | 349 | ("ignoring unrecognized response")); |
322 | } | 350 | } |
323 | return 0; | 351 | return 0; |
324 | } | 352 | } | ... | ... |
... | @@ -89,21 +89,26 @@ mu_imap_trace_mask (mu_imap_t imap, int op, int lev) | ... | @@ -89,21 +89,26 @@ mu_imap_trace_mask (mu_imap_t imap, int op, int lev) |
89 | switch (op) | 89 | switch (op) |
90 | { | 90 | { |
91 | case MU_IMAP_TRACE_SET: | 91 | case MU_IMAP_TRACE_SET: |
92 | imap->flags |= MU_IMAP_XSCRIPT_MASK (lev); | 92 | MU_IMAP_SET_XSCRIPT_MASK (imap, lev); |
93 | if (lev & MU_XSCRIPT_PAYLOAD) | ||
94 | mu_imapio_trace_payload (imap->io, 1); | ||
93 | break; | 95 | break; |
94 | 96 | ||
95 | case MU_IMAP_TRACE_CLR: | 97 | case MU_IMAP_TRACE_CLR: |
96 | imap->flags &= ~MU_IMAP_XSCRIPT_MASK (lev); | 98 | MU_IMAP_CLR_XSCRIPT_MASK (imap, lev); |
99 | if (lev & MU_XSCRIPT_PAYLOAD) | ||
100 | mu_imapio_trace_payload (imap->io, 0); | ||
97 | break; | 101 | break; |
98 | 102 | ||
99 | case MU_IMAP_TRACE_QRY: | 103 | case MU_IMAP_TRACE_QRY: |
100 | if (imap->flags & MU_IMAP_XSCRIPT_MASK (lev)) | 104 | if (MU_IMAP_IS_XSCRIPT_MASK (imap, lev)) |
101 | break; | 105 | break; |
102 | return MU_ERR_NOENT; | 106 | return MU_ERR_NOENT; |
103 | 107 | ||
104 | default: | 108 | default: |
105 | return EINVAL; | 109 | return EINVAL; |
106 | } | 110 | } |
111 | |||
107 | return 0; | 112 | return 0; |
108 | } | 113 | } |
109 | 114 | ... | ... |
... | @@ -182,6 +182,13 @@ imap_bye_callback (void *data, int code, size_t sdat, void *pdat) | ... | @@ -182,6 +182,13 @@ imap_bye_callback (void *data, int code, size_t sdat, void *pdat) |
182 | mu_diag_output (MU_DIAG_INFO, _("server is closing connection")); | 182 | mu_diag_output (MU_DIAG_INFO, _("server is closing connection")); |
183 | } | 183 | } |
184 | 184 | ||
185 | static void | ||
186 | imap_bad_callback (void *data, int code, size_t sdat, void *pdat) | ||
187 | { | ||
188 | const char *text = pdat; | ||
189 | mu_diag_output (MU_DIAG_CRIT, "SERVER ALERT: %s", text); | ||
190 | } | ||
191 | |||
185 | 192 | ||
186 | static int | 193 | static int |
187 | com_disconnect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) | 194 | com_disconnect (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) |
... | @@ -282,6 +289,9 @@ com_connect (int argc, char **argv) | ... | @@ -282,6 +289,9 @@ com_connect (int argc, char **argv) |
282 | mu_imap_register_callback_function (imap, MU_IMAP_CB_BYE, | 289 | mu_imap_register_callback_function (imap, MU_IMAP_CB_BYE, |
283 | imap_bye_callback, | 290 | imap_bye_callback, |
284 | NULL); | 291 | NULL); |
292 | mu_imap_register_callback_function (imap, MU_IMAP_CB_BAD, | ||
293 | imap_bad_callback, | ||
294 | NULL); | ||
285 | 295 | ||
286 | 296 | ||
287 | status = mu_imap_connect (imap); | 297 | status = mu_imap_connect (imap); |
... | @@ -587,44 +597,68 @@ com_noop (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) | ... | @@ -587,44 +597,68 @@ com_noop (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) |
587 | return 0; | 597 | return 0; |
588 | } | 598 | } |
589 | 599 | ||
600 | static int | ||
601 | com_fetch (int argc, char **argv) | ||
602 | { | ||
603 | int status = mu_imap_fetch (imap, argv[1], argv[2]); | ||
604 | if (status) | ||
605 | report_failure ("fetch", status); | ||
606 | return 0; | ||
607 | } | ||
608 | |||
590 | struct mutool_command imap_comtab[] = { | 609 | struct mutool_command imap_comtab[] = { |
591 | { "capability", 1, -1, com_capability, | 610 | { "capability", 1, -1, 0, |
611 | com_capability, | ||
592 | /* TRANSLATORS: -reread is a keyword; do not translate. */ | 612 | /* TRANSLATORS: -reread is a keyword; do not translate. */ |
593 | N_("[-reread] [NAME...]"), | 613 | N_("[-reread] [NAME...]"), |
594 | N_("list server capabilities") }, | 614 | N_("list server capabilities") }, |
595 | { "verbose", 1, 4, com_verbose, | 615 | { "verbose", 1, 4, 0, |
616 | com_verbose, | ||
596 | "[on|off|mask|unmask] [secure [payload]]", | 617 | "[on|off|mask|unmask] [secure [payload]]", |
597 | N_("control the protocol tracing") }, | 618 | N_("control the protocol tracing") }, |
598 | { "connect", 1, 4, com_connect, | 619 | { "connect", 1, 4, 0, |
620 | com_connect, | ||
599 | /* TRANSLATORS: --tls is a keyword. */ | 621 | /* TRANSLATORS: --tls is a keyword. */ |
600 | N_("[-tls] HOSTNAME [PORT]"), | 622 | N_("[-tls] HOSTNAME [PORT]"), |
601 | N_("open connection") }, | 623 | N_("open connection") }, |
602 | { "disconnect", 1, 1, | 624 | { "disconnect", 1, 1, 0, |
603 | com_disconnect, | 625 | com_disconnect, |
604 | NULL, | 626 | NULL, |
605 | N_("close connection") }, | 627 | N_("close connection") }, |
606 | { "login", 2, 3, com_login, | 628 | { "login", 2, 3, 0, |
629 | com_login, | ||
607 | N_("USER [PASS]"), | 630 | N_("USER [PASS]"), |
608 | N_("login to the server") }, | 631 | N_("login to the server") }, |
609 | { "logout", 1, 1, com_logout, | 632 | { "logout", 1, 1, 0, |
633 | com_logout, | ||
610 | NULL, | 634 | NULL, |
611 | N_("quit imap session") }, | 635 | N_("quit imap session") }, |
612 | { "id", 1, -1, com_id, | 636 | { "id", 1, -1, 0, |
637 | com_id, | ||
613 | N_("[-test KW] [ARG [ARG...]]"), | 638 | N_("[-test KW] [ARG [ARG...]]"), |
614 | N_("send ID command") }, | 639 | N_("send ID command") }, |
615 | { "noop", 1, 1, com_noop, | 640 | { "noop", 1, 1, 0, |
641 | com_noop, | ||
616 | NULL, | 642 | NULL, |
617 | N_("no operation (keepalive)") }, | 643 | N_("no operation (keepalive)") }, |
618 | { "select", 1, 2, com_select, | 644 | { "select", 1, 2, 0, |
645 | com_select, | ||
619 | N_("[MBOX]"), | 646 | N_("[MBOX]"), |
620 | N_("select a mailbox") }, | 647 | N_("select a mailbox") }, |
621 | { "examine", 1, 2, com_examine, | 648 | { "examine", 1, 2, 0, |
649 | com_examine, | ||
622 | N_("[MBOX]"), | 650 | N_("[MBOX]"), |
623 | N_("examine a mailbox") }, | 651 | N_("examine a mailbox") }, |
624 | { "status", 3, -1, com_status, | 652 | { "status", 3, -1, 0, |
653 | com_status, | ||
625 | N_("MBOX KW [KW...]"), | 654 | N_("MBOX KW [KW...]"), |
626 | N_("get mailbox status") }, | 655 | N_("get mailbox status") }, |
627 | { "quit", 1, 1, com_logout, | 656 | { "fetch", 3, 3, CMD_COALESCE_EXTRA_ARGS, |
657 | com_fetch, | ||
658 | N_("MSGSET ITEMS"), | ||
659 | N_("fetch message data") }, | ||
660 | { "quit", 1, 1, 0, | ||
661 | com_logout, | ||
628 | NULL, | 662 | NULL, |
629 | N_("same as `logout'") }, | 663 | N_("same as `logout'") }, |
630 | { NULL } | 664 | { NULL } | ... | ... |
... | @@ -18,13 +18,15 @@ | ... | @@ -18,13 +18,15 @@ |
18 | 18 | ||
19 | typedef int (*mutool_action_t) (int argc, char **argv); | 19 | typedef int (*mutool_action_t) (int argc, char **argv); |
20 | 20 | ||
21 | #define CMD_COALESCE_EXTRA_ARGS 0x01 | ||
22 | |||
21 | struct mutool_command | 23 | struct mutool_command |
22 | { | 24 | { |
23 | const char *name; /* User printable name of the function. */ | 25 | const char *name; /* User printable name of the function. */ |
24 | int argmin; /* Min. acceptable number of arguments (-1 means | 26 | int argmin; /* Min. acceptable number of arguments (> 1) */ |
25 | pass all arguments as a single string */ | ||
26 | int argmax; /* Max. allowed number of arguments (-1 means not | 27 | int argmax; /* Max. allowed number of arguments (-1 means not |
27 | limited */ | 28 | limited */ |
29 | int flags; | ||
28 | mutool_action_t func; /* Function to call to do the job. */ | 30 | mutool_action_t func; /* Function to call to do the job. */ |
29 | const char *argdoc; /* Documentation for the arguments */ | 31 | const char *argdoc; /* Documentation for the arguments */ |
30 | const char *docstring;/* Documentation for this function. */ | 32 | const char *docstring;/* Documentation for this function. */ | ... | ... |
... | @@ -611,59 +611,59 @@ com_quit (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) | ... | @@ -611,59 +611,59 @@ com_quit (int argc MU_ARG_UNUSED, char **argv MU_ARG_UNUSED) |
611 | } | 611 | } |
612 | 612 | ||
613 | struct mutool_command pop_comtab[] = { | 613 | struct mutool_command pop_comtab[] = { |
614 | { "apop", 2, 3, com_apop, | 614 | { "apop", 2, 3, 0, com_apop, |
615 | N_("USER [PASS]"), | 615 | N_("USER [PASS]"), |
616 | N_("authenticate with APOP") }, | 616 | N_("authenticate with APOP") }, |
617 | { "capa", 1, -1, com_capa, | 617 | { "capa", 1, -1, 0, com_capa, |
618 | /* TRANSLATORS: -reread is a keyword; do not translate. */ | 618 | /* TRANSLATORS: -reread is a keyword; do not translate. */ |
619 | N_("[-reread] [NAME...]"), | 619 | N_("[-reread] [NAME...]"), |
620 | N_("list server capabilities") }, | 620 | N_("list server capabilities") }, |
621 | { "disconnect", 1, 1, | 621 | { "disconnect", 1, 1, 0, |
622 | com_disconnect, | 622 | com_disconnect, |
623 | NULL, | 623 | NULL, |
624 | N_("close connection") }, | 624 | N_("close connection") }, |
625 | { "dele", 2, 2, com_dele, | 625 | { "dele", 2, 2, 0, com_dele, |
626 | N_("NUMBER"), | 626 | N_("NUMBER"), |
627 | N_("mark message for deletion") }, | 627 | N_("mark message for deletion") }, |
628 | { "list", 1, 2, com_list, | 628 | { "list", 1, 2, 0, com_list, |
629 | N_("[NUMBER]"), | 629 | N_("[NUMBER]"), |
630 | N_("list messages") }, | 630 | N_("list messages") }, |
631 | { "noop", 1, 1, com_noop, | 631 | { "noop", 1, 1, 0, com_noop, |
632 | NULL, | 632 | NULL, |
633 | N_("send a \"no operation\"") }, | 633 | N_("send a \"no operation\"") }, |
634 | { "pass", 1, 2, com_pass, | 634 | { "pass", 1, 2, 0, com_pass, |
635 | N_("[PASSWORD]"), | 635 | N_("[PASSWORD]"), |
636 | N_("send password") }, | 636 | N_("send password") }, |
637 | { "connect", 1, 4, com_connect, | 637 | { "connect", 1, 4, 0, com_connect, |
638 | /* TRANSLATORS: --tls is a keyword. */ | 638 | /* TRANSLATORS: --tls is a keyword. */ |
639 | N_("[-tls] HOSTNAME [PORT]"), | 639 | N_("[-tls] HOSTNAME [PORT]"), |
640 | N_("open connection") }, | 640 | N_("open connection") }, |
641 | { "quit", 1, 1, com_quit, | 641 | { "quit", 1, 1, 0, com_quit, |
642 | NULL, | 642 | NULL, |
643 | N_("quit pop3 session") }, | 643 | N_("quit pop3 session") }, |
644 | { "retr", 2, 2, com_retr, | 644 | { "retr", 2, 2, 0, com_retr, |
645 | "NUMBER", | 645 | "NUMBER", |
646 | N_("retrieve a message") }, | 646 | N_("retrieve a message") }, |
647 | { "rset", 1, 1, com_rset, | 647 | { "rset", 1, 1, 0, com_rset, |
648 | NULL, | 648 | NULL, |
649 | N_("remove deletion marks") }, | 649 | N_("remove deletion marks") }, |
650 | { "stat", 1, 1, com_stat, | 650 | { "stat", 1, 1, 0, com_stat, |
651 | NULL, | 651 | NULL, |
652 | N_("get the mailbox size and number of messages in it") }, | 652 | N_("get the mailbox size and number of messages in it") }, |
653 | { "stls", 1, 1, com_stls, | 653 | { "stls", 1, 1, 0, com_stls, |
654 | NULL, | 654 | NULL, |
655 | N_("start TLS negotiation") }, | 655 | N_("start TLS negotiation") }, |
656 | { "top", 2, 3, com_top, | 656 | { "top", 2, 3, 0, com_top, |
657 | "MSGNO [NUMBER]", | 657 | "MSGNO [NUMBER]", |
658 | N_("display message headers and first NUMBER (default 5) lines of" | 658 | N_("display message headers and first NUMBER (default 5) lines of" |
659 | " its body") }, | 659 | " its body") }, |
660 | { "uidl", 1, 2, com_uidl, | 660 | { "uidl", 1, 2, 0, com_uidl, |
661 | N_("[NUMBER]"), | 661 | N_("[NUMBER]"), |
662 | N_("show unique message identifiers") }, | 662 | N_("show unique message identifiers") }, |
663 | { "user", 2, 2, com_user, | 663 | { "user", 2, 2, 0, com_user, |
664 | N_("NAME"), | 664 | N_("NAME"), |
665 | N_("send login") }, | 665 | N_("send login") }, |
666 | { "verbose", 1, 4, com_verbose, | 666 | { "verbose", 1, 4, 0, com_verbose, |
667 | "[on|off|mask|unmask] [secure [payload]]", | 667 | "[on|off|mask|unmask] [secure [payload]]", |
668 | N_("control the protocol tracing") }, | 668 | N_("control the protocol tracing") }, |
669 | { NULL } | 669 | { NULL } | ... | ... |
... | @@ -71,18 +71,18 @@ static int shell_history (int, char **); | ... | @@ -71,18 +71,18 @@ static int shell_history (int, char **); |
71 | #endif | 71 | #endif |
72 | 72 | ||
73 | struct mutool_command default_comtab[] = { | 73 | struct mutool_command default_comtab[] = { |
74 | { "prompt", -1, -1, shell_prompt, | 74 | { "prompt", 1, 2, CMD_COALESCE_EXTRA_ARGS, shell_prompt, |
75 | N_("STRING"), | 75 | N_("STRING"), |
76 | N_("set command prompt") }, | 76 | N_("set command prompt") }, |
77 | { "exit", 1, 1, shell_exit, NULL, N_("exit program") }, | 77 | { "exit", 1, 1, 0, shell_exit, NULL, N_("exit program") }, |
78 | { "help", 1, 2, shell_help, | 78 | { "help", 1, 2, 0, shell_help, |
79 | N_("[COMMAND]"), | 79 | N_("[COMMAND]"), |
80 | N_("display this text") }, | 80 | N_("display this text") }, |
81 | { "?", 1, 1, shell_help, | 81 | { "?", 1, 1, 0, shell_help, |
82 | N_("[COMMAND]"), | 82 | N_("[COMMAND]"), |
83 | N_("synonym for `help'") }, | 83 | N_("synonym for `help'") }, |
84 | #ifdef WITH_READLINE | 84 | #ifdef WITH_READLINE |
85 | { "history", 1, 1, shell_history, | 85 | { "history", 1, 1, 0, shell_history, |
86 | NULL, | 86 | NULL, |
87 | N_("show command history") }, | 87 | N_("show command history") }, |
88 | #endif | 88 | #endif |
... | @@ -492,59 +492,101 @@ add_history (const char *s MU_ARG_UNUSED) | ... | @@ -492,59 +492,101 @@ add_history (const char *s MU_ARG_UNUSED) |
492 | } | 492 | } |
493 | #endif | 493 | #endif |
494 | 494 | ||
495 | static int | ||
496 | next_arg (struct mu_wordsplit *ws) | ||
497 | { | ||
498 | int rc = mu_wordsplit (NULL, ws, MU_WRDSF_INCREMENTAL); | ||
499 | if (rc == MU_WRDSE_NOINPUT) | ||
500 | { | ||
501 | mu_error ("%s: too few arguments", ws->ws_wordv[0]); | ||
502 | mu_wordsplit_free (ws); | ||
503 | return -1; | ||
504 | } | ||
505 | else if (rc) | ||
506 | { | ||
507 | mu_error ("cannot parse input line: %s", | ||
508 | mu_wordsplit_strerror (ws)); | ||
509 | return 1; | ||
510 | } | ||
511 | return 0; | ||
512 | } | ||
513 | |||
495 | /* Parse and execute a command line. */ | 514 | /* Parse and execute a command line. */ |
496 | int | 515 | int |
497 | execute_line (char *line) | 516 | execute_line (char *line) |
498 | { | 517 | { |
518 | int rc; | ||
499 | struct mu_wordsplit ws; | 519 | struct mu_wordsplit ws; |
500 | int argc; | 520 | struct mutool_command *cmd; |
501 | char **argv; | ||
502 | int status = 0; | 521 | int status = 0; |
503 | 522 | ||
504 | ws.ws_comment = "#"; | 523 | ws.ws_comment = "#"; |
505 | ws.ws_offs = 1; /* Keep extra slot for expansion in case when argmin == -1 */ | 524 | rc = mu_wordsplit (line, &ws, |
506 | if (mu_wordsplit (line, &ws, | 525 | MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT| |
507 | MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT|MU_WRDSF_DOOFFS)) | 526 | MU_WRDSF_INCREMENTAL|MU_WRDSF_APPEND); |
527 | if (rc == MU_WRDSE_NOINPUT) | ||
528 | { | ||
529 | mu_wordsplit_free (&ws); | ||
530 | return 0; | ||
531 | } | ||
532 | else if (rc) | ||
508 | { | 533 | { |
509 | mu_error("cannot parse input line: %s", mu_wordsplit_strerror (&ws)); | 534 | mu_error ("cannot parse input line: %s", mu_wordsplit_strerror (&ws)); |
510 | return 0; | 535 | return 0; |
511 | } | 536 | } |
512 | argc = ws.ws_wordc; | ||
513 | argv = ws.ws_wordv + 1; | ||
514 | 537 | ||
515 | if (argc >= 0) | 538 | if (ws.ws_wordc) |
516 | { | 539 | { |
517 | struct mutool_command *cmd = find_command (argv[0]); | 540 | int argmin; |
518 | 541 | cmd = find_command (ws.ws_wordv[0]); | |
542 | |||
519 | if (!cmd) | 543 | if (!cmd) |
520 | mu_error ("%s: no such command.", argv[0]); | 544 | { |
521 | else if (cmd->argmin > 0 && argc < cmd->argmin) | 545 | mu_error ("%s: no such command.", ws.ws_wordv[0]); |
522 | mu_error ("%s: too few arguments", argv[0]); | 546 | mu_wordsplit_free (&ws); |
523 | else if (cmd->argmax > 0 && argc > cmd->argmax) | 547 | return 0; |
524 | mu_error ("%s: too many arguments", argv[0]); | 548 | } |
549 | |||
550 | argmin = cmd->argmin; | ||
551 | if (cmd->flags & CMD_COALESCE_EXTRA_ARGS) | ||
552 | --argmin; | ||
553 | while (ws.ws_wordc < argmin) | ||
554 | { | ||
555 | if (next_arg (&ws)) | ||
556 | return 0; | ||
557 | } | ||
558 | |||
559 | if (cmd->flags & CMD_COALESCE_EXTRA_ARGS) | ||
560 | { | ||
561 | ws.ws_flags |= MU_WRDSF_NOSPLIT; | ||
562 | if (next_arg (&ws)) | ||
563 | return 0; | ||
564 | } | ||
525 | else | 565 | else |
526 | { | 566 | { |
527 | if (cmd->argmin <= 0 && argc != 2) | 567 | for (;;) |
528 | { | 568 | { |
529 | size_t i; | 569 | if (cmd->argmax > 0 && ws.ws_wordc > cmd->argmax) |
530 | char *word = mu_str_skip_class (line, MU_CTYPE_SPACE); | 570 | { |
531 | char *arg = mu_str_skip_class_comp (word, MU_CTYPE_SPACE); | 571 | mu_error ("%s: too many arguments", ws.ws_wordv[0]); |
532 | if (*arg) | 572 | mu_wordsplit_free (&ws); |
573 | return 0; | ||
574 | } | ||
575 | rc = mu_wordsplit (NULL, &ws, MU_WRDSF_INCREMENTAL); | ||
576 | if (rc == 0) | ||
577 | continue; | ||
578 | else if (rc == MU_WRDSE_NOINPUT) | ||
579 | break; | ||
580 | else | ||
533 | { | 581 | { |
534 | *arg++ = 0; | 582 | mu_error ("cannot parse input line: %s", |
535 | arg = mu_str_skip_class (arg, MU_CTYPE_SPACE); | 583 | mu_wordsplit_strerror (&ws)); |
584 | return 0; | ||
536 | } | 585 | } |
537 | for (i = 0; i < ws.ws_wordc; i++) | ||
538 | free (ws.ws_wordv[i + 1]); | ||
539 | ws.ws_wordv[0] = xstrdup (word); | ||
540 | ws.ws_wordv[1] = xstrdup (arg); | ||
541 | ws.ws_wordv[2] = NULL; | ||
542 | ws.ws_wordc = 2; | ||
543 | argc = ws.ws_wordc; | ||
544 | argv = ws.ws_wordv; | ||
545 | } | 586 | } |
546 | status = cmd->func (argc, argv); | ||
547 | } | 587 | } |
588 | |||
589 | status = cmd->func (ws.ws_wordc, ws.ws_wordv); | ||
548 | } | 590 | } |
549 | mu_wordsplit_free (&ws); | 591 | mu_wordsplit_free (&ws); |
550 | return status; | 592 | return status; | ... | ... |
-
Please register or sign in to post a comment