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, &param);
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 (&param);
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