4 * Implements the FETCH command in IMAP.
5 * This command is way too convoluted. Marc Crispin is a fscking idiot.
18 #include <sys/types.h>
20 #if TIME_WITH_SYS_TIME
21 # include <sys/time.h>
25 # include <sys/time.h>
37 #include "sysdep_decls.h"
38 #include "citserver.h"
41 #include "serv_extensions.h"
48 #include "internet_addressing.h"
49 #include "mime_parser.h"
50 #include "serv_imap.h"
51 #include "imap_tools.h"
52 #include "imap_fetch.h"
57 struct imap_fetch_part {
58 char desired_section[SIZ];
63 * Individual field functions for imap_do_fetch_msg() ...
68 void imap_fetch_uid(int seq) {
69 cprintf("UID %ld", IMAP->msgids[seq-1]);
72 void imap_fetch_flags(int seq) {
73 int num_flags_printed = 0;
75 if (IMAP->flags[seq] & IMAP_DELETED) {
76 if (num_flags_printed > 0) cprintf(" ");
80 if (IMAP->flags[seq] & IMAP_SEEN) {
81 if (num_flags_printed > 0) cprintf(" ");
88 void imap_fetch_internaldate(struct CtdlMessage *msg) {
92 if (msg->cm_fields['T'] != NULL) {
93 msgdate = atol(msg->cm_fields['T']);
99 datestring(buf, sizeof buf, msgdate, DATESTRING_IMAP);
100 cprintf("INTERNALDATE \"%s\"", buf);
105 * Fetch RFC822-formatted messages.
107 * 'whichfmt' should be set to one of:
108 * "RFC822" entire message
109 * "RFC822.HEADER" headers only (with trailing blank line)
110 * "RFC822.SIZE" size of translated message
111 * "RFC822.TEXT" body only (without leading blank line)
113 void imap_fetch_rfc822(int msgnum, char *whichfmt, struct CtdlMessage *msg) {
116 long headers_size, text_size, total_size;
117 long bytes_remaining = 0;
123 lprintf(1, "Cannot open temp file: %s\n",
129 * Load the message into a temp file for translation
132 CtdlRedirectOutput(tmp, -1);
133 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
135 CtdlRedirectOutput(NULL, -1);
136 if (!is_valid_message(msg)) {
137 lprintf(1, "WARNING: output clobbered the message!\n");
141 * Now figure out where the headers/text break is. IMAP considers the
142 * intervening blank line to be part of the headers, not the text.
147 ptr = fgets(buf, sizeof buf, tmp);
150 if (strlen(buf) == 0) {
151 headers_size = ftell(tmp);
154 } while ( (headers_size == 0L) && (ptr != NULL) );
155 fseek(tmp, 0L, SEEK_END);
156 total_size = ftell(tmp);
157 text_size = total_size - headers_size;
159 if (!strcasecmp(whichfmt, "RFC822.SIZE")) {
160 cprintf("RFC822.SIZE %ld", total_size);
165 else if (!strcasecmp(whichfmt, "RFC822")) {
166 bytes_remaining = total_size;
170 else if (!strcasecmp(whichfmt, "RFC822.HEADER")) {
171 bytes_remaining = headers_size;
175 else if (!strcasecmp(whichfmt, "RFC822.TEXT")) {
176 bytes_remaining = text_size;
177 fseek(tmp, headers_size, SEEK_SET);
180 cprintf("%s {%ld}\r\n", whichfmt, bytes_remaining);
181 blocksize = sizeof(buf);
182 while (bytes_remaining > 0L) {
183 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
184 fread(buf, blocksize, 1, tmp);
185 client_write(buf, blocksize);
186 bytes_remaining = bytes_remaining - blocksize;
195 * Load a specific part of a message into the temp file to be output to a
196 * client. FIXME we can handle parts like "2" and "2.1" and even "2.MIME"
197 * but we still can't handle "2.HEADER" (which might not be a problem, because
198 * we currently don't have the ability to break out nested RFC822's anyway).
200 * Note: mime_parser() was called with dont_decode set to 1, so we have the
201 * luxury of simply spewing without having to re-encode.
203 void imap_load_part(char *name, char *filename, char *partnum, char *disp,
204 void *content, char *cbtype, size_t length, char *encoding,
207 struct imap_fetch_part *imfp;
210 imfp = (struct imap_fetch_part *)cbuserdata;
212 if (!strcasecmp(partnum, imfp->desired_section)) {
213 fwrite(content, length, 1, imfp->output_fp);
216 snprintf(mbuf2, sizeof mbuf2, "%s.MIME", partnum);
218 if (!strcasecmp(imfp->desired_section, mbuf2)) {
219 fprintf(imfp->output_fp, "Content-type: %s", cbtype);
220 if (strlen(name) > 0)
221 fprintf(imfp->output_fp, "; name=\"%s\"", name);
222 fprintf(imfp->output_fp, "\r\n");
223 if (strlen(encoding) > 0)
224 fprintf(imfp->output_fp,
225 "Content-Transfer-Encoding: %s\r\n", encoding);
226 if (strlen(encoding) > 0) {
227 fprintf(imfp->output_fp, "Content-Disposition: %s",
229 if (strlen(filename) > 0) {
230 fprintf(imfp->output_fp, "; filename=\"%s\"",
233 fprintf(imfp->output_fp, "\r\n");
235 fprintf(imfp->output_fp, "Content-Length: %ld\r\n", (long)length);
236 fprintf(imfp->output_fp, "\r\n");
244 * Called by imap_fetch_envelope() to output the "From" field.
245 * This is in its own function because its logic is kind of complex. We
246 * really need to make this suck less.
248 void imap_output_envelope_from(struct CtdlMessage *msg) {
249 char user[1024], node[1024], name[1024];
251 cprintf("(("); /* open double-parens */
252 imap_strout(msg->cm_fields['A']); /* personal name */
253 cprintf(" NIL "); /* source route (not used) */
255 if (msg->cm_fields['F'] != NULL) {
256 process_rfc822_addr(msg->cm_fields['F'], user, node, name);
257 imap_strout(user); /* mailbox name (user id) */
259 if (!strcasecmp(node, config.c_nodename)) {
260 imap_strout(config.c_fqdn);
263 imap_strout(node); /* host name */
267 imap_strout(msg->cm_fields['A']); /* mailbox name (user id) */
269 imap_strout(msg->cm_fields['N']); /* host name */
272 cprintf(")) "); /* close double-parens */
277 * Implements the ENVELOPE fetch item
279 * FIXME ... we only output some of the fields right now. Definitely need
280 * to do all of them. Accurately, too.
282 * Note that the imap_strout() function can cleverly output NULL fields as NIL,
283 * so we don't have to check for that condition like we do elsewhere.
285 void imap_fetch_envelope(long msgnum, struct CtdlMessage *msg) {
286 char datestringbuf[SIZ];
288 char *fieldptr = NULL;
290 /* Parse the message date into an IMAP-format date string */
291 if (msg->cm_fields['T'] != NULL) {
292 msgdate = atol(msg->cm_fields['T']);
295 msgdate = time(NULL);
297 datestring(datestringbuf, sizeof datestringbuf,
298 msgdate, DATESTRING_IMAP);
300 /* Now start spewing data fields. The order is important, as it is
301 * defined by the protocol specification. Nonexistent fields must
302 * be output as NIL, existent fields must be quoted or literalled.
303 * The imap_strout() function conveniently does all this for us.
305 cprintf("ENVELOPE (");
308 imap_strout(datestringbuf);
312 imap_strout(msg->cm_fields['U']);
316 imap_output_envelope_from(msg);
323 imap_output_envelope_from(msg);
331 imap_output_envelope_from(msg);
334 cprintf("NIL "); /* to */
336 cprintf("NIL "); /* cc */
338 cprintf("NIL "); /* bcc */
341 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "In-reply-to");
342 imap_strout(fieldptr);
344 if (fieldptr != NULL) phree(fieldptr);
347 imap_strout(msg->cm_fields['I']);
354 * Strip any non header information out of a chunk of RFC822 data on disk,
355 * then boil it down to just the fields we want.
357 void imap_strip_headers(FILE *fp, char *section) {
359 char *which_fields = NULL;
360 int doing_headers = 0;
365 char *boiled_headers = NULL;
367 int done_headers = 0;
369 which_fields = strdoop(section);
371 if (!strncasecmp(which_fields, "HEADER.FIELDS", 13))
373 if (!strncasecmp(which_fields, "HEADER.FIELDS.NOT", 17))
376 for (i=0; i<strlen(which_fields); ++i) {
377 if (which_fields[i]=='(')
378 strcpy(which_fields, &which_fields[i+1]);
380 for (i=0; i<strlen(which_fields); ++i) {
381 if (which_fields[i]==')')
384 num_parms = imap_parameterize(parms, which_fields);
386 fseek(fp, 0L, SEEK_END);
387 boiled_headers = mallok((size_t)(ftell(fp) + 256L));
388 strcpy(boiled_headers, "");
392 while ( (done_headers == 0) && (fgets(buf, sizeof buf, fp) != NULL) ) {
393 if (!isspace(buf[0])) {
395 if (doing_headers == 0) ok = 1;
397 if (headers_not) ok = 1;
399 for (i=0; i<num_parms; ++i) {
400 if ( (!strncasecmp(buf, parms[i],
401 strlen(parms[i]))) &&
402 (buf[strlen(parms[i])]==':') ) {
403 if (headers_not) ok = 0;
411 strcat(boiled_headers, buf);
414 if (strlen(buf) == 0) done_headers = 1;
415 if (buf[0]=='\r') done_headers = 1;
416 if (buf[0]=='\n') done_headers = 1;
419 /* Now write it back */
421 fwrite(boiled_headers, strlen(boiled_headers), 1, fp);
423 ftruncate(fileno(fp), ftell(fp));
427 phree(boiled_headers);
432 * Implements the BODY and BODY.PEEK fetch items
434 void imap_fetch_body(long msgnum, char *item, int is_peek,
435 struct CtdlMessage *msg) {
442 long bytes_remaining = 0;
445 struct imap_fetch_part imfp;
447 /* extract section */
448 strcpy(section, item);
449 for (i=0; i<strlen(section); ++i) {
450 if (section[i]=='[') strcpy(section, §ion[i+1]);
452 for (i=0; i<strlen(section); ++i) {
453 if (section[i]==']') section[i] = 0;
455 lprintf(9, "Section is %s\n", section);
457 /* extract partial */
458 strcpy(partial, item);
459 for (i=0; i<strlen(partial); ++i) {
460 if (partial[i]=='<') {
461 strcpy(partial, &partial[i+1]);
465 for (i=0; i<strlen(partial); ++i) {
466 if (partial[i]=='>') partial[i] = 0;
468 if (is_partial == 0) strcpy(partial, "");
469 if (strlen(partial) > 0) lprintf(9, "Partial is %s\n", partial);
473 lprintf(1, "Cannot open temp file: %s\n", strerror(errno));
477 /* Now figure out what the client wants, and get it */
479 if ( (!strcmp(section, "1")) && (msg->cm_format_type != 4) ) {
480 CtdlRedirectOutput(tmp, -1);
481 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
483 CtdlRedirectOutput(NULL, -1);
486 else if (!strcmp(section, "")) {
487 CtdlRedirectOutput(tmp, -1);
488 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
490 CtdlRedirectOutput(NULL, -1);
494 * If the client asked for just headers, or just particular header
495 * fields, strip it down.
497 else if (!strncasecmp(section, "HEADER", 6)) {
498 CtdlRedirectOutput(tmp, -1);
499 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
501 CtdlRedirectOutput(NULL, -1);
502 imap_strip_headers(tmp, section);
506 * Strip it down if the client asked for everything _except_ headers.
508 else if (!strncasecmp(section, "TEXT", 4)) {
509 CtdlRedirectOutput(tmp, -1);
510 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
512 CtdlRedirectOutput(NULL, -1);
516 * Anything else must be a part specifier.
517 * (Note value of 1 passed as 'dont_decode' so client gets it encoded)
520 safestrncpy(imfp.desired_section, section,
521 sizeof(imfp.desired_section));
522 imfp.output_fp = tmp;
524 mime_parser(msg->cm_fields['M'], NULL,
525 *imap_load_part, NULL, NULL,
531 fseek(tmp, 0L, SEEK_END);
532 bytes_remaining = ftell(tmp);
534 if (is_partial == 0) {
536 cprintf("BODY[%s] {%ld}\r\n", section, bytes_remaining);
539 sscanf(partial, "%ld.%ld", &pstart, &pbytes);
540 if ((bytes_remaining - pstart) < pbytes) {
541 pbytes = bytes_remaining - pstart;
543 fseek(tmp, pstart, SEEK_SET);
544 bytes_remaining = pbytes;
545 cprintf("BODY[%s]<%ld> {%ld}\r\n",
546 section, pstart, bytes_remaining);
549 blocksize = sizeof(buf);
550 while (bytes_remaining > 0L) {
551 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
552 fread(buf, blocksize, 1, tmp);
553 client_write(buf, blocksize);
554 bytes_remaining = bytes_remaining - blocksize;
559 /* Mark this message as "seen" *unless* this is a "peek" operation */
561 CtdlSetSeen(msgnum, 1);
566 * Called immediately before outputting a multipart bodystructure
568 void imap_fetch_bodystructure_pre(
569 char *name, char *filename, char *partnum, char *disp,
570 void *content, char *cbtype, size_t length, char *encoding,
580 * Called immediately after outputting a multipart bodystructure
582 void imap_fetch_bodystructure_post(
583 char *name, char *filename, char *partnum, char *disp,
584 void *content, char *cbtype, size_t length, char *encoding,
593 extract_token(subtype, cbtype, 1, '/');
594 imap_strout(subtype);
605 * Output the info for a MIME part in the format required by BODYSTRUCTURE.
608 void imap_fetch_bodystructure_part(
609 char *name, char *filename, char *partnum, char *disp,
610 void *content, char *cbtype, size_t length, char *encoding,
615 int have_encoding = 0;
618 char cbmaintype[SIZ];
621 if (cbtype != NULL) if (strlen(cbtype)>0) have_cbtype = 1;
623 extract_token(cbmaintype, cbtype, 0, '/');
624 extract_token(cbsubtype, cbtype, 1, '/');
627 strcpy(cbmaintype, "TEXT");
628 strcpy(cbsubtype, "PLAIN");
632 imap_strout(cbmaintype);
634 imap_strout(cbsubtype);
637 cprintf("(\"CHARSET\" \"US-ASCII\"");
639 if (name != NULL) if (strlen(name)>0) {
640 cprintf(" \"NAME\" ");
646 cprintf("NIL "); /* Body ID */
647 cprintf("NIL "); /* Body description */
649 if (encoding != NULL) if (strlen(encoding) > 0) have_encoding = 1;
651 imap_strout(encoding);
658 /* The next field is the size of the part in bytes. */
659 cprintf("%ld ", (long)length); /* bytes */
661 /* The next field is the number of lines in the part, if and only
662 * if the part is TEXT. Crispin is a fscking idiot.
664 if (!strcasecmp(cbmaintype, "TEXT")) {
665 if (length) for (i=0; i<length; ++i) {
666 if (((char *)content)[i] == '\n') ++lines;
668 cprintf("%d ", lines);
671 /* More of Crispin being a fscking idiot */
672 if ((!strcasecmp(cbmaintype, "MESSAGE"))
673 && (!strcasecmp(cbsubtype, "RFC822"))) {
675 A body type of type MESSAGE and subtype RFC822
676 contains, immediately after the basic fields, the
677 envelope structure, body structure, and size in
678 text lines of the encapsulated message.
682 /* MD5 value of body part; we can get away with NIL'ing this */
689 else if (strlen(disp) == 0) {
695 if (filename != NULL) if (strlen(filename)>0) {
696 cprintf(" (\"FILENAME\" ");
697 imap_strout(filename);
703 /* Body language (not defined yet) */
710 * Spew the BODYSTRUCTURE data for a message. (Do you need a silencer if
711 * you're going to shoot a MIME? Do you need a reason to shoot Mark Crispin?
715 void imap_fetch_bodystructure (long msgnum, char *item,
716 struct CtdlMessage *msg) {
720 long start_of_body = 0L;
721 long body_bytes = 0L;
723 /* For non-RFC822 (ordinary Citadel) messages, this is short and
726 if (msg->cm_format_type != FMT_RFC822) {
728 /* *sigh* We have to RFC822-format the message just to be able
732 if (tmp == NULL) return;
733 CtdlRedirectOutput(tmp, -1);
734 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822, 0, 0, 1);
735 CtdlRedirectOutput(NULL, -1);
738 while (fgets(buf, sizeof buf, tmp) != NULL) {
740 if ((!strcmp(buf, "\r\n")) && (start_of_body == 0L)) {
741 start_of_body = ftell(tmp);
744 body_bytes = ftell(tmp) - start_of_body;
747 cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" "
748 "(\"CHARSET\" \"US-ASCII\") NIL NIL "
749 "\"7BIT\" %ld %ld)", body_bytes, lines);
754 /* For messages already stored in RFC822 format, we have to parse. */
755 cprintf("BODYSTRUCTURE ");
756 mime_parser(msg->cm_fields['M'],
758 *imap_fetch_bodystructure_part, /* part */
759 *imap_fetch_bodystructure_pre, /* pre-multi */
760 *imap_fetch_bodystructure_post, /* post-multi */
762 1); /* don't decode -- we want it as-is */
771 * imap_do_fetch() calls imap_do_fetch_msg() to output the data of an
772 * individual message, once it has been successfully loaded from disk.
774 void imap_do_fetch_msg(int seq, struct CtdlMessage *msg,
775 int num_items, char **itemlist) {
778 cprintf("* %d FETCH (", seq);
780 for (i=0; i<num_items; ++i) {
782 if (!strncasecmp(itemlist[i], "BODY[", 5)) {
783 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
786 else if (!strncasecmp(itemlist[i], "BODY.PEEK[", 10)) {
787 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
790 else if (!strcasecmp(itemlist[i], "BODYSTRUCTURE")) {
791 imap_fetch_bodystructure(IMAP->msgids[seq-1],
794 else if (!strcasecmp(itemlist[i], "ENVELOPE")) {
795 imap_fetch_envelope(IMAP->msgids[seq-1], msg);
797 else if (!strcasecmp(itemlist[i], "FLAGS")) {
798 imap_fetch_flags(seq-1);
800 else if (!strcasecmp(itemlist[i], "INTERNALDATE")) {
801 imap_fetch_internaldate(msg);
803 else if (!strcasecmp(itemlist[i], "RFC822")) {
804 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
806 else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
807 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
809 else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
810 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
812 else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
813 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
815 else if (!strcasecmp(itemlist[i], "UID")) {
819 if (i != num_items-1) cprintf(" ");
828 * imap_fetch() calls imap_do_fetch() to do its actual work, once it's
829 * validated and boiled down the request a bit.
831 void imap_do_fetch(int num_items, char **itemlist) {
833 struct CtdlMessage *msg;
835 if (IMAP->num_msgs > 0)
836 for (i = 0; i < IMAP->num_msgs; ++i)
837 if (IMAP->flags[i] & IMAP_SELECTED) {
838 msg = CtdlFetchMessage(IMAP->msgids[i]);
840 imap_do_fetch_msg(i+1, msg, num_items, itemlist);
841 CtdlFreeMessage(msg);
844 cprintf("* %d FETCH <internal error>\r\n", i+1);
852 * Back end for imap_handle_macros()
853 * Note that this function *only* looks at the beginning of the string. It
854 * is not a generic search-and-replace function.
856 void imap_macro_replace(char *str, char *find, char *replace) {
859 if (!strncasecmp(str, find, strlen(find))) {
860 if (str[strlen(find)]==' ') {
861 strcpy(holdbuf, &str[strlen(find)+1]);
862 strcpy(str, replace);
864 strcat(str, holdbuf);
866 if (str[strlen(find)]==0) {
867 strcpy(holdbuf, &str[strlen(find)+1]);
868 strcpy(str, replace);
876 * Handle macros embedded in FETCH data items.
877 * (What the heck are macros doing in a wire protocol? Are we trying to save
878 * the computer at the other end the trouble of typing a lot of characters?)
880 void imap_handle_macros(char *str) {
884 for (i=0; i<strlen(str); ++i) {
885 if (str[i]=='(') ++nest;
886 if (str[i]=='[') ++nest;
887 if (str[i]=='<') ++nest;
888 if (str[i]=='{') ++nest;
889 if (str[i]==')') --nest;
890 if (str[i]==']') --nest;
891 if (str[i]=='>') --nest;
892 if (str[i]=='}') --nest;
895 imap_macro_replace(&str[i],
897 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"
899 imap_macro_replace(&str[i],
903 imap_macro_replace(&str[i],
905 "FLAGS INTERNALDATE RFC822.SIZE"
907 imap_macro_replace(&str[i],
909 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"
917 * Break out the data items requested, possibly a parenthesized list.
918 * Returns the number of data items, or -1 if the list is invalid.
919 * NOTE: this function alters the string it is fed, and uses it as a buffer
920 * to hold the data for the pointers it returns.
922 int imap_extract_data_items(char **argv, char *items) {
928 /* Convert all whitespace to ordinary space characters. */
929 for (i=0; i<strlen(items); ++i) {
930 if (isspace(items[i])) items[i]=' ';
933 /* Strip leading and trailing whitespace, then strip leading and
934 * trailing parentheses if it's a list
937 if ( (items[0]=='(') && (items[strlen(items)-1]==')') ) {
938 items[strlen(items)-1] = 0;
939 strcpy(items, &items[1]);
943 /* Parse any macro data items */
944 imap_handle_macros(items);
947 * Now break out the data items. We throw in one trailing space in
948 * order to avoid having to break out the last one manually.
952 initial_len = strlen(items);
953 for (i=0; i<initial_len; ++i) {
954 if (items[i]=='(') ++nest;
955 if (items[i]=='[') ++nest;
956 if (items[i]=='<') ++nest;
957 if (items[i]=='{') ++nest;
958 if (items[i]==')') --nest;
959 if (items[i]==']') --nest;
960 if (items[i]=='>') --nest;
961 if (items[i]=='}') --nest;
963 if (nest <= 0) if (items[i]==' ') {
965 argv[num_items++] = start;
976 * One particularly hideous aspect of IMAP is that we have to allow the client
977 * to specify arbitrary ranges and/or sets of messages to fetch. Citadel IMAP
978 * handles this by setting the IMAP_SELECTED flag for each message specified in
979 * the ranges/sets, then looping through the message array, outputting messages
980 * with the flag set. We don't bother returning an error if an out-of-range
981 * number is specified (we just return quietly) because any client braindead
982 * enough to request a bogus message number isn't going to notice the
985 * This function clears out the IMAP_SELECTED bits, then sets that bit for each
986 * message included in the specified range.
988 * Set is_uid to 1 to fetch by UID instead of sequence number.
990 void imap_pick_range(char *supplied_range, int is_uid) {
994 char setstr[SIZ], lostr[SIZ], histr[SIZ]; /* was 1024 */
996 char actual_range[SIZ];
999 * Handle the "ALL" macro
1001 if (!strcasecmp(supplied_range, "ALL")) {
1002 safestrncpy(actual_range, "1:*", sizeof actual_range);
1005 safestrncpy(actual_range, supplied_range, sizeof actual_range);
1009 * Clear out the IMAP_SELECTED flags for all messages.
1011 for (i = 0; i < IMAP->num_msgs; ++i) {
1012 IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SELECTED;
1016 * Now set it for all specified messages.
1018 num_sets = num_tokens(actual_range, ',');
1019 for (s=0; s<num_sets; ++s) {
1020 extract_token(setstr, actual_range, s, ',');
1022 extract_token(lostr, setstr, 0, ':');
1023 if (num_tokens(setstr, ':') >= 2) {
1024 extract_token(histr, setstr, 1, ':');
1025 if (!strcmp(histr, "*")) snprintf(histr, sizeof histr, "%d", INT_MAX);
1028 strcpy(histr, lostr);
1033 /* Loop through the array, flipping bits where appropriate */
1034 for (i = 1; i <= IMAP->num_msgs; ++i) {
1035 if (is_uid) { /* fetch by sequence number */
1036 if ( (IMAP->msgids[i-1]>=lo)
1037 && (IMAP->msgids[i-1]<=hi)) {
1039 IMAP->flags[i-1] | IMAP_SELECTED;
1042 else { /* fetch by uid */
1043 if ( (i>=lo) && (i<=hi)) {
1045 IMAP->flags[i-1] | IMAP_SELECTED;
1056 * This function is called by the main command loop.
1058 void imap_fetch(int num_parms, char *parms[]) {
1059 char items[SIZ]; /* was 1024 */
1060 char *itemlist[SIZ];
1064 if (num_parms < 4) {
1065 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1069 imap_pick_range(parms[2], 0);
1072 for (i=3; i<num_parms; ++i) {
1073 strcat(items, parms[i]);
1074 if (i < (num_parms-1)) strcat(items, " ");
1077 num_items = imap_extract_data_items(itemlist, items);
1078 if (num_items < 1) {
1079 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1083 imap_do_fetch(num_items, itemlist);
1084 cprintf("%s OK FETCH completed\r\n", parms[0]);
1088 * This function is called by the main command loop.
1090 void imap_uidfetch(int num_parms, char *parms[]) {
1091 char items[SIZ]; /* was 1024 */
1092 char *itemlist[SIZ];
1095 int have_uid_item = 0;
1097 if (num_parms < 5) {
1098 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1102 imap_pick_range(parms[3], 1);
1105 for (i=4; i<num_parms; ++i) {
1106 strcat(items, parms[i]);
1107 if (i < (num_parms-1)) strcat(items, " ");
1110 num_items = imap_extract_data_items(itemlist, items);
1111 if (num_items < 1) {
1112 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1116 /* If the "UID" item was not included, we include it implicitly
1117 * because this is a UID FETCH command
1119 for (i=0; i<num_items; ++i) {
1120 if (!strcasecmp(itemlist[i], "UID")) ++have_uid_item;
1122 if (have_uid_item == 0) itemlist[num_items++] = "UID";
1124 imap_do_fetch(num_items, itemlist);
1125 cprintf("%s OK UID FETCH completed\r\n", parms[0]);