Commit b848706b b848706b89934be5a4419ab2ec382157d2f30aba by Sergey Poznyakoff

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".
1 parent 29021e95
...@@ -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)
......
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;
......