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(" ");
85 if (IMAP->flags[seq] & IMAP_ANSWERED) {
86 if (num_flags_printed > 0) cprintf(" ");
87 cprintf("\\Answered");
93 void imap_fetch_internaldate(struct CtdlMessage *msg) {
97 if (msg->cm_fields['T'] != NULL) {
98 msgdate = atol(msg->cm_fields['T']);
101 msgdate = time(NULL);
104 datestring(buf, sizeof buf, msgdate, DATESTRING_IMAP);
105 cprintf("INTERNALDATE \"%s\"", buf);
110 * Fetch RFC822-formatted messages.
112 * 'whichfmt' should be set to one of:
113 * "RFC822" entire message
114 * "RFC822.HEADER" headers only (with trailing blank line)
115 * "RFC822.SIZE" size of translated message
116 * "RFC822.TEXT" body only (without leading blank line)
118 void imap_fetch_rfc822(int msgnum, char *whichfmt, struct CtdlMessage *msg) {
121 long headers_size, text_size, total_size;
122 long bytes_remaining = 0;
128 lprintf(1, "Cannot open temp file: %s\n",
134 * Load the message into a temp file for translation
137 CtdlRedirectOutput(tmp, -1);
138 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
140 CtdlRedirectOutput(NULL, -1);
141 if (!is_valid_message(msg)) {
142 lprintf(1, "WARNING: output clobbered the message!\n");
146 * Now figure out where the headers/text break is. IMAP considers the
147 * intervening blank line to be part of the headers, not the text.
152 ptr = fgets(buf, sizeof buf, tmp);
155 if (strlen(buf) == 0) {
156 headers_size = ftell(tmp);
159 } while ( (headers_size == 0L) && (ptr != NULL) );
160 fseek(tmp, 0L, SEEK_END);
161 total_size = ftell(tmp);
162 text_size = total_size - headers_size;
164 if (!strcasecmp(whichfmt, "RFC822.SIZE")) {
165 cprintf("RFC822.SIZE %ld", total_size);
170 else if (!strcasecmp(whichfmt, "RFC822")) {
171 bytes_remaining = total_size;
175 else if (!strcasecmp(whichfmt, "RFC822.HEADER")) {
176 bytes_remaining = headers_size;
180 else if (!strcasecmp(whichfmt, "RFC822.TEXT")) {
181 bytes_remaining = text_size;
182 fseek(tmp, headers_size, SEEK_SET);
185 cprintf("%s {%ld}\r\n", whichfmt, bytes_remaining);
186 blocksize = sizeof(buf);
187 while (bytes_remaining > 0L) {
188 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
189 fread(buf, blocksize, 1, tmp);
190 client_write(buf, blocksize);
191 bytes_remaining = bytes_remaining - blocksize;
200 * Load a specific part of a message into the temp file to be output to a
201 * client. FIXME we can handle parts like "2" and "2.1" and even "2.MIME"
202 * but we still can't handle "2.HEADER" (which might not be a problem, because
203 * we currently don't have the ability to break out nested RFC822's anyway).
205 * Note: mime_parser() was called with dont_decode set to 1, so we have the
206 * luxury of simply spewing without having to re-encode.
208 void imap_load_part(char *name, char *filename, char *partnum, char *disp,
209 void *content, char *cbtype, size_t length, char *encoding,
212 struct imap_fetch_part *imfp;
215 imfp = (struct imap_fetch_part *)cbuserdata;
217 if (!strcasecmp(partnum, imfp->desired_section)) {
218 fwrite(content, length, 1, imfp->output_fp);
221 snprintf(mbuf2, sizeof mbuf2, "%s.MIME", partnum);
223 if (!strcasecmp(imfp->desired_section, mbuf2)) {
224 fprintf(imfp->output_fp, "Content-type: %s", cbtype);
225 if (strlen(name) > 0)
226 fprintf(imfp->output_fp, "; name=\"%s\"", name);
227 fprintf(imfp->output_fp, "\r\n");
228 if (strlen(encoding) > 0)
229 fprintf(imfp->output_fp,
230 "Content-Transfer-Encoding: %s\r\n", encoding);
231 if (strlen(encoding) > 0) {
232 fprintf(imfp->output_fp, "Content-Disposition: %s",
234 if (strlen(filename) > 0) {
235 fprintf(imfp->output_fp, "; filename=\"%s\"",
238 fprintf(imfp->output_fp, "\r\n");
240 fprintf(imfp->output_fp, "Content-Length: %ld\r\n", (long)length);
241 fprintf(imfp->output_fp, "\r\n");
249 * Called by imap_fetch_envelope() to output the "From" field.
250 * This is in its own function because its logic is kind of complex. We
251 * really need to make this suck less.
253 void imap_output_envelope_from(struct CtdlMessage *msg) {
254 char user[1024], node[1024], name[1024];
256 cprintf("(("); /* open double-parens */
257 imap_strout(msg->cm_fields['A']); /* personal name */
258 cprintf(" NIL "); /* source route (not used) */
260 if (msg->cm_fields['F'] != NULL) {
261 process_rfc822_addr(msg->cm_fields['F'], user, node, name);
262 imap_strout(user); /* mailbox name (user id) */
264 if (!strcasecmp(node, config.c_nodename)) {
265 imap_strout(config.c_fqdn);
268 imap_strout(node); /* host name */
272 imap_strout(msg->cm_fields['A']); /* mailbox name (user id) */
274 imap_strout(msg->cm_fields['N']); /* host name */
277 cprintf(")) "); /* close double-parens */
282 * Implements the ENVELOPE fetch item
284 * FIXME ... we only output some of the fields right now. Definitely need
285 * to do all of them. Accurately, too.
287 * Note that the imap_strout() function can cleverly output NULL fields as NIL,
288 * so we don't have to check for that condition like we do elsewhere.
290 void imap_fetch_envelope(long msgnum, struct CtdlMessage *msg) {
291 char datestringbuf[SIZ];
293 char *fieldptr = NULL;
295 /* Parse the message date into an IMAP-format date string */
296 if (msg->cm_fields['T'] != NULL) {
297 msgdate = atol(msg->cm_fields['T']);
300 msgdate = time(NULL);
302 datestring(datestringbuf, sizeof datestringbuf,
303 msgdate, DATESTRING_IMAP);
305 /* Now start spewing data fields. The order is important, as it is
306 * defined by the protocol specification. Nonexistent fields must
307 * be output as NIL, existent fields must be quoted or literalled.
308 * The imap_strout() function conveniently does all this for us.
310 cprintf("ENVELOPE (");
313 imap_strout(datestringbuf);
317 imap_strout(msg->cm_fields['U']);
321 imap_output_envelope_from(msg);
328 imap_output_envelope_from(msg);
336 imap_output_envelope_from(msg);
339 cprintf("NIL "); /* to */
341 cprintf("NIL "); /* cc */
343 cprintf("NIL "); /* bcc */
346 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "In-reply-to");
347 imap_strout(fieldptr);
349 if (fieldptr != NULL) phree(fieldptr);
352 imap_strout(msg->cm_fields['I']);
359 * Strip any non header information out of a chunk of RFC822 data on disk,
360 * then boil it down to just the fields we want.
362 void imap_strip_headers(FILE *fp, char *section) {
364 char *which_fields = NULL;
365 int doing_headers = 0;
370 char *boiled_headers = NULL;
372 int done_headers = 0;
374 which_fields = strdoop(section);
376 if (!strncasecmp(which_fields, "HEADER.FIELDS", 13))
378 if (!strncasecmp(which_fields, "HEADER.FIELDS.NOT", 17))
381 for (i=0; i<strlen(which_fields); ++i) {
382 if (which_fields[i]=='(')
383 strcpy(which_fields, &which_fields[i+1]);
385 for (i=0; i<strlen(which_fields); ++i) {
386 if (which_fields[i]==')')
389 num_parms = imap_parameterize(parms, which_fields);
391 fseek(fp, 0L, SEEK_END);
392 boiled_headers = mallok((size_t)(ftell(fp) + 256L));
393 strcpy(boiled_headers, "");
397 while ( (done_headers == 0) && (fgets(buf, sizeof buf, fp) != NULL) ) {
398 if (!isspace(buf[0])) {
400 if (doing_headers == 0) ok = 1;
402 if (headers_not) ok = 1;
404 for (i=0; i<num_parms; ++i) {
405 if ( (!strncasecmp(buf, parms[i],
406 strlen(parms[i]))) &&
407 (buf[strlen(parms[i])]==':') ) {
408 if (headers_not) ok = 0;
416 strcat(boiled_headers, buf);
419 if (strlen(buf) == 0) done_headers = 1;
420 if (buf[0]=='\r') done_headers = 1;
421 if (buf[0]=='\n') done_headers = 1;
424 strcat(boiled_headers, "\r\n");
426 /* Now write it back */
428 fwrite(boiled_headers, strlen(boiled_headers), 1, fp);
430 ftruncate(fileno(fp), ftell(fp));
434 phree(boiled_headers);
439 * Implements the BODY and BODY.PEEK fetch items
441 void imap_fetch_body(long msgnum, char *item, int is_peek,
442 struct CtdlMessage *msg) {
449 long bytes_remaining = 0;
452 struct imap_fetch_part imfp;
454 /* extract section */
455 strcpy(section, item);
456 for (i=0; i<strlen(section); ++i) {
457 if (section[i]=='[') strcpy(section, §ion[i+1]);
459 for (i=0; i<strlen(section); ++i) {
460 if (section[i]==']') section[i] = 0;
462 lprintf(9, "Section is %s\n", section);
464 /* extract partial */
465 strcpy(partial, item);
466 for (i=0; i<strlen(partial); ++i) {
467 if (partial[i]=='<') {
468 strcpy(partial, &partial[i+1]);
472 for (i=0; i<strlen(partial); ++i) {
473 if (partial[i]=='>') partial[i] = 0;
475 if (is_partial == 0) strcpy(partial, "");
476 if (strlen(partial) > 0) lprintf(9, "Partial is %s\n", partial);
480 lprintf(1, "Cannot open temp file: %s\n", strerror(errno));
484 /* Now figure out what the client wants, and get it */
486 if ( (!strcmp(section, "1")) && (msg->cm_format_type != 4) ) {
487 CtdlRedirectOutput(tmp, -1);
488 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
490 CtdlRedirectOutput(NULL, -1);
493 else if (!strcmp(section, "")) {
494 CtdlRedirectOutput(tmp, -1);
495 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
497 CtdlRedirectOutput(NULL, -1);
501 * If the client asked for just headers, or just particular header
502 * fields, strip it down.
504 else if (!strncasecmp(section, "HEADER", 6)) {
505 CtdlRedirectOutput(tmp, -1);
506 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
508 CtdlRedirectOutput(NULL, -1);
509 imap_strip_headers(tmp, section);
513 * Strip it down if the client asked for everything _except_ headers.
515 else if (!strncasecmp(section, "TEXT", 4)) {
516 CtdlRedirectOutput(tmp, -1);
517 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
519 CtdlRedirectOutput(NULL, -1);
523 * Anything else must be a part specifier.
524 * (Note value of 1 passed as 'dont_decode' so client gets it encoded)
527 safestrncpy(imfp.desired_section, section,
528 sizeof(imfp.desired_section));
529 imfp.output_fp = tmp;
531 mime_parser(msg->cm_fields['M'], NULL,
532 *imap_load_part, NULL, NULL,
538 fseek(tmp, 0L, SEEK_END);
539 bytes_remaining = ftell(tmp);
541 if (is_partial == 0) {
543 cprintf("BODY[%s] {%ld}\r\n", section, bytes_remaining);
546 sscanf(partial, "%ld.%ld", &pstart, &pbytes);
547 if ((bytes_remaining - pstart) < pbytes) {
548 pbytes = bytes_remaining - pstart;
550 fseek(tmp, pstart, SEEK_SET);
551 bytes_remaining = pbytes;
552 cprintf("BODY[%s]<%ld> {%ld}\r\n",
553 section, pstart, bytes_remaining);
556 blocksize = sizeof(buf);
557 while (bytes_remaining > 0L) {
558 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
559 fread(buf, blocksize, 1, tmp);
560 client_write(buf, blocksize);
561 bytes_remaining = bytes_remaining - blocksize;
566 /* Mark this message as "seen" *unless* this is a "peek" operation */
568 CtdlSetSeen(msgnum, 1, ctdlsetseen_seen);
573 * Called immediately before outputting a multipart bodystructure
575 void imap_fetch_bodystructure_pre(
576 char *name, char *filename, char *partnum, char *disp,
577 void *content, char *cbtype, size_t length, char *encoding,
587 * Called immediately after outputting a multipart bodystructure
589 void imap_fetch_bodystructure_post(
590 char *name, char *filename, char *partnum, char *disp,
591 void *content, char *cbtype, size_t length, char *encoding,
600 extract_token(subtype, cbtype, 1, '/');
601 imap_strout(subtype);
612 * Output the info for a MIME part in the format required by BODYSTRUCTURE.
615 void imap_fetch_bodystructure_part(
616 char *name, char *filename, char *partnum, char *disp,
617 void *content, char *cbtype, size_t length, char *encoding,
622 int have_encoding = 0;
625 char cbmaintype[SIZ];
628 if (cbtype != NULL) if (strlen(cbtype)>0) have_cbtype = 1;
630 extract_token(cbmaintype, cbtype, 0, '/');
631 extract_token(cbsubtype, cbtype, 1, '/');
634 strcpy(cbmaintype, "TEXT");
635 strcpy(cbsubtype, "PLAIN");
639 imap_strout(cbmaintype);
641 imap_strout(cbsubtype);
644 cprintf("(\"CHARSET\" \"US-ASCII\"");
646 if (name != NULL) if (strlen(name)>0) {
647 cprintf(" \"NAME\" ");
653 cprintf("NIL "); /* Body ID */
654 cprintf("NIL "); /* Body description */
656 if (encoding != NULL) if (strlen(encoding) > 0) have_encoding = 1;
658 imap_strout(encoding);
665 /* The next field is the size of the part in bytes. */
666 cprintf("%ld ", (long)length); /* bytes */
668 /* The next field is the number of lines in the part, if and only
669 * if the part is TEXT. Crispin is a fscking idiot.
671 if (!strcasecmp(cbmaintype, "TEXT")) {
672 if (length) for (i=0; i<length; ++i) {
673 if (((char *)content)[i] == '\n') ++lines;
675 cprintf("%d ", lines);
678 /* More of Crispin being a fscking idiot */
679 if ((!strcasecmp(cbmaintype, "MESSAGE"))
680 && (!strcasecmp(cbsubtype, "RFC822"))) {
682 A body type of type MESSAGE and subtype RFC822
683 contains, immediately after the basic fields, the
684 envelope structure, body structure, and size in
685 text lines of the encapsulated message.
689 /* MD5 value of body part; we can get away with NIL'ing this */
696 else if (strlen(disp) == 0) {
702 if (filename != NULL) if (strlen(filename)>0) {
703 cprintf(" (\"FILENAME\" ");
704 imap_strout(filename);
710 /* Body language (not defined yet) */
717 * Spew the BODYSTRUCTURE data for a message. (Do you need a silencer if
718 * you're going to shoot a MIME? Do you need a reason to shoot Mark Crispin?
722 void imap_fetch_bodystructure (long msgnum, char *item,
723 struct CtdlMessage *msg) {
727 long start_of_body = 0L;
728 long body_bytes = 0L;
730 /* For non-RFC822 (ordinary Citadel) messages, this is short and
733 if (msg->cm_format_type != FMT_RFC822) {
735 /* *sigh* We have to RFC822-format the message just to be able
739 if (tmp == NULL) return;
740 CtdlRedirectOutput(tmp, -1);
741 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822, 0, 0, 1);
742 CtdlRedirectOutput(NULL, -1);
745 while (fgets(buf, sizeof buf, tmp) != NULL) {
747 if ((!strcmp(buf, "\r\n")) && (start_of_body == 0L)) {
748 start_of_body = ftell(tmp);
751 body_bytes = ftell(tmp) - start_of_body;
754 cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" "
755 "(\"CHARSET\" \"US-ASCII\") NIL NIL "
756 "\"7BIT\" %ld %ld)", body_bytes, lines);
761 /* For messages already stored in RFC822 format, we have to parse. */
762 cprintf("BODYSTRUCTURE ");
763 mime_parser(msg->cm_fields['M'],
765 *imap_fetch_bodystructure_part, /* part */
766 *imap_fetch_bodystructure_pre, /* pre-multi */
767 *imap_fetch_bodystructure_post, /* post-multi */
769 1); /* don't decode -- we want it as-is */
778 * imap_do_fetch() calls imap_do_fetch_msg() to output the data of an
779 * individual message, once it has been successfully loaded from disk.
781 void imap_do_fetch_msg(int seq, struct CtdlMessage *msg,
782 int num_items, char **itemlist) {
785 cprintf("* %d FETCH (", seq);
787 for (i=0; i<num_items; ++i) {
789 if (!strncasecmp(itemlist[i], "BODY[", 5)) {
790 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
793 else if (!strncasecmp(itemlist[i], "BODY.PEEK[", 10)) {
794 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
797 else if (!strcasecmp(itemlist[i], "BODYSTRUCTURE")) {
798 imap_fetch_bodystructure(IMAP->msgids[seq-1],
801 else if (!strcasecmp(itemlist[i], "ENVELOPE")) {
802 imap_fetch_envelope(IMAP->msgids[seq-1], msg);
804 else if (!strcasecmp(itemlist[i], "FLAGS")) {
805 imap_fetch_flags(seq-1);
807 else if (!strcasecmp(itemlist[i], "INTERNALDATE")) {
808 imap_fetch_internaldate(msg);
810 else if (!strcasecmp(itemlist[i], "RFC822")) {
811 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
813 else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
814 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
816 else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
817 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
819 else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
820 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
822 else if (!strcasecmp(itemlist[i], "UID")) {
826 if (i != num_items-1) cprintf(" ");
835 * imap_fetch() calls imap_do_fetch() to do its actual work, once it's
836 * validated and boiled down the request a bit.
838 void imap_do_fetch(int num_items, char **itemlist) {
840 struct CtdlMessage *msg;
842 if (IMAP->num_msgs > 0)
843 for (i = 0; i < IMAP->num_msgs; ++i)
844 if (IMAP->flags[i] & IMAP_SELECTED) {
845 msg = CtdlFetchMessage(IMAP->msgids[i]);
847 imap_do_fetch_msg(i+1, msg, num_items, itemlist);
848 CtdlFreeMessage(msg);
851 cprintf("* %d FETCH <internal error>\r\n", i+1);
859 * Back end for imap_handle_macros()
860 * Note that this function *only* looks at the beginning of the string. It
861 * is not a generic search-and-replace function.
863 void imap_macro_replace(char *str, char *find, char *replace) {
866 if (!strncasecmp(str, find, strlen(find))) {
867 if (str[strlen(find)]==' ') {
868 strcpy(holdbuf, &str[strlen(find)+1]);
869 strcpy(str, replace);
871 strcat(str, holdbuf);
873 if (str[strlen(find)]==0) {
874 strcpy(holdbuf, &str[strlen(find)+1]);
875 strcpy(str, replace);
883 * Handle macros embedded in FETCH data items.
884 * (What the heck are macros doing in a wire protocol? Are we trying to save
885 * the computer at the other end the trouble of typing a lot of characters?)
887 void imap_handle_macros(char *str) {
891 for (i=0; i<strlen(str); ++i) {
892 if (str[i]=='(') ++nest;
893 if (str[i]=='[') ++nest;
894 if (str[i]=='<') ++nest;
895 if (str[i]=='{') ++nest;
896 if (str[i]==')') --nest;
897 if (str[i]==']') --nest;
898 if (str[i]=='>') --nest;
899 if (str[i]=='}') --nest;
902 imap_macro_replace(&str[i],
904 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"
906 imap_macro_replace(&str[i],
910 imap_macro_replace(&str[i],
912 "FLAGS INTERNALDATE RFC822.SIZE"
914 imap_macro_replace(&str[i],
916 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"
924 * Break out the data items requested, possibly a parenthesized list.
925 * Returns the number of data items, or -1 if the list is invalid.
926 * NOTE: this function alters the string it is fed, and uses it as a buffer
927 * to hold the data for the pointers it returns.
929 int imap_extract_data_items(char **argv, char *items) {
935 /* Convert all whitespace to ordinary space characters. */
936 for (i=0; i<strlen(items); ++i) {
937 if (isspace(items[i])) items[i]=' ';
940 /* Strip leading and trailing whitespace, then strip leading and
941 * trailing parentheses if it's a list
944 if ( (items[0]=='(') && (items[strlen(items)-1]==')') ) {
945 items[strlen(items)-1] = 0;
946 strcpy(items, &items[1]);
950 /* Parse any macro data items */
951 imap_handle_macros(items);
954 * Now break out the data items. We throw in one trailing space in
955 * order to avoid having to break out the last one manually.
959 initial_len = strlen(items);
960 for (i=0; i<initial_len; ++i) {
961 if (items[i]=='(') ++nest;
962 if (items[i]=='[') ++nest;
963 if (items[i]=='<') ++nest;
964 if (items[i]=='{') ++nest;
965 if (items[i]==')') --nest;
966 if (items[i]==']') --nest;
967 if (items[i]=='>') --nest;
968 if (items[i]=='}') --nest;
970 if (nest <= 0) if (items[i]==' ') {
972 argv[num_items++] = start;
983 * One particularly hideous aspect of IMAP is that we have to allow the client
984 * to specify arbitrary ranges and/or sets of messages to fetch. Citadel IMAP
985 * handles this by setting the IMAP_SELECTED flag for each message specified in
986 * the ranges/sets, then looping through the message array, outputting messages
987 * with the flag set. We don't bother returning an error if an out-of-range
988 * number is specified (we just return quietly) because any client braindead
989 * enough to request a bogus message number isn't going to notice the
992 * This function clears out the IMAP_SELECTED bits, then sets that bit for each
993 * message included in the specified range.
995 * Set is_uid to 1 to fetch by UID instead of sequence number.
997 void imap_pick_range(char *supplied_range, int is_uid) {
1001 char setstr[SIZ], lostr[SIZ], histr[SIZ]; /* was 1024 */
1003 char actual_range[SIZ];
1006 * Handle the "ALL" macro
1008 if (!strcasecmp(supplied_range, "ALL")) {
1009 safestrncpy(actual_range, "1:*", sizeof actual_range);
1012 safestrncpy(actual_range, supplied_range, sizeof actual_range);
1016 * Clear out the IMAP_SELECTED flags for all messages.
1018 for (i = 0; i < IMAP->num_msgs; ++i) {
1019 IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SELECTED;
1023 * Now set it for all specified messages.
1025 num_sets = num_tokens(actual_range, ',');
1026 for (s=0; s<num_sets; ++s) {
1027 extract_token(setstr, actual_range, s, ',');
1029 extract_token(lostr, setstr, 0, ':');
1030 if (num_tokens(setstr, ':') >= 2) {
1031 extract_token(histr, setstr, 1, ':');
1032 if (!strcmp(histr, "*")) snprintf(histr, sizeof histr, "%d", INT_MAX);
1035 strcpy(histr, lostr);
1040 /* Loop through the array, flipping bits where appropriate */
1041 for (i = 1; i <= IMAP->num_msgs; ++i) {
1042 if (is_uid) { /* fetch by sequence number */
1043 if ( (IMAP->msgids[i-1]>=lo)
1044 && (IMAP->msgids[i-1]<=hi)) {
1046 IMAP->flags[i-1] | IMAP_SELECTED;
1049 else { /* fetch by uid */
1050 if ( (i>=lo) && (i<=hi)) {
1052 IMAP->flags[i-1] | IMAP_SELECTED;
1063 * This function is called by the main command loop.
1065 void imap_fetch(int num_parms, char *parms[]) {
1066 char items[SIZ]; /* was 1024 */
1067 char *itemlist[SIZ];
1071 if (num_parms < 4) {
1072 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1076 imap_pick_range(parms[2], 0);
1079 for (i=3; i<num_parms; ++i) {
1080 strcat(items, parms[i]);
1081 if (i < (num_parms-1)) strcat(items, " ");
1084 num_items = imap_extract_data_items(itemlist, items);
1085 if (num_items < 1) {
1086 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1090 imap_do_fetch(num_items, itemlist);
1091 cprintf("%s OK FETCH completed\r\n", parms[0]);
1095 * This function is called by the main command loop.
1097 void imap_uidfetch(int num_parms, char *parms[]) {
1098 char items[SIZ]; /* was 1024 */
1099 char *itemlist[SIZ];
1102 int have_uid_item = 0;
1104 if (num_parms < 5) {
1105 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1109 imap_pick_range(parms[3], 1);
1112 for (i=4; i<num_parms; ++i) {
1113 strcat(items, parms[i]);
1114 if (i < (num_parms-1)) strcat(items, " ");
1117 num_items = imap_extract_data_items(itemlist, items);
1118 if (num_items < 1) {
1119 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1123 /* If the "UID" item was not included, we include it implicitly
1124 * because this is a UID FETCH command
1126 for (i=0; i<num_items; ++i) {
1127 if (!strcasecmp(itemlist[i], "UID")) ++have_uid_item;
1129 if (have_uid_item == 0) itemlist[num_items++] = "UID";
1131 imap_do_fetch(num_items, itemlist);
1132 cprintf("%s OK UID FETCH completed\r\n", parms[0]);