1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 1999-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils 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
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "imap4d.h"
18 #include <ctype.h>
19 #include <mailutils/assoc.h>
20
21 /* Taken from RFC2060
22 fetch ::= "FETCH" SPACE set SPACE ("ALL" / "FULL" /
23 "FAST" / fetch_att / "(" 1#fetch_att ")")
24
25 fetch_att ::= "ENVELOPE" / "FLAGS" / "INTERNALDATE" /
26 "RFC822" [".HEADER" / ".SIZE" / ".TEXT"] /
27 "BODY" ["STRUCTURE"] / "UID" /
28 "BODY" [".PEEK"] section
29 ["<" number "." nz_number ">"]
30 */
31
32 struct fetch_runtime_closure
33 {
34 int eltno; /* Serial number of the last output FETCH element */
35 size_t msgno; /* Sequence number of the message being processed */
36 mu_message_t msg; /* The message itself */
37 mu_list_t msglist; /* A list of referenced messages. See KLUDGE below. */
38 char *err_text; /* On return: error description if failed. */
39
40 mu_list_t fnlist;
41 };
42
43 struct fetch_function_closure;
44
45 typedef int (*fetch_function_t) (struct fetch_function_closure *,
46 struct fetch_runtime_closure *);
47
48 struct fetch_function_closure
49 {
50 fetch_function_t fun; /* Handler function */
51 const char *name; /* Response tag */
52 const char *section_tag;
53 size_t *section_part; /* Section-part */
54 size_t nset; /* Number of elements in section_part */
55 int peek;
56 int not; /* Negate header set */
57 mu_list_t headers; /* Headers */
58 size_t start; /* Substring start */
59 size_t size; /* Substring length */
60 };
61
62 struct fetch_parse_closure
63 {
64 int isuid;
65 mu_list_t fnlist;
66 mu_msgset_t msgset;
67 };
68
69
70 static int
fetch_send_address(const char * addr)71 fetch_send_address (const char *addr)
72 {
73 mu_address_t address;
74 size_t i, count = 0;
75
76 /* Short circuit. */
77 if (addr == NULL || *addr == '\0')
78 {
79 io_sendf ("NIL");
80 return RESP_OK;
81 }
82
83 mu_address_create (&address, addr);
84 mu_address_get_count (address, &count);
85
86 /* We failed: can't parse. */
87 if (count == 0)
88 {
89 io_sendf ("NIL");
90 return RESP_OK;
91 }
92
93 io_sendf ("(");
94 for (i = 1; i <= count; i++)
95 {
96 const char *str;
97 int is_group = 0;
98
99 io_sendf ("(");
100
101 mu_address_sget_personal (address, i, &str);
102 io_send_qstring (str);
103 io_sendf (" ");
104
105 mu_address_sget_route (address, i, &str);
106 io_send_qstring (str);
107
108 io_sendf (" ");
109
110 mu_address_is_group (address, i, &is_group);
111 str = NULL;
112 if (is_group)
113 mu_address_sget_personal (address, i, &str);
114 else
115 mu_address_sget_local_part (address, i, &str);
116
117 io_send_qstring (str);
118
119 io_sendf (" ");
120
121 mu_address_sget_domain (address, i, &str);
122 io_send_qstring (str);
123
124 io_sendf (")");
125 }
126 io_sendf (")");
127 return RESP_OK;
128 }
129
130 static void
fetch_send_header_value(mu_header_t header,const char * name,const char * defval,int space)131 fetch_send_header_value (mu_header_t header, const char *name,
132 const char *defval, int space)
133 {
134 char *buffer;
135
136 if (space)
137 io_sendf (" ");
138 if (mu_header_aget_value (header, name, &buffer) == 0)
139 {
140 io_send_qstring (buffer);
141 free (buffer);
142 }
143 else if (defval)
144 io_send_qstring (defval);
145 else
146 io_sendf ("NIL");
147 }
148
149 static void
fetch_send_header_address(mu_header_t header,const char * name,const char * defval,int space)150 fetch_send_header_address (mu_header_t header, const char *name,
151 const char *defval, int space)
152 {
153 char *buffer;
154
155 if (space)
156 io_sendf (" ");
157 if (mu_header_aget_value (header, name, &buffer) == 0)
158 {
159 fetch_send_address (buffer);
160 free (buffer);
161 }
162 else
163 fetch_send_address (defval);
164 }
165
166 static int format_param (char const *name, void *item, void *data);
167
168 /* Send parameter list for the bodystructure. */
169 static void
send_parameter_list(const char * buffer)170 send_parameter_list (const char *buffer)
171 {
172 int rc;
173 char *value;
174 mu_assoc_t param;
175
176 if (!buffer || mu_str_skip_class (buffer, MU_CTYPE_BLANK)[0] == 0)
177 {
178 io_sendf ("NIL");
179 return;
180 }
181
182 rc = mu_mime_header_parse (buffer, NULL, &value, ¶m);
183 if (rc)
184 {
185 mu_diag_funcall (MU_DIAG_ERROR, "mu_mime_header_parse", buffer, rc);
186 io_sendf ("NIL");
187 return;
188 }
189
190 io_sendf ("(");
191 io_send_qstring (value);
192 io_sendf (" ");
193 if (mu_assoc_is_empty (param))
194 {
195 io_sendf ("NIL");
196 }
197 else
198 {
199 int first = 1;
200 io_sendf ("(");
201 mu_assoc_foreach (param, format_param, &first);
202 io_sendf (")");
203 }
204 io_sendf (")");
205 free (value);
206 mu_assoc_destroy (¶m);
207 }
208
209 static void
fetch_send_header_list(mu_header_t header,const char * name,const char * defval,int space)210 fetch_send_header_list (mu_header_t header, const char *name,
211 const char *defval, int space)
212 {
213 char *buffer;
214
215 if (space)
216 io_sendf (" ");
217 if (mu_header_aget_value_unfold (header, name, &buffer) == 0)
218 {
219 send_parameter_list (buffer);
220 free (buffer);
221 }
222 else if (defval)
223 send_parameter_list (defval);
224 else
225 io_sendf ("NIL");
226 }
227
228 /* ENVELOPE:
229 The envelope structure of the message. This is computed by the server by
230 parsing the [RFC-822] header into the component parts, defaulting various
231 fields as necessary. The fields are presented in the order:
232 Date, Subject, From, Sender, Reply-To, To, Cc, Bcc, In-Reply-To, Message-ID.
233 Any field of an envelope or address structure that is not applicable is
234 presented as NIL. Note that the server MUST default the reply-to and sender
235 fields from the from field. The date, subject, in-reply-to, and message-id
236 fields are strings. The from, sender, reply-to, to, cc, and bcc fields
237 are parenthesized lists of address structures. */
238 static int
fetch_envelope0(mu_message_t msg)239 fetch_envelope0 (mu_message_t msg)
240 {
241 char *from = NULL;
242 mu_header_t header = NULL;
243
244 mu_message_get_header (msg, &header);
245
246 fetch_send_header_value (header, "Date", NULL, 0);
247 fetch_send_header_value (header, "Subject", NULL, 1);
248
249 /* From: */
250 mu_header_aget_value (header, "From", &from);
251 io_sendf (" ");
252 fetch_send_address (from);
253
254 fetch_send_header_address (header, "Sender", from, 1);
255 fetch_send_header_address (header, "Reply-to", from, 1);
256 fetch_send_header_address (header, "To", NULL, 1);
257 fetch_send_header_address (header, "Cc", NULL, 1);
258 fetch_send_header_address (header, "Bcc", NULL, 1);
259 fetch_send_header_value (header, "In-Reply-To", NULL, 1);
260 fetch_send_header_value (header, "Message-ID", NULL, 1);
261
262 free (from);
263 return RESP_OK;
264 }
265
266 static int fetch_bodystructure0 (mu_message_t message, int extension);
267
268 static int
format_param(char const * name,void * item,void * data)269 format_param (char const *name, void *item, void *data)
270 {
271 struct mu_mime_param *p = item;
272 int *first = data;
273
274 if (!*first)
275 io_sendf (" ");
276 io_send_qstring (name);
277 io_sendf (" ");
278 if (p->cset)
279 {
280 char *text;
281 int rc = mu_rfc2047_encode (p->cset, "base64", p->value, &text);
282 if (rc == 0)
283 {
284 io_send_qstring (text);
285 free (text);
286 }
287 else
288 {
289 mu_diag_funcall (MU_DIAG_ERROR, "mu_rfc2047_encode", p->value, rc);
290 io_send_qstring (p->value);
291 }
292 }
293 else
294 io_send_qstring (p->value);
295 *first = 0;
296 return 0;
297 }
298
299 static int
get_content_type(mu_header_t hdr,mu_content_type_t * ctp,char const * dfl)300 get_content_type (mu_header_t hdr, mu_content_type_t *ctp, char const *dfl)
301 {
302 int rc;
303 char *buffer = NULL;
304
305 rc = mu_header_aget_value_unfold (hdr, MU_HEADER_CONTENT_TYPE, &buffer);
306 if (rc == 0)
307 {
308 rc = mu_content_type_parse (buffer, NULL, ctp);
309 if (rc == MU_ERR_PARSE)
310 {
311 mu_error (_("malformed content type: %s"), buffer);
312 if (dfl)
313 rc = mu_content_type_parse (dfl, NULL, ctp);
314 }
315 else if (rc)
316 mu_diag_funcall (MU_DIAG_ERROR, "mu_content_type_parse", buffer, rc);
317 free (buffer);
318 }
319 else if (rc == MU_ERR_NOENT && dfl)
320 rc = mu_content_type_parse (dfl, NULL, ctp);
321 return rc;
322 }
323
324 /* The basic fields of a non-multipart body part are in the following order:
325 body type:
326 A string giving the content media type name as defined in [MIME-IMB].
327
328 body subtype:
329 A string giving the content subtype name as defined in [MIME-IMB].
330
331 body parameter parenthesized list:
332 A parenthesized list of attribute/value pairs [e.g. ("foo" "bar" "baz"
333 "rag") where "bar" is the value of "foo" and "rag" is the value of "baz"]
334 as defined in [MIME-IMB].
335
336 body id:
337 A string giving the content id as defined in [MIME-IMB].
338
339 body description:
340 A string giving the content description as defined in [MIME-IMB].
341
342 body encoding:
343 A string giving the content transfer encoding as defined in [MIME-IMB].
344
345 body size:
346 A number giving the size of the body in octets. Note that this size is the
347 size in its transfer encoding and not the resulting size after any decoding.
348
349 A body type of type TEXT contains, immediately after the basic fields, the
350 size of the body in text lines.
351
352 A body type of type MESSAGE and subtype RFC822 contains, immediately after
353 the basic fields, the envelope structure, body structure, and size in text
354 lines of the encapsulated message.
355
356 The extension data of a non-multipart body part are in the following order:
357 body MD5:
358 A string giving the body MD5 value as defined in [MD5].
359
360 body disposition:
361 A parenthesized list with the same content and function as the body
362 disposition for a multipart body part.
363
364 body language:\
365 A string or parenthesized list giving the body language value as defined
366 in [LANGUAGE-TAGS].
367 */
368 static int
bodystructure(mu_message_t msg,int extension)369 bodystructure (mu_message_t msg, int extension)
370 {
371 mu_header_t header = NULL;
372 size_t blines = 0;
373 int message_rfc822 = 0;
374 int text_plain = 0;
375 mu_content_type_t ct;
376 int rc;
377
378 mu_message_get_header (msg, &header);
379
380 rc = get_content_type (header, &ct, "TEXT/PLAIN; CHARSET=US-ASCII");
381 if (rc == 0)
382 {
383 if (mu_c_strcasecmp (ct->type, "MESSAGE") == 0
384 && mu_c_strcasecmp (ct->subtype, "RFC822") == 0)
385 message_rfc822 = 1;
386 else if (mu_c_strcasecmp (ct->type, "TEXT") == 0)
387 text_plain = 1;
388
389 io_send_qstring (ct->type);
390 io_sendf (" ");
391 io_send_qstring (ct->subtype);
392
393 /* body parameter parenthesized list: Content-type attributes */
394 if (mu_assoc_is_empty (ct->param))
395 io_sendf (" NIL");
396 else
397 {
398 int first = 1;
399 io_sendf (" (");
400 mu_assoc_foreach (ct->param, format_param, &first);
401 io_sendf (")");
402 }
403 mu_content_type_destroy (&ct);
404 }
405 else
406 {
407 mu_diag_funcall (MU_DIAG_ERROR, "get_content_type", NULL, rc);
408 return RESP_BAD; /* FIXME: a better error handling, maybe? */
409 }
410
411 /* body id: Content-ID. */
412 fetch_send_header_value (header, MU_HEADER_CONTENT_ID, NULL, 1);
413 /* body description: Content-Description. */
414 fetch_send_header_value (header, MU_HEADER_CONTENT_DESCRIPTION, NULL, 1);
415
416 /* body encoding: Content-Transfer-Encoding. */
417 fetch_send_header_value (header, MU_HEADER_CONTENT_TRANSFER_ENCODING,
418 "7BIT", 1);
419
420 /* body size RFC822 format. */
421 {
422 size_t size = 0;
423 mu_body_t body = NULL;
424 mu_message_get_body (msg, &body);
425 mu_body_size (body, &size);
426 mu_body_lines (body, &blines);
427 io_sendf (" %s", mu_umaxtostr (0, size + blines));
428 }
429
430 /* If the mime type was text. */
431 if (text_plain)
432 {
433 /* Add the line number of the body. */
434 io_sendf (" %s", mu_umaxtostr (0, blines));
435 }
436 else if (message_rfc822)
437 {
438 size_t lines = 0;
439 mu_message_t emsg = NULL;
440 mu_message_unencapsulate (msg, &emsg, NULL);
441 /* Add envelope structure of the encapsulated message. */
442 io_sendf (" (");
443 fetch_envelope0 (emsg);
444 io_sendf (")");
445 /* Add body structure of the encapsulated message. */
446 io_sendf ("(");
447 fetch_bodystructure0 (emsg, extension);
448 io_sendf (")");
449 /* Size in text lines of the encapsulated message. */
450 mu_message_lines (emsg, &lines);
451 io_sendf (" %s", mu_umaxtostr (0, lines));
452 mu_message_destroy (&emsg, NULL);
453 }
454
455 if (extension)
456 {
457 /* body MD5: Content-MD5. */
458 fetch_send_header_value (header, MU_HEADER_CONTENT_MD5, NULL, 1);
459
460 /* body disposition: Content-Disposition. */
461 fetch_send_header_list (header, MU_HEADER_CONTENT_DISPOSITION, NULL, 1);
462
463 /* body language: Content-Language. */
464 fetch_send_header_value (header, MU_HEADER_CONTENT_LANGUAGE, NULL, 1);
465 }
466 return RESP_OK;
467 }
468
469 /* The beef BODYSTRUCTURE.
470 A parenthesized list that describes the [MIME-IMB] body structure of a
471 message. Multiple parts are indicated by parenthesis nesting. Instead of
472 a body type as the first element of the parenthesized list there is a nested
473 body. The second element of the parenthesized list is the multipart
474 subtype (mixed, digest, parallel, alternative, etc.).
475
476 The extension data of a multipart body part are in the following order:
477 body parameter parenthesized list:
478 A parenthesized list of attribute/value pairs [e.g. ("foo" "bar" "baz"
479 "rag") where "bar" is the value of "foo" and "rag" is the value of
480 "baz"] as defined in [MIME-IMB].
481
482 body disposition:
483 A parenthesized list, consisting of a disposition type string followed by a
484 parenthesized list of disposition attribute/value pairs. The disposition
485 type and attribute names will be defined in a future standards-track
486 revision to [DISPOSITION].
487
488 body language:
489 A string or parenthesized list giving the body language value as defined
490 in [LANGUAGE-TAGS]. */
491 static int
fetch_bodystructure0(mu_message_t message,int extension)492 fetch_bodystructure0 (mu_message_t message, int extension)
493 {
494 size_t i;
495 int is_multipart = 0;
496
497 mu_message_is_multipart (message, &is_multipart);
498 if (is_multipart)
499 {
500 mu_content_type_t ct;
501 mu_header_t header = NULL;
502 size_t nparts;
503 int rc;
504
505 rc = mu_message_get_num_parts (message, &nparts);
506 if (rc)
507 {
508 mu_diag_funcall (MU_DIAG_ERR, "mu_message_get_num_parts", NULL, rc);
509 }
510 else
511 {
512 /* Get all the sub messages. */
513 for (i = 1; i <= nparts; i++)
514 {
515 mu_message_t msg = NULL;
516 mu_message_get_part (message, i, &msg);
517 io_sendf ("(");
518 fetch_bodystructure0 (msg, extension);
519 io_sendf (")");
520 } /* for () */
521 }
522
523 mu_message_get_header (message, &header);
524
525 /* The subtype. */
526 rc = get_content_type (header, &ct, NULL);
527 if (rc == 0)
528 {
529 io_sendf (" ");
530 io_send_qstring (ct->subtype);
531
532 /* The extension data for multipart. */
533 if (extension && !mu_assoc_is_empty (ct->param))
534 {
535 int first = 1;
536 io_sendf (" (");
537 mu_assoc_foreach (ct->param, format_param, &first);
538 io_sendf (")");
539 }
540 else
541 io_sendf (" NIL");
542 mu_content_type_destroy (&ct);
543 }
544 else if (rc == MU_ERR_NOENT)
545 /* No content-type header */
546 io_sendf (" NIL");
547 else
548 {
549 mu_diag_funcall (MU_DIAG_ERROR, "get_content_type", NULL, rc);
550 return RESP_BAD; /* FIXME: a better error handling, maybe? */
551 }
552
553 /* body disposition: Content-Disposition. */
554 fetch_send_header_list (header, MU_HEADER_CONTENT_DISPOSITION,
555 NULL, 1);
556 /* body language: Content-Language. */
557 fetch_send_header_value (header, MU_HEADER_CONTENT_LANGUAGE,
558 NULL, 1);
559 }
560 else
561 bodystructure (message, extension);
562 return RESP_OK;
563 }
564
565 static void
set_seen(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)566 set_seen (struct fetch_function_closure *ffc,
567 struct fetch_runtime_closure *frt)
568 {
569 if (!ffc->peek)
570 {
571 mu_attribute_t attr = NULL;
572 mu_message_get_attribute (frt->msg, &attr);
573 if (!mu_attribute_is_read (attr))
574 {
575 io_sendf ("FLAGS (\\Seen) ");
576 mu_attribute_set_read (attr);
577 }
578 }
579 }
580
581 static mu_message_t
fetch_get_part(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)582 fetch_get_part (struct fetch_function_closure *ffc,
583 struct fetch_runtime_closure *frt)
584 {
585 mu_message_t msg = frt->msg;
586 size_t i;
587
588 for (i = 0; i < ffc->nset; i++)
589 if (mu_message_get_part (msg, ffc->section_part[i], &msg))
590 return NULL;
591 return msg;
592 }
593
594 /* FIXME: This is a KLUDGE.
595
596 There is so far no way to unref the MU elements being used to construct
597 a certain entity when this entity itself is being destroyed. In particular,
598 retrieving a nested message/rfc822 part involves creating several messages
599 while unencapsulating, which messages should be unreferenced when the
600 topmost one is destroyed.
601
602 A temporary solution used here is to keep a list of such messages and
603 unreference them together with the topmost one when no longer needed.
604 A message is added to the list by frt_register_message(). All messages
605 in the list are unreferenced by calling frt_unregister_messages().
606
607 The proper solution is of course providing a way for mu_message_t (and
608 other MU objects) to unreference its parent elements. This should be fixed
609 in later releases.
610 */
611
612 static void
_unref_message_item(void * data)613 _unref_message_item (void *data)
614 {
615 mu_message_unref ((mu_message_t)data);
616 }
617
618 static int
frt_register_message(struct fetch_runtime_closure * frt,mu_message_t msg)619 frt_register_message (struct fetch_runtime_closure *frt, mu_message_t msg)
620 {
621 if (!frt->msglist)
622 {
623 int rc = mu_list_create (&frt->msglist);
624 if (rc)
625 return rc;
626 mu_list_set_destroy_item (frt->msglist, _unref_message_item);
627 }
628 return mu_list_append (frt->msglist, msg);
629 }
630
631 static void
frt_unregister_messages(struct fetch_runtime_closure * frt)632 frt_unregister_messages (struct fetch_runtime_closure *frt)
633 {
634 mu_list_clear (frt->msglist);
635 }
636
637 static mu_message_t
fetch_get_part_rfc822(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)638 fetch_get_part_rfc822 (struct fetch_function_closure *ffc,
639 struct fetch_runtime_closure *frt)
640 {
641 mu_message_t msg = frt->msg, retmsg = NULL;
642 size_t i;
643 mu_header_t header;
644
645 if (ffc->nset == 0)
646 {
647 mu_message_ref (msg);
648 return msg;
649 }
650
651 for (i = 0; i < ffc->nset; i++)
652 {
653 mu_content_type_t ct;
654 int rc;
655
656 if (mu_message_get_part (msg, ffc->section_part[i], &msg))
657 return NULL;
658
659 if (mu_message_get_header (msg, &header))
660 return NULL;
661
662 rc = get_content_type (header, &ct, NULL);
663 if (rc == 0)
664 {
665 if (mu_c_strcasecmp (ct->type, "MESSAGE") == 0
666 && mu_c_strcasecmp (ct->subtype, "RFC822") == 0)
667 {
668 rc = mu_message_unencapsulate (msg, &retmsg, NULL);
669 if (rc)
670 {
671 mu_error (_("%s failed: %s"), "mu_message_unencapsulate",
672 mu_strerror (rc));
673 return NULL;
674 }
675 if (frt_register_message (frt, retmsg))
676 {
677 frt_unregister_messages (frt);
678 return NULL;
679 }
680 msg = retmsg;
681 }
682 mu_content_type_destroy (&ct);
683 }
684 else if (rc != MU_ERR_NOENT)
685 mu_diag_funcall (MU_DIAG_ERROR, "get_content_type", NULL, rc);
686 }
687
688 return retmsg;
689 }
690
691 static void
fetch_send_section_part(struct fetch_function_closure * ffc,const char * suffix,int close_bracket)692 fetch_send_section_part (struct fetch_function_closure *ffc,
693 const char *suffix, int close_bracket)
694 {
695 int i;
696
697 io_sendf ("BODY[");
698 for (i = 0; i < ffc->nset; i++)
699 {
700 if (i)
701 io_sendf (".");
702 io_sendf ("%lu", (unsigned long) ffc->section_part[i]);
703 }
704 if (suffix)
705 {
706 if (i)
707 io_sendf (".");
708 io_sendf ("%s", suffix);
709 }
710 if (close_bracket)
711 io_sendf ("]");
712 }
713
714 static int
fetch_io(mu_stream_t stream,size_t start,size_t size,size_t max)715 fetch_io (mu_stream_t stream, size_t start, size_t size, size_t max)
716 {
717 if (start == 0 && size == (size_t) -1)
718 {
719 int rc;
720
721 rc = mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
722 if (rc)
723 {
724 mu_error ("seek error: %s", mu_stream_strerror (stream, rc));
725 return RESP_BAD;
726 }
727 if (max)
728 {
729 io_sendf (" {%lu}\n", (unsigned long) max);
730 io_copy_out (stream, max);
731 /* FIXME: Make sure exactly max bytes were sent */
732 }
733 else
734 io_sendf (" \"\"");
735 }
736 else if (start > max)
737 {
738 io_sendf ("<%lu>", (unsigned long) start);
739 io_sendf (" \"\"");
740 }
741 else
742 {
743 mu_stream_t rfc = NULL;
744 int rc;
745 char *buffer, *p;
746 size_t total = 0;
747 size_t n = 0;
748
749 if (size > max)
750 size = max;
751 if (size + 2 < size) /* Check for integer overflow */
752 {
753 mu_stream_destroy (&rfc);
754 return RESP_BAD;
755 }
756
757 mu_filter_create (&rfc, stream, "CRLF", MU_FILTER_ENCODE,
758 MU_STREAM_READ|MU_STREAM_SEEK);
759
760 p = buffer = mu_alloc (size + 1);
761
762 rc = mu_stream_seek (rfc, start, MU_SEEK_SET, NULL);
763 if (rc)
764 {
765 mu_error ("seek error: %s", mu_stream_strerror (rfc, rc));
766 free (buffer);
767 mu_stream_destroy (&rfc);
768 return RESP_BAD;
769 }
770
771 while (total < size
772 && (rc = mu_stream_read (rfc, p, size - total, &n)) == 0
773 && n > 0)
774 {
775 total += n;
776 p += n;
777 }
778
779 if (rc)
780 {
781 mu_error ("read error: %s", mu_stream_strerror (rfc, rc));
782 free (buffer);
783 mu_stream_destroy (&rfc);
784 return RESP_BAD;
785 }
786
787 *p = 0;
788 io_sendf ("<%lu>", (unsigned long) start);
789 if (total)
790 {
791 io_sendf (" {%lu}\n", (unsigned long) total);
792 io_enable_crlf (0);
793 io_send_bytes (buffer, total);
794 io_enable_crlf (1);
795 }
796 else
797 io_sendf (" \"\"");
798 free (buffer);
799 mu_stream_destroy (&rfc);
800 }
801 return RESP_OK;
802 }
803
804
805 /* Runtime functions */
806 static int
_frt_uid(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)807 _frt_uid (struct fetch_function_closure *ffc,
808 struct fetch_runtime_closure *frt)
809 {
810 size_t uid = 0;
811
812 mu_message_get_uid (frt->msg, &uid);
813 io_sendf ("%s %s", ffc->name, mu_umaxtostr (0, uid));
814 return RESP_OK;
815 }
816
817 static int
_frt_envelope(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)818 _frt_envelope (struct fetch_function_closure *ffc,
819 struct fetch_runtime_closure *frt)
820 {
821 io_sendf ("%s (", ffc->name);
822 fetch_envelope0 (frt->msg);
823 io_sendf (")");
824 return RESP_OK;
825 }
826
827 static int
_frt_flags(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)828 _frt_flags (struct fetch_function_closure *ffc,
829 struct fetch_runtime_closure *frt)
830 {
831 mu_attribute_t attr = NULL;
832
833 mu_message_get_attribute (frt->msg, &attr);
834 io_sendf ("%s (", ffc->name);
835 util_print_flags (attr);
836 io_sendf (")");
837 return 0;
838 }
839
840 /* INTERNALDATE The internal date of the message.
841 Format:
842
843 date_time ::= <"> date_day_fixed "-" date_month "-" date_year
844 SPACE time SPACE zone <">
845
846 date_day ::= 1*2digit
847 ;; Day of month
848
849 date_day_fixed ::= (SPACE digit) / 2digit
850 ;; Fixed-format version of date_day
851
852 date_month ::= "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" /
853 "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
854
855 date_text ::= date_day "-" date_month "-" date_year
856
857 date_year ::= 4digit
858
859 time ::= 2digit ":" 2digit ":" 2digit
860 ;; Hours minutes seconds
861
862 zone ::= ("+" / "-") 4digit
863 ;; Signed four-digit value of hhmm representing
864 ;; hours and minutes west of Greenwich (that is,
865 ;; (the amount that the given time differs from
866 ;; Universal Time). Subtracting the timezone
867 ;; from the given time will give the UT form.
868 ;; The Universal Time zone is "+0000". */
869 static int
_frt_internaldate(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)870 _frt_internaldate (struct fetch_function_closure *ffc,
871 struct fetch_runtime_closure *frt)
872 {
873 const char *date;
874 mu_envelope_t env = NULL;
875 struct tm tm, *tmp = NULL;
876 struct mu_timezone tz;
877
878 mu_message_get_envelope (frt->msg, &env);
879 if (mu_envelope_sget_date (env, &date) == 0
880 && mu_scan_datetime (date, MU_DATETIME_FROM, &tm, &tz, NULL) == 0)
881 {
882 tmp = &tm;
883 mu_datetime_tz_utc (&tz);
884 }
885 else
886 {
887 time_t t;
888 struct timeval stv;
889 struct timezone stz;
890
891 gettimeofday (&stv, &stz);
892 t = stv.tv_sec;
893 tz.utc_offset = - stz.tz_minuteswest;
894 tmp = localtime (&t);
895 }
896 io_sendf ("%s", ffc->name);
897 mu_c_streamftime (iostream, " \"%d-%b-%Y %H:%M:%S %z\"", tmp, &tz);
898 return 0;
899 }
900
901 static int
_frt_bodystructure(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)902 _frt_bodystructure (struct fetch_function_closure *ffc,
903 struct fetch_runtime_closure *frt)
904 {
905 io_sendf ("%s (", ffc->name);
906 fetch_bodystructure0 (frt->msg, 1); /* 1 means with extension data. */
907 io_sendf (")");
908 return RESP_OK;
909 }
910
911 static int
_frt_bodystructure0(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)912 _frt_bodystructure0 (struct fetch_function_closure *ffc,
913 struct fetch_runtime_closure *frt)
914 {
915 io_sendf ("%s (", ffc->name);
916 fetch_bodystructure0 (frt->msg, 0);
917 io_sendf (")");
918 return RESP_OK;
919 }
920
921 /* BODY[] */
922 static int
_frt_body(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)923 _frt_body (struct fetch_function_closure *ffc,
924 struct fetch_runtime_closure *frt)
925 {
926 mu_message_t msg = frt->msg;
927 mu_stream_t stream = NULL;
928 size_t size = 0, lines = 0;
929 int rc;
930
931 set_seen (ffc, frt);
932 if (ffc->name)
933 io_sendf ("%s", ffc->name);
934 else
935 fetch_send_section_part (ffc, NULL, 1);
936 mu_message_get_streamref (msg, &stream);
937 mu_message_size (msg, &size);
938 mu_message_lines (msg, &lines);
939 rc = fetch_io (stream, ffc->start, ffc->size, size + lines);
940 mu_stream_destroy (&stream);
941 return rc;
942 }
943
944 /* BODY[N] */
945 static int
_frt_body_n(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)946 _frt_body_n (struct fetch_function_closure *ffc,
947 struct fetch_runtime_closure *frt)
948 {
949 mu_message_t msg;
950 mu_body_t body = NULL;
951 mu_stream_t stream = NULL;
952 size_t size = 0, lines = 0;
953 int rc;
954
955 set_seen (ffc, frt);
956 if (ffc->name)
957 io_sendf ("%s", ffc->name);
958 else
959 fetch_send_section_part (ffc, ffc->section_tag, 1);
960 msg = fetch_get_part (ffc, frt);
961 if (!msg)
962 {
963 io_sendf (" NIL");
964 return RESP_OK;
965 }
966
967 mu_message_get_body (msg, &body);
968 mu_body_size (body, &size);
969 mu_body_lines (body, &lines);
970 mu_body_get_streamref (body, &stream);
971 rc = fetch_io (stream, ffc->start, ffc->size, size + lines);
972 mu_stream_destroy (&stream);
973
974 return rc;
975 }
976
977 static int
_frt_body_text(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)978 _frt_body_text (struct fetch_function_closure *ffc,
979 struct fetch_runtime_closure *frt)
980 {
981 mu_message_t msg;
982 mu_body_t body = NULL;
983 mu_stream_t stream = NULL;
984 size_t size = 0, lines = 0;
985 int rc;
986
987 set_seen (ffc, frt);
988 if (ffc->name)
989 io_sendf ("%s", ffc->name);
990 else
991 fetch_send_section_part (ffc, ffc->section_tag, 1);
992 msg = fetch_get_part_rfc822 (ffc, frt);
993 if (!msg)
994 {
995 io_sendf (" NIL");
996 return RESP_OK;
997 }
998
999 mu_message_get_body (msg, &body);
1000 mu_body_size (body, &size);
1001 mu_body_lines (body, &lines);
1002 mu_body_get_streamref (body, &stream);
1003 rc = fetch_io (stream, ffc->start, ffc->size, size + lines);
1004 mu_stream_destroy (&stream);
1005 frt_unregister_messages (frt);
1006 return rc;
1007 }
1008
1009 static int
_frt_size(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)1010 _frt_size (struct fetch_function_closure *ffc,
1011 struct fetch_runtime_closure *frt)
1012 {
1013 size_t size = 0;
1014 size_t lines = 0;
1015
1016 mu_message_size (frt->msg, &size);
1017 mu_message_lines (frt->msg, &lines);
1018 io_sendf ("%s %lu", ffc->name, (unsigned long) (size + lines));
1019 return RESP_OK;
1020 }
1021
1022 static int
_frt_header(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)1023 _frt_header (struct fetch_function_closure *ffc,
1024 struct fetch_runtime_closure *frt)
1025 {
1026 mu_message_t msg;
1027 mu_header_t header = NULL;
1028 mu_stream_t stream = NULL;
1029 size_t size = 0, lines = 0;
1030 int rc;
1031
1032 set_seen (ffc, frt);
1033 if (ffc->name)
1034 io_sendf ("%s", ffc->name);
1035 else
1036 fetch_send_section_part (ffc, ffc->section_tag, 1);
1037
1038 msg = fetch_get_part_rfc822 (ffc, frt);
1039 if (!msg)
1040 {
1041 io_sendf (" NIL");
1042 return RESP_OK;
1043 }
1044 mu_message_get_header (msg, &header);
1045 mu_header_size (header, &size);
1046 mu_header_lines (header, &lines);
1047 mu_header_get_streamref (header, &stream);
1048 rc = fetch_io (stream, ffc->start, ffc->size, size + lines);
1049 mu_stream_destroy (&stream);
1050 frt_unregister_messages (frt);
1051 return rc;
1052 }
1053
1054 static int
_frt_mime(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)1055 _frt_mime (struct fetch_function_closure *ffc,
1056 struct fetch_runtime_closure *frt)
1057 {
1058 mu_message_t msg;
1059 mu_header_t header = NULL;
1060 mu_stream_t stream = NULL;
1061 size_t size = 0, lines = 0;
1062 int rc;
1063
1064 set_seen (ffc, frt);
1065 if (ffc->name)
1066 io_sendf ("%s", ffc->name);
1067 else
1068 fetch_send_section_part (ffc, ffc->section_tag, 1);
1069
1070 msg = fetch_get_part (ffc, frt);
1071 if (!msg)
1072 {
1073 io_sendf (" NIL");
1074 return RESP_OK;
1075 }
1076 mu_message_get_header (msg, &header);
1077 mu_header_size (header, &size);
1078 mu_header_lines (header, &lines);
1079 mu_header_get_streamref (header, &stream);
1080 rc = fetch_io (stream, ffc->start, ffc->size, size + lines);
1081 mu_stream_destroy (&stream);
1082
1083 return rc;
1084 }
1085
1086 static int
_send_header_name(void * item,void * data)1087 _send_header_name (void *item, void *data)
1088 {
1089 int *pf = data;
1090 if (*pf)
1091 io_sendf (" ");
1092 else
1093 *pf = 1;
1094 io_sendf ("%s", (char*) item);
1095 return 0;
1096 }
1097
1098 static int
count_nl(const char * str)1099 count_nl (const char *str)
1100 {
1101 int n = 0;
1102 for (;(str = strchr (str, '\n')); str++)
1103 n++;
1104 return n;
1105 }
1106
1107 static int
_frt_header_fields(struct fetch_function_closure * ffc,struct fetch_runtime_closure * frt)1108 _frt_header_fields (struct fetch_function_closure *ffc,
1109 struct fetch_runtime_closure *frt)
1110 {
1111 int status;
1112 mu_message_t msg;
1113 mu_off_t size = 0;
1114 size_t lines = 0;
1115 mu_stream_t stream;
1116 mu_header_t header;
1117 mu_iterator_t itr;
1118
1119 set_seen (ffc, frt);
1120
1121 fetch_send_section_part (ffc, "HEADER.FIELDS", 0);
1122 if (ffc->not)
1123 io_sendf (".NOT");
1124 io_sendf (" (");
1125 status = 0;
1126 mu_list_foreach (ffc->headers, _send_header_name, &status);
1127 io_sendf (")]");
1128
1129 msg = fetch_get_part_rfc822 (ffc, frt);
1130 if (!msg)
1131 {
1132 io_sendf (" NIL");
1133 return RESP_OK;
1134 }
1135
1136 /* Collect headers: */
1137 if (mu_message_get_header (msg, &header)
1138 || mu_header_get_iterator (header, &itr))
1139 {
1140 frt_unregister_messages (frt);
1141 io_sendf (" NIL");
1142 return RESP_OK;
1143 }
1144
1145 status = mu_memory_stream_create (&stream, 0);
1146 if (status != 0)
1147 imap4d_bye (ERR_NO_MEM);
1148
1149 for (mu_iterator_first (itr);
1150 !mu_iterator_is_done (itr); mu_iterator_next (itr))
1151 {
1152 const char *hf;
1153 char *hv;
1154 const char *item;
1155
1156 mu_iterator_current_kv (itr, (const void **)&hf, (void **)&hv);
1157 status = mu_list_locate (ffc->headers, (void *)hf, (void**) &item) == 0;
1158 if (ffc->not)
1159 {
1160 status = !status;
1161 item = hf;
1162 }
1163
1164 if (status)
1165 {
1166 mu_stream_printf (stream, "%s: %s\n", item, hv);
1167 lines += 1 + count_nl (hv);
1168 }
1169 }
1170 mu_iterator_destroy (&itr);
1171 mu_stream_write (stream, "\n", 1, NULL);
1172 lines++;
1173
1174 /* Output collected data */
1175 mu_stream_size (stream, &size);
1176 mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
1177 status = fetch_io (stream, ffc->start, ffc->size, size + lines);
1178 mu_stream_destroy (&stream);
1179 frt_unregister_messages (frt);
1180
1181 return status;
1182 }
1183
1184
1185 static void
ffc_init(struct fetch_function_closure * ffc)1186 ffc_init (struct fetch_function_closure *ffc)
1187 {
1188 memset(ffc, 0, sizeof *ffc);
1189 ffc->start = 0;
1190 ffc->size = (size_t) -1;
1191 }
1192
1193 static void
_free_ffc(void * item)1194 _free_ffc (void *item)
1195 {
1196 struct fetch_function_closure *ffc = item;
1197 mu_list_destroy (&ffc->headers);
1198 free (ffc);
1199 }
1200
1201 static int
_do_fetch(void * item,void * data)1202 _do_fetch (void *item, void *data)
1203 {
1204 struct fetch_function_closure *ffc = item;
1205 struct fetch_runtime_closure *frt = data;
1206 if (frt->eltno++)
1207 io_sendf (" ");
1208 return ffc->fun (ffc, frt);
1209 }
1210
1211 static void
append_ffc(struct fetch_parse_closure * p,struct fetch_function_closure * ffc)1212 append_ffc (struct fetch_parse_closure *p, struct fetch_function_closure *ffc)
1213 {
1214 struct fetch_function_closure *new_ffc = mu_alloc (sizeof (*new_ffc));
1215 *new_ffc = *ffc;
1216 mu_list_append (p->fnlist, new_ffc);
1217 }
1218
1219 static void
append_simple_function(struct fetch_parse_closure * p,const char * name,fetch_function_t fun)1220 append_simple_function (struct fetch_parse_closure *p, const char *name,
1221 fetch_function_t fun)
1222 {
1223 struct fetch_function_closure ffc;
1224 ffc_init (&ffc);
1225 ffc.fun = fun;
1226 ffc.name = name;
1227 append_ffc (p, &ffc);
1228 }
1229
1230
1231 static struct fetch_macro
1232 {
1233 char *macro;
1234 char *exp;
1235 } fetch_macro_tab[] = {
1236 { "ALL", "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE" },
1237 { "FULL", "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY" },
1238 { "FAST", "FLAGS INTERNALDATE RFC822.SIZE" },
1239 { NULL }
1240 };
1241
1242 static char *
find_macro(const char * name)1243 find_macro (const char *name)
1244 {
1245 int i;
1246 for (i = 0; fetch_macro_tab[i].macro; i++)
1247 if (mu_c_strcasecmp (fetch_macro_tab[i].macro, name) == 0)
1248 return fetch_macro_tab[i].exp;
1249 return NULL;
1250 }
1251
1252
1253 struct fetch_att_tab
1254 {
1255 char *name;
1256 fetch_function_t fun;
1257 };
1258
1259 static struct fetch_att_tab fetch_att_tab[] = {
1260 { "ENVELOPE", _frt_envelope },
1261 { "FLAGS", _frt_flags },
1262 { "INTERNALDATE", _frt_internaldate },
1263 { "UID", _frt_uid },
1264 { NULL }
1265 };
1266
1267 static struct fetch_att_tab *
find_fetch_att_tab(char * name)1268 find_fetch_att_tab (char *name)
1269 {
1270 struct fetch_att_tab *p;
1271 for (p = fetch_att_tab; p->name; p++)
1272 if (mu_c_strcasecmp (p->name, name) == 0)
1273 return p;
1274 return NULL;
1275 }
1276
1277 /*
1278 fetch-att = "ENVELOPE" / "FLAGS" / "INTERNALDATE" /
1279 "RFC822" [".HEADER" / ".SIZE" / ".TEXT"] /
1280 "BODY" ["STRUCTURE"] / "UID" /
1281 "BODY" section ["<" number "." nz-number ">"] /
1282 "BODY.PEEK" section ["<" number "." nz-number ">"]
1283
1284 */
1285
1286 /* "RFC822" [".HEADER" / ".SIZE" / ".TEXT"] */
1287 static void
parse_fetch_rfc822(imap4d_parsebuf_t p)1288 parse_fetch_rfc822 (imap4d_parsebuf_t p)
1289 {
1290 struct fetch_function_closure ffc;
1291 ffc_init (&ffc);
1292 ffc.name = "RFC822";
1293 imap4d_parsebuf_next (p, 0);
1294 if (p->token == NULL || p->token[0] == ')')
1295 {
1296 /* Equivalent to BODY[]. */
1297 ffc.fun = _frt_body;
1298 }
1299 else if (p->token[0] == '.')
1300 {
1301 imap4d_parsebuf_next (p, 1);
1302 if (mu_c_strcasecmp (p->token, "HEADER") == 0)
1303 {
1304 /* RFC822.HEADER
1305 Equivalent to BODY[HEADER]. Note that this did not result in
1306 \Seen being set, because RFC822.HEADER response data occurs as
1307 a result of a FETCH of RFC822.HEADER. BODY[HEADER] response
1308 data occurs as a result of a FETCH of BODY[HEADER] (which sets
1309 \Seen) or BODY.PEEK[HEADER] (which does not set \Seen). */
1310
1311 ffc.name = "RFC822.HEADER";
1312 ffc.fun = _frt_header;
1313 ffc.peek = 1;
1314 imap4d_parsebuf_next (p, 0);
1315 }
1316 else if (mu_c_strcasecmp (p->token, "SIZE") == 0)
1317 {
1318 /* A number expressing the [RFC-2822] size of the message. */
1319 ffc.name = "RFC822.SIZE";
1320 ffc.fun = _frt_size;
1321 imap4d_parsebuf_next (p, 0);
1322 }
1323 else if (mu_c_strcasecmp (p->token, "TEXT") == 0)
1324 {
1325 /* RFC822.TEXT
1326 Equivalent to BODY[TEXT]. */
1327 ffc.name = "RFC822.TEXT";
1328 ffc.fun = _frt_body_text;
1329 imap4d_parsebuf_next (p, 0);
1330 }
1331 else
1332 imap4d_parsebuf_exit (p, "Syntax error after RFC822.");
1333 }
1334 else
1335 imap4d_parsebuf_exit (p, "Syntax error after RFC822");
1336 append_ffc (imap4d_parsebuf_data (p), &ffc);
1337 }
1338
1339 static int
_header_cmp(const void * a,const void * b)1340 _header_cmp (const void *a, const void *b)
1341 {
1342 return mu_c_strcasecmp ((char*)a, (char*)b);
1343 }
1344
1345 /*
1346 header-fld-name = astring
1347
1348 header-list = "(" header-fld-name *(SP header-fld-name) ")"
1349 */
1350 static void
parse_header_list(imap4d_parsebuf_t p,struct fetch_function_closure * ffc)1351 parse_header_list (imap4d_parsebuf_t p, struct fetch_function_closure *ffc)
1352 {
1353 if (!(p->token && p->token[0] == '('))
1354 imap4d_parsebuf_exit (p, "Syntax error: expected (");
1355 mu_list_create (&ffc->headers);
1356 mu_list_set_comparator (ffc->headers, _header_cmp);
1357 for (imap4d_parsebuf_next (p, 1); p->token[0] != ')'; imap4d_parsebuf_next (p, 1))
1358 {
1359 if (p->token[1] == 0 && strchr ("()[]<>.", p->token[0]))
1360 imap4d_parsebuf_exit (p, "Syntax error: unexpected delimiter");
1361 mu_list_append (ffc->headers, p->token);
1362 }
1363 imap4d_parsebuf_next (p, 1);
1364 }
1365
1366 /*
1367 section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list /
1368 "TEXT"
1369 ; top-level or MESSAGE/RFC822 part
1370 section-text = section-msgtext / "MIME"
1371 ; text other than actual body part (headers, etc.)
1372 */
1373 static int
parse_section_text(imap4d_parsebuf_t p,struct fetch_function_closure * ffc,int allow_mime)1374 parse_section_text (imap4d_parsebuf_t p, struct fetch_function_closure *ffc,
1375 int allow_mime)
1376 {
1377 if (mu_c_strcasecmp (p->token, "HEADER") == 0)
1378 {
1379 /* "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list */
1380 imap4d_parsebuf_next (p, 1);
1381 if (p->token[0] == '.')
1382 {
1383 imap4d_parsebuf_next (p, 1);
1384 if (mu_c_strcasecmp (p->token, "FIELDS"))
1385 imap4d_parsebuf_exit (p, "Expected FIELDS");
1386 ffc->fun = _frt_header_fields;
1387 imap4d_parsebuf_next (p, 1);
1388 if (p->token[0] == '.')
1389 {
1390 imap4d_parsebuf_next (p, 1);
1391 if (mu_c_strcasecmp (p->token, "NOT") == 0)
1392 {
1393 ffc->not = 1;
1394 imap4d_parsebuf_next (p, 1);
1395 }
1396 else
1397 imap4d_parsebuf_exit (p, "Expected NOT");
1398 }
1399 parse_header_list (p, ffc);
1400 }
1401 else
1402 {
1403 ffc->fun = _frt_header;
1404 ffc->section_tag = "HEADER";
1405 }
1406 }
1407 else if (mu_c_strcasecmp (p->token, "TEXT") == 0)
1408 {
1409 imap4d_parsebuf_next (p, 1);
1410 ffc->fun = _frt_body_text;
1411 ffc->section_tag = "TEXT";
1412 }
1413 else if (allow_mime && mu_c_strcasecmp (p->token, "MIME") == 0)
1414 {
1415 imap4d_parsebuf_next (p, 1);
1416 ffc->fun = _frt_mime;
1417 ffc->section_tag = "MIME";
1418 }
1419 else
1420 return 1;
1421 return 0;
1422 }
1423
1424 static size_t
parsebuf_get_number(imap4d_parsebuf_t p)1425 parsebuf_get_number (imap4d_parsebuf_t p)
1426 {
1427 char *cp;
1428 unsigned long n = strtoul (p->token, &cp, 10);
1429
1430 if (*cp)
1431 imap4d_parsebuf_exit (p, "Syntax error: expected number");
1432 return n;
1433 }
1434
1435 /*
1436 section-part = nz-number *("." nz-number)
1437 ; body part nesting
1438 */
1439 static void
parse_section_part(imap4d_parsebuf_t p,struct fetch_function_closure * ffc)1440 parse_section_part (imap4d_parsebuf_t p, struct fetch_function_closure *ffc)
1441 {
1442 size_t *parts;
1443 size_t nmax = 0;
1444 size_t ncur = 0;
1445
1446 for (;;)
1447 {
1448 char *cp;
1449 size_t n = parsebuf_get_number (p);
1450 if (ncur == nmax)
1451 {
1452 if (nmax == 0)
1453 {
1454 nmax = 16;
1455 parts = calloc (nmax, sizeof (parts[0]));
1456 }
1457 else
1458 {
1459 nmax *= 2;
1460 parts = realloc (parts, nmax * sizeof (parts[0]));
1461 }
1462 if (!parts)
1463 imap4d_bye (ERR_NO_MEM);
1464 }
1465 parts[ncur++] = n;
1466
1467 imap4d_parsebuf_next (p, 1);
1468
1469 if (p->token[0] == '.'
1470 && (cp = imap4d_parsebuf_peek (p))
1471 && mu_isdigit (*cp))
1472 imap4d_parsebuf_next (p, 1);
1473 else
1474 break;
1475 }
1476 ffc->section_part = parts;
1477 ffc->nset = ncur;
1478 }
1479
1480 /*
1481 section = "[" [section-spec] "]"
1482 section-spec = section-msgtext / (section-part ["." section-text])
1483 */
1484 static int
parse_section(imap4d_parsebuf_t p,struct fetch_function_closure * ffc)1485 parse_section (imap4d_parsebuf_t p, struct fetch_function_closure *ffc)
1486 {
1487 if (p->token[0] != '[')
1488 return 1;
1489 ffc_init (ffc);
1490 ffc->name = NULL;
1491 ffc->fun = _frt_body_text;
1492 imap4d_parsebuf_next (p, 1);
1493 if (parse_section_text (p, ffc, 0))
1494 {
1495 if (p->token[0] == ']')
1496 ffc->fun = _frt_body;
1497 else if (mu_isdigit (p->token[0]))
1498 {
1499 parse_section_part (p, ffc);
1500 if (p->token[0] == '.')
1501 {
1502 imap4d_parsebuf_next (p, 1);
1503 parse_section_text (p, ffc, 1);
1504 }
1505 else
1506 ffc->fun = _frt_body_n;
1507 }
1508 else
1509 imap4d_parsebuf_exit (p, "Syntax error");
1510 }
1511 if (p->token[0] != ']')
1512 imap4d_parsebuf_exit (p, "Syntax error: missing ]");
1513 imap4d_parsebuf_next (p, 0);
1514 return 0;
1515 }
1516
1517 static void
parse_substring(imap4d_parsebuf_t p,struct fetch_function_closure * ffc)1518 parse_substring (imap4d_parsebuf_t p, struct fetch_function_closure *ffc)
1519 {
1520 if (p->token && p->token[0] == '<')
1521 {
1522 imap4d_parsebuf_next (p, 1);
1523 ffc->start = parsebuf_get_number (p);
1524 imap4d_parsebuf_next (p, 1);
1525 if (p->token[0] != '.')
1526 imap4d_parsebuf_exit (p, "Syntax error: expected .");
1527 imap4d_parsebuf_next (p, 1);
1528 ffc->size = parsebuf_get_number (p);
1529 imap4d_parsebuf_next (p, 1);
1530 if (p->token[0] != '>')
1531 imap4d_parsebuf_exit (p, "Syntax error: expected >");
1532 imap4d_parsebuf_next (p, 0);
1533 }
1534 }
1535
1536 /* section ["<" number "." nz-number ">"] */
1537 static int
parse_body_args(imap4d_parsebuf_t p,int peek)1538 parse_body_args (imap4d_parsebuf_t p, int peek)
1539 {
1540 struct fetch_function_closure ffc;
1541 if (parse_section (p, &ffc) == 0)
1542 {
1543 parse_substring (p, &ffc);
1544 ffc.peek = peek;
1545 append_ffc (imap4d_parsebuf_data (p), &ffc);
1546 return 0;
1547 }
1548 return 1;
1549 }
1550
1551 static void
parse_body_peek(imap4d_parsebuf_t p)1552 parse_body_peek (imap4d_parsebuf_t p)
1553 {
1554 imap4d_parsebuf_next (p, 1);
1555 if (mu_c_strcasecmp (p->token, "PEEK") == 0)
1556 {
1557 imap4d_parsebuf_next (p, 1);
1558 if (parse_body_args (p, 1))
1559 imap4d_parsebuf_exit (p, "Syntax error");
1560 }
1561 else
1562 imap4d_parsebuf_exit (p, "Syntax error: expected PEEK");
1563 }
1564
1565 /* "BODY" ["STRUCTURE"] /
1566 "BODY" section ["<" number "." nz-number ">"] /
1567 "BODY.PEEK" section ["<" number "." nz-number ">"] */
1568 static void
parse_fetch_body(imap4d_parsebuf_t p)1569 parse_fetch_body (imap4d_parsebuf_t p)
1570 {
1571 if (imap4d_parsebuf_next (p, 0) == NULL || p->token[0] == ')')
1572 append_simple_function (imap4d_parsebuf_data (p),
1573 "BODY", _frt_bodystructure0);
1574 else if (p->token[0] == '.')
1575 parse_body_peek (p);
1576 else if (mu_c_strcasecmp (p->token, "STRUCTURE") == 0)
1577 {
1578 /* For compatibility with previous versions */
1579 append_simple_function (imap4d_parsebuf_data (p),
1580 "BODYSTRUCTURE", _frt_bodystructure);
1581 imap4d_parsebuf_next (p, 0);
1582 }
1583 else if (parse_body_args (p, 0))
1584 append_simple_function (imap4d_parsebuf_data (p),
1585 "BODY", _frt_bodystructure0);
1586 }
1587
1588 static int
parse_fetch_att(imap4d_parsebuf_t p)1589 parse_fetch_att (imap4d_parsebuf_t p)
1590 {
1591 struct fetch_att_tab *ent;
1592 struct fetch_parse_closure *pclos = imap4d_parsebuf_data (p);
1593
1594 ent = find_fetch_att_tab (p->token);
1595 if (ent)
1596 {
1597 if (!(ent->fun == _frt_uid && pclos->isuid))
1598 append_simple_function (pclos, ent->name, ent->fun);
1599 imap4d_parsebuf_next (p, 0);
1600 }
1601 else if (mu_c_strcasecmp (p->token, "RFC822") == 0)
1602 parse_fetch_rfc822 (p);
1603 else if (mu_c_strcasecmp (p->token, "BODY") == 0)
1604 parse_fetch_body (p);
1605 else if (mu_c_strcasecmp (p->token, "BODYSTRUCTURE") == 0)
1606 {
1607 append_simple_function (pclos, "BODYSTRUCTURE", _frt_bodystructure);
1608 imap4d_parsebuf_next (p, 0);
1609 }
1610 else
1611 return 1;
1612 return 0;
1613 }
1614
1615 /* fetch-att *(SP fetch-att) */
1616 static void
parse_fetch_att_list(imap4d_parsebuf_t p)1617 parse_fetch_att_list (imap4d_parsebuf_t p)
1618 {
1619 while (p->token && parse_fetch_att (p) == 0)
1620 ;
1621 }
1622
1623 /* "ALL" / "FULL" / "FAST" / fetch-att / "(" */
1624 static void
parse_macro(imap4d_parsebuf_t p)1625 parse_macro (imap4d_parsebuf_t p)
1626 {
1627 char *exp;
1628
1629 imap4d_parsebuf_next (p, 1);
1630 if (p->token[0] == '(')
1631 {
1632 imap4d_parsebuf_next (p, 1);
1633 parse_fetch_att_list (p);
1634 if (!(p->token && p->token[0] == ')'))
1635 imap4d_parsebuf_exit (p, "Unknown token or missing closing parenthesis");
1636 }
1637 else if ((exp = find_macro (p->token)))
1638 {
1639 imap4d_tokbuf_t save_tok = p->tok;
1640 int save_arg = p->arg;
1641 p->tok = imap4d_tokbuf_from_string (exp);
1642 p->arg = 0;
1643 imap4d_parsebuf_next (p, 1);
1644 parse_fetch_att_list (p);
1645 imap4d_tokbuf_destroy (&p->tok);
1646
1647 p->arg = save_arg;
1648 p->tok = save_tok;
1649
1650 if (imap4d_parsebuf_peek (p))
1651 imap4d_parsebuf_exit (p, "Too many arguments");
1652 }
1653 else
1654 {
1655 parse_fetch_att (p);
1656 if (p->token)
1657 imap4d_parsebuf_exit (p, "Too many arguments");
1658 }
1659 }
1660
1661
1662 static int
fetch_thunk(imap4d_parsebuf_t pb)1663 fetch_thunk (imap4d_parsebuf_t pb)
1664 {
1665 int status;
1666 char *mstr;
1667 char *end;
1668 struct fetch_parse_closure *pclos = imap4d_parsebuf_data (pb);
1669
1670 mstr = imap4d_parsebuf_next (pb, 1);
1671
1672 status = mu_msgset_create (&pclos->msgset, mbox, MU_MSGSET_NUM);
1673 if (status)
1674 imap4d_parsebuf_exit (pb, "Software error");
1675
1676 /* Parse sequence numbers. */
1677 status = mu_msgset_parse_imap (pclos->msgset,
1678 pclos->isuid ? MU_MSGSET_UID : MU_MSGSET_NUM,
1679 mstr, &end);
1680 if (status)
1681 imap4d_parsebuf_exit (pb, "Failed to parse message set");
1682
1683 /* Compile the expression */
1684
1685 /* Server implementations MUST implicitly
1686 include the UID message data item as part of any FETCH
1687 response caused by a UID command, regardless of whether
1688 a UID was specified as a message data item to the FETCH. */
1689 if (pclos->isuid)
1690 append_simple_function (pclos, "UID", _frt_uid);
1691
1692 parse_macro (pb);
1693 return RESP_OK;
1694 }
1695
1696 int
_fetch_from_message(size_t msgno,mu_message_t msg,void * data)1697 _fetch_from_message (size_t msgno, mu_message_t msg, void *data)
1698 {
1699 int rc = 0;
1700 struct fetch_runtime_closure *frc = data;
1701
1702 frc->msgno = msgno;
1703 frc->msg = msg;
1704
1705 io_sendf ("* %lu FETCH (", (unsigned long) msgno);
1706 frc->eltno = 0;
1707 rc = mu_list_foreach (frc->fnlist, _do_fetch, frc);
1708 io_sendf (")\n");
1709
1710 return rc;
1711 }
1712
1713 /* Where the real implementation is. It is here since UID command also
1714 calls FETCH. */
1715 int
imap4d_fetch0(imap4d_tokbuf_t tok,int isuid,char ** err_text)1716 imap4d_fetch0 (imap4d_tokbuf_t tok, int isuid, char **err_text)
1717 {
1718 int rc;
1719 struct fetch_parse_closure pclos;
1720
1721 if (imap4d_tokbuf_argc (tok) - (IMAP4_ARG_1 + isuid) < 2)
1722 {
1723 *err_text = "Invalid arguments";
1724 return 1;
1725 }
1726
1727 memset (&pclos, 0, sizeof (pclos));
1728 pclos.isuid = isuid;
1729 mu_list_create (&pclos.fnlist);
1730 mu_list_set_destroy_item (pclos.fnlist, _free_ffc);
1731
1732 rc = imap4d_with_parsebuf (tok, IMAP4_ARG_1 + isuid,
1733 ".[]<>",
1734 fetch_thunk, &pclos,
1735 err_text);
1736
1737 if (rc == RESP_OK)
1738 {
1739 struct fetch_runtime_closure frc;
1740
1741 memset (&frc, 0, sizeof (frc));
1742 frc.fnlist = pclos.fnlist;
1743 /* Prepare status code. It will be replaced if an error occurs in the
1744 loop below */
1745 frc.err_text = "Completed";
1746
1747 mu_msgset_foreach_message (pclos.msgset, _fetch_from_message, &frc);
1748 mu_list_destroy (&frc.msglist);
1749 }
1750
1751 mu_list_destroy (&pclos.fnlist);
1752 mu_msgset_free (pclos.msgset);
1753 return rc;
1754 }
1755
1756
1757 /*
1758 6.4.5. FETCH Command
1759
1760 Arguments: message set
1761 message data item names
1762
1763 Responses: untagged responses: FETCH
1764
1765 Result: OK - fetch completed
1766 NO - fetch error: can't fetch that data
1767 BAD - command unknown or arguments invalid
1768 */
1769
1770 /* The FETCH command retrieves data associated with a message in the
1771 mailbox. The data items to be fetched can be either a single atom
1772 or a parenthesized list. */
1773 int
imap4d_fetch(struct imap4d_session * session,struct imap4d_command * command,imap4d_tokbuf_t tok)1774 imap4d_fetch (struct imap4d_session *session,
1775 struct imap4d_command *command, imap4d_tokbuf_t tok)
1776 {
1777 int rc;
1778 char *err_text = "Completed";
1779 int xlev = set_xscript_level (MU_XSCRIPT_PAYLOAD);
1780 rc = imap4d_fetch0 (tok, 0, &err_text);
1781 set_xscript_level (xlev);
1782 return io_completion_response (command, rc, "%s", err_text);
1783 }
1784