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 /* Now write it back */
426 fwrite(boiled_headers, strlen(boiled_headers), 1, fp);
428 ftruncate(fileno(fp), ftell(fp));
432 phree(boiled_headers);
437 * Implements the BODY and BODY.PEEK fetch items
439 void imap_fetch_body(long msgnum, char *item, int is_peek,
440 struct CtdlMessage *msg) {
447 long bytes_remaining = 0;
450 struct imap_fetch_part imfp;
452 /* extract section */
453 strcpy(section, item);
454 for (i=0; i<strlen(section); ++i) {
455 if (section[i]=='[') strcpy(section, §ion[i+1]);
457 for (i=0; i<strlen(section); ++i) {
458 if (section[i]==']') section[i] = 0;
460 lprintf(9, "Section is %s\n", section);
462 /* extract partial */
463 strcpy(partial, item);
464 for (i=0; i<strlen(partial); ++i) {
465 if (partial[i]=='<') {
466 strcpy(partial, &partial[i+1]);
470 for (i=0; i<strlen(partial); ++i) {
471 if (partial[i]=='>') partial[i] = 0;
473 if (is_partial == 0) strcpy(partial, "");
474 if (strlen(partial) > 0) lprintf(9, "Partial is %s\n", partial);
478 lprintf(1, "Cannot open temp file: %s\n", strerror(errno));
482 /* Now figure out what the client wants, and get it */
484 if ( (!strcmp(section, "1")) && (msg->cm_format_type != 4) ) {
485 CtdlRedirectOutput(tmp, -1);
486 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
488 CtdlRedirectOutput(NULL, -1);
491 else if (!strcmp(section, "")) {
492 CtdlRedirectOutput(tmp, -1);
493 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
495 CtdlRedirectOutput(NULL, -1);
499 * If the client asked for just headers, or just particular header
500 * fields, strip it down.
502 else if (!strncasecmp(section, "HEADER", 6)) {
503 CtdlRedirectOutput(tmp, -1);
504 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
506 CtdlRedirectOutput(NULL, -1);
507 imap_strip_headers(tmp, section);
511 * Strip it down if the client asked for everything _except_ headers.
513 else if (!strncasecmp(section, "TEXT", 4)) {
514 CtdlRedirectOutput(tmp, -1);
515 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
517 CtdlRedirectOutput(NULL, -1);
521 * Anything else must be a part specifier.
522 * (Note value of 1 passed as 'dont_decode' so client gets it encoded)
525 safestrncpy(imfp.desired_section, section,
526 sizeof(imfp.desired_section));
527 imfp.output_fp = tmp;
529 mime_parser(msg->cm_fields['M'], NULL,
530 *imap_load_part, NULL, NULL,
536 fseek(tmp, 0L, SEEK_END);
537 bytes_remaining = ftell(tmp);
539 if (is_partial == 0) {
541 cprintf("BODY[%s] {%ld}\r\n", section, bytes_remaining);
544 sscanf(partial, "%ld.%ld", &pstart, &pbytes);
545 if ((bytes_remaining - pstart) < pbytes) {
546 pbytes = bytes_remaining - pstart;
548 fseek(tmp, pstart, SEEK_SET);
549 bytes_remaining = pbytes;
550 cprintf("BODY[%s]<%ld> {%ld}\r\n",
551 section, pstart, bytes_remaining);
554 blocksize = sizeof(buf);
555 while (bytes_remaining > 0L) {
556 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
557 fread(buf, blocksize, 1, tmp);
558 client_write(buf, blocksize);
559 bytes_remaining = bytes_remaining - blocksize;
564 /* Mark this message as "seen" *unless* this is a "peek" operation */
566 CtdlSetSeen(msgnum, 1, ctdlsetseen_seen);
571 * Called immediately before outputting a multipart bodystructure
573 void imap_fetch_bodystructure_pre(
574 char *name, char *filename, char *partnum, char *disp,
575 void *content, char *cbtype, size_t length, char *encoding,
585 * Called immediately after outputting a multipart bodystructure
587 void imap_fetch_bodystructure_post(
588 char *name, char *filename, char *partnum, char *disp,
589 void *content, char *cbtype, size_t length, char *encoding,
598 extract_token(subtype, cbtype, 1, '/');
599 imap_strout(subtype);
610 * Output the info for a MIME part in the format required by BODYSTRUCTURE.
613 void imap_fetch_bodystructure_part(
614 char *name, char *filename, char *partnum, char *disp,
615 void *content, char *cbtype, size_t length, char *encoding,
620 int have_encoding = 0;
623 char cbmaintype[SIZ];
626 if (cbtype != NULL) if (strlen(cbtype)>0) have_cbtype = 1;
628 extract_token(cbmaintype, cbtype, 0, '/');
629 extract_token(cbsubtype, cbtype, 1, '/');
632 strcpy(cbmaintype, "TEXT");
633 strcpy(cbsubtype, "PLAIN");
637 imap_strout(cbmaintype);
639 imap_strout(cbsubtype);
642 cprintf("(\"CHARSET\" \"US-ASCII\"");
644 if (name != NULL) if (strlen(name)>0) {
645 cprintf(" \"NAME\" ");
651 cprintf("NIL "); /* Body ID */
652 cprintf("NIL "); /* Body description */
654 if (encoding != NULL) if (strlen(encoding) > 0) have_encoding = 1;
656 imap_strout(encoding);
663 /* The next field is the size of the part in bytes. */
664 cprintf("%ld ", (long)length); /* bytes */
666 /* The next field is the number of lines in the part, if and only
667 * if the part is TEXT. Crispin is a fscking idiot.
669 if (!strcasecmp(cbmaintype, "TEXT")) {
670 if (length) for (i=0; i<length; ++i) {
671 if (((char *)content)[i] == '\n') ++lines;
673 cprintf("%d ", lines);
676 /* More of Crispin being a fscking idiot */
677 if ((!strcasecmp(cbmaintype, "MESSAGE"))
678 && (!strcasecmp(cbsubtype, "RFC822"))) {
680 A body type of type MESSAGE and subtype RFC822
681 contains, immediately after the basic fields, the
682 envelope structure, body structure, and size in
683 text lines of the encapsulated message.
687 /* MD5 value of body part; we can get away with NIL'ing this */
694 else if (strlen(disp) == 0) {
700 if (filename != NULL) if (strlen(filename)>0) {
701 cprintf(" (\"FILENAME\" ");
702 imap_strout(filename);
708 /* Body language (not defined yet) */
715 * Spew the BODYSTRUCTURE data for a message. (Do you need a silencer if
716 * you're going to shoot a MIME? Do you need a reason to shoot Mark Crispin?
720 void imap_fetch_bodystructure (long msgnum, char *item,
721 struct CtdlMessage *msg) {
725 long start_of_body = 0L;
726 long body_bytes = 0L;
728 /* For non-RFC822 (ordinary Citadel) messages, this is short and
731 if (msg->cm_format_type != FMT_RFC822) {
733 /* *sigh* We have to RFC822-format the message just to be able
737 if (tmp == NULL) return;
738 CtdlRedirectOutput(tmp, -1);
739 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822, 0, 0, 1);
740 CtdlRedirectOutput(NULL, -1);
743 while (fgets(buf, sizeof buf, tmp) != NULL) {
745 if ((!strcmp(buf, "\r\n")) && (start_of_body == 0L)) {
746 start_of_body = ftell(tmp);
749 body_bytes = ftell(tmp) - start_of_body;
752 cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" "
753 "(\"CHARSET\" \"US-ASCII\") NIL NIL "
754 "\"7BIT\" %ld %ld)", body_bytes, lines);
759 /* For messages already stored in RFC822 format, we have to parse. */
760 cprintf("BODYSTRUCTURE ");
761 mime_parser(msg->cm_fields['M'],
763 *imap_fetch_bodystructure_part, /* part */
764 *imap_fetch_bodystructure_pre, /* pre-multi */
765 *imap_fetch_bodystructure_post, /* post-multi */
767 1); /* don't decode -- we want it as-is */
776 * imap_do_fetch() calls imap_do_fetch_msg() to output the data of an
777 * individual message, once it has been successfully loaded from disk.
779 void imap_do_fetch_msg(int seq, struct CtdlMessage *msg,
780 int num_items, char **itemlist) {
783 cprintf("* %d FETCH (", seq);
785 for (i=0; i<num_items; ++i) {
787 if (!strncasecmp(itemlist[i], "BODY[", 5)) {
788 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
791 else if (!strncasecmp(itemlist[i], "BODY.PEEK[", 10)) {
792 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
795 else if (!strcasecmp(itemlist[i], "BODYSTRUCTURE")) {
796 imap_fetch_bodystructure(IMAP->msgids[seq-1],
799 else if (!strcasecmp(itemlist[i], "ENVELOPE")) {
800 imap_fetch_envelope(IMAP->msgids[seq-1], msg);
802 else if (!strcasecmp(itemlist[i], "FLAGS")) {
803 imap_fetch_flags(seq-1);
805 else if (!strcasecmp(itemlist[i], "INTERNALDATE")) {
806 imap_fetch_internaldate(msg);
808 else if (!strcasecmp(itemlist[i], "RFC822")) {
809 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
811 else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
812 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
814 else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
815 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
817 else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
818 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
820 else if (!strcasecmp(itemlist[i], "UID")) {
824 if (i != num_items-1) cprintf(" ");
833 * imap_fetch() calls imap_do_fetch() to do its actual work, once it's
834 * validated and boiled down the request a bit.
836 void imap_do_fetch(int num_items, char **itemlist) {
838 struct CtdlMessage *msg;
840 if (IMAP->num_msgs > 0)
841 for (i = 0; i < IMAP->num_msgs; ++i)
842 if (IMAP->flags[i] & IMAP_SELECTED) {
843 msg = CtdlFetchMessage(IMAP->msgids[i]);
845 imap_do_fetch_msg(i+1, msg, num_items, itemlist);
846 CtdlFreeMessage(msg);
849 cprintf("* %d FETCH <internal error>\r\n", i+1);
857 * Back end for imap_handle_macros()
858 * Note that this function *only* looks at the beginning of the string. It
859 * is not a generic search-and-replace function.
861 void imap_macro_replace(char *str, char *find, char *replace) {
864 if (!strncasecmp(str, find, strlen(find))) {
865 if (str[strlen(find)]==' ') {
866 strcpy(holdbuf, &str[strlen(find)+1]);
867 strcpy(str, replace);
869 strcat(str, holdbuf);
871 if (str[strlen(find)]==0) {
872 strcpy(holdbuf, &str[strlen(find)+1]);
873 strcpy(str, replace);
881 * Handle macros embedded in FETCH data items.
882 * (What the heck are macros doing in a wire protocol? Are we trying to save
883 * the computer at the other end the trouble of typing a lot of characters?)
885 void imap_handle_macros(char *str) {
889 for (i=0; i<strlen(str); ++i) {
890 if (str[i]=='(') ++nest;
891 if (str[i]=='[') ++nest;
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;
900 imap_macro_replace(&str[i],
902 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"
904 imap_macro_replace(&str[i],
908 imap_macro_replace(&str[i],
910 "FLAGS INTERNALDATE RFC822.SIZE"
912 imap_macro_replace(&str[i],
914 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"
922 * Break out the data items requested, possibly a parenthesized list.
923 * Returns the number of data items, or -1 if the list is invalid.
924 * NOTE: this function alters the string it is fed, and uses it as a buffer
925 * to hold the data for the pointers it returns.
927 int imap_extract_data_items(char **argv, char *items) {
933 /* Convert all whitespace to ordinary space characters. */
934 for (i=0; i<strlen(items); ++i) {
935 if (isspace(items[i])) items[i]=' ';
938 /* Strip leading and trailing whitespace, then strip leading and
939 * trailing parentheses if it's a list
942 if ( (items[0]=='(') && (items[strlen(items)-1]==')') ) {
943 items[strlen(items)-1] = 0;
944 strcpy(items, &items[1]);
948 /* Parse any macro data items */
949 imap_handle_macros(items);
952 * Now break out the data items. We throw in one trailing space in
953 * order to avoid having to break out the last one manually.
957 initial_len = strlen(items);
958 for (i=0; i<initial_len; ++i) {
959 if (items[i]=='(') ++nest;
960 if (items[i]=='[') ++nest;
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;
968 if (nest <= 0) if (items[i]==' ') {
970 argv[num_items++] = start;
981 * One particularly hideous aspect of IMAP is that we have to allow the client
982 * to specify arbitrary ranges and/or sets of messages to fetch. Citadel IMAP
983 * handles this by setting the IMAP_SELECTED flag for each message specified in
984 * the ranges/sets, then looping through the message array, outputting messages
985 * with the flag set. We don't bother returning an error if an out-of-range
986 * number is specified (we just return quietly) because any client braindead
987 * enough to request a bogus message number isn't going to notice the
990 * This function clears out the IMAP_SELECTED bits, then sets that bit for each
991 * message included in the specified range.
993 * Set is_uid to 1 to fetch by UID instead of sequence number.
995 void imap_pick_range(char *supplied_range, int is_uid) {
999 char setstr[SIZ], lostr[SIZ], histr[SIZ]; /* was 1024 */
1001 char actual_range[SIZ];
1004 * Handle the "ALL" macro
1006 if (!strcasecmp(supplied_range, "ALL")) {
1007 safestrncpy(actual_range, "1:*", sizeof actual_range);
1010 safestrncpy(actual_range, supplied_range, sizeof actual_range);
1014 * Clear out the IMAP_SELECTED flags for all messages.
1016 for (i = 0; i < IMAP->num_msgs; ++i) {
1017 IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SELECTED;
1021 * Now set it for all specified messages.
1023 num_sets = num_tokens(actual_range, ',');
1024 for (s=0; s<num_sets; ++s) {
1025 extract_token(setstr, actual_range, s, ',');
1027 extract_token(lostr, setstr, 0, ':');
1028 if (num_tokens(setstr, ':') >= 2) {
1029 extract_token(histr, setstr, 1, ':');
1030 if (!strcmp(histr, "*")) snprintf(histr, sizeof histr, "%d", INT_MAX);
1033 strcpy(histr, lostr);
1038 /* Loop through the array, flipping bits where appropriate */
1039 for (i = 1; i <= IMAP->num_msgs; ++i) {
1040 if (is_uid) { /* fetch by sequence number */
1041 if ( (IMAP->msgids[i-1]>=lo)
1042 && (IMAP->msgids[i-1]<=hi)) {
1044 IMAP->flags[i-1] | IMAP_SELECTED;
1047 else { /* fetch by uid */
1048 if ( (i>=lo) && (i<=hi)) {
1050 IMAP->flags[i-1] | IMAP_SELECTED;
1061 * This function is called by the main command loop.
1063 void imap_fetch(int num_parms, char *parms[]) {
1064 char items[SIZ]; /* was 1024 */
1065 char *itemlist[SIZ];
1069 if (num_parms < 4) {
1070 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1074 imap_pick_range(parms[2], 0);
1077 for (i=3; i<num_parms; ++i) {
1078 strcat(items, parms[i]);
1079 if (i < (num_parms-1)) strcat(items, " ");
1082 num_items = imap_extract_data_items(itemlist, items);
1083 if (num_items < 1) {
1084 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1088 imap_do_fetch(num_items, itemlist);
1089 cprintf("%s OK FETCH completed\r\n", parms[0]);
1093 * This function is called by the main command loop.
1095 void imap_uidfetch(int num_parms, char *parms[]) {
1096 char items[SIZ]; /* was 1024 */
1097 char *itemlist[SIZ];
1100 int have_uid_item = 0;
1102 if (num_parms < 5) {
1103 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1107 imap_pick_range(parms[3], 1);
1110 for (i=4; i<num_parms; ++i) {
1111 strcat(items, parms[i]);
1112 if (i < (num_parms-1)) strcat(items, " ");
1115 num_items = imap_extract_data_items(itemlist, items);
1116 if (num_items < 1) {
1117 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1121 /* If the "UID" item was not included, we include it implicitly
1122 * because this is a UID FETCH command
1124 for (i=0; i<num_items; ++i) {
1125 if (!strcasecmp(itemlist[i], "UID")) ++have_uid_item;
1127 if (have_uid_item == 0) itemlist[num_items++] = "UID";
1129 imap_do_fetch(num_items, itemlist);
1130 cprintf("%s OK UID FETCH completed\r\n", parms[0]);