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) {
74 if (IMAP->flags[seq] & IMAP_DELETED) cprintf("\\Deleted ");
75 if (IMAP->flags[seq] & IMAP_SEEN) cprintf("\\Seen ");
79 void imap_fetch_internaldate(struct CtdlMessage *msg) {
83 if (msg->cm_fields['T'] != NULL) {
84 msgdate = atol(msg->cm_fields['T']);
90 datestring(buf, sizeof buf, msgdate, DATESTRING_IMAP);
91 cprintf("INTERNALDATE \"%s\"", buf);
96 * Fetch RFC822-formatted messages.
98 * 'whichfmt' should be set to one of:
99 * "RFC822" entire message
100 * "RFC822.HEADER" headers only (with trailing blank line)
101 * "RFC822.SIZE" size of translated message
102 * "RFC822.TEXT" body only (without leading blank line)
104 void imap_fetch_rfc822(int msgnum, char *whichfmt, struct CtdlMessage *msg) {
108 long headers_size, text_size, total_size;
109 long bytes_remaining = 0;
114 lprintf(1, "Cannot open temp file: %s\n", strerror(errno));
119 * Load the message into a temp file for translation and measurement
121 CtdlRedirectOutput(tmp, -1);
122 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822, HEADERS_ALL, 0, 1);
123 CtdlRedirectOutput(NULL, -1);
124 if (!is_valid_message(msg)) {
125 lprintf(1, "WARNING: output clobbered the message!\n");
129 * Now figure out where the headers/text break is. IMAP considers the
130 * intervening blank line to be part of the headers, not the text.
135 ptr = fgets(buf, sizeof buf, tmp);
138 if (strlen(buf) == 0) headers_size = ftell(tmp);
140 } while ( (headers_size == 0L) && (ptr != NULL) );
141 fseek(tmp, 0L, SEEK_END);
142 total_size = ftell(tmp);
143 text_size = total_size - headers_size;
145 if (!strcasecmp(whichfmt, "RFC822.SIZE")) {
146 cprintf("RFC822.SIZE %ld", total_size);
151 else if (!strcasecmp(whichfmt, "RFC822")) {
152 bytes_remaining = total_size;
156 else if (!strcasecmp(whichfmt, "RFC822.HEADER")) {
157 bytes_remaining = headers_size;
161 else if (!strcasecmp(whichfmt, "RFC822.TEXT")) {
162 bytes_remaining = text_size;
163 fseek(tmp, headers_size, SEEK_SET);
166 cprintf("%s {%ld}\r\n", whichfmt, bytes_remaining);
167 blocksize = sizeof(buf);
168 while (bytes_remaining > 0L) {
169 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
170 fread(buf, blocksize, 1, tmp);
171 client_write(buf, blocksize);
172 bytes_remaining = bytes_remaining - blocksize;
181 * Load a specific part of a message into the temp file to be output to a
182 * client. FIXME we can handle parts like "2" and "2.1" and even "2.MIME"
183 * but we still can't handle "2.HEADER" (which might not be a problem, because
184 * we currently don't have the ability to break out nested RFC822's anyway).
186 * Note: mime_parser() was called with dont_decode set to 1, so we have the
187 * luxury of simply spewing without having to re-encode.
189 void imap_load_part(char *name, char *filename, char *partnum, char *disp,
190 void *content, char *cbtype, size_t length, char *encoding,
193 struct imap_fetch_part *imfp;
196 imfp = (struct imap_fetch_part *)cbuserdata;
198 if (!strcasecmp(partnum, imfp->desired_section)) {
199 fwrite(content, length, 1, imfp->output_fp);
202 snprintf(mbuf2, sizeof mbuf2, "%s.MIME", partnum);
204 if (!strcasecmp(imfp->desired_section, mbuf2)) {
205 fprintf(imfp->output_fp, "Content-type: %s", cbtype);
206 if (strlen(name) > 0)
207 fprintf(imfp->output_fp, "; name=\"%s\"", name);
208 fprintf(imfp->output_fp, "\r\n");
209 if (strlen(encoding) > 0)
210 fprintf(imfp->output_fp,
211 "Content-Transfer-Encoding: %s\r\n", encoding);
212 if (strlen(encoding) > 0) {
213 fprintf(imfp->output_fp, "Content-Disposition: %s",
215 if (strlen(filename) > 0) {
216 fprintf(imfp->output_fp, "; filename=\"%s\"",
219 fprintf(imfp->output_fp, "\r\n");
221 fprintf(imfp->output_fp, "Content-Length: %ld\r\n", (long)length);
222 fprintf(imfp->output_fp, "\r\n");
230 * Called by imap_fetch_envelope() to output the "From" field.
231 * This is in its own function because its logic is kind of complex. We
232 * really need to make this suck less.
234 void imap_output_envelope_from(struct CtdlMessage *msg) {
235 char user[1024], node[1024], name[1024];
237 cprintf("(("); /* open double-parens */
238 imap_strout(msg->cm_fields['A']); /* personal name */
239 cprintf(" NIL "); /* source route (not used) */
241 if (msg->cm_fields['F'] != NULL) {
242 process_rfc822_addr(msg->cm_fields['F'], user, node, name);
243 imap_strout(user); /* mailbox name (user id) */
245 if (!strcasecmp(node, config.c_nodename)) {
246 imap_strout(config.c_fqdn);
249 imap_strout(node); /* host name */
253 imap_strout(msg->cm_fields['A']); /* mailbox name (user id) */
255 imap_strout(msg->cm_fields['N']); /* host name */
258 cprintf(")) "); /* close double-parens */
263 * Implements the ENVELOPE fetch item
265 * FIXME ... we only output some of the fields right now. Definitely need
266 * to do all of them. Accurately, too.
268 * Note that the imap_strout() function can cleverly output NULL fields as NIL,
269 * so we don't have to check for that condition like we do elsewhere.
271 void imap_fetch_envelope(long msgnum, struct CtdlMessage *msg) {
272 char datestringbuf[SIZ];
274 char *fieldptr = NULL;
276 /* Parse the message date into an IMAP-format date string */
277 if (msg->cm_fields['T'] != NULL) {
278 msgdate = atol(msg->cm_fields['T']);
281 msgdate = time(NULL);
283 datestring(datestringbuf, sizeof datestringbuf, msgdate, DATESTRING_IMAP);
285 /* Now start spewing data fields. The order is important, as it is
286 * defined by the protocol specification. Nonexistent fields must
287 * be output as NIL, existent fields must be quoted or literalled.
288 * The imap_strout() function conveniently does all this for us.
290 cprintf("ENVELOPE (");
293 imap_strout(datestringbuf);
297 imap_strout(msg->cm_fields['U']);
301 imap_output_envelope_from(msg);
305 /* FIXME ... check for a *real* Sender: field */
308 imap_output_envelope_from(msg);
313 /* FIXME ... check for a *real* Reply-to: field */
316 imap_output_envelope_from(msg);
319 cprintf("NIL "); /* to */
321 cprintf("NIL "); /* cc */
323 cprintf("NIL "); /* bcc */
326 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "In-reply-to");
327 imap_strout(fieldptr);
329 if (fieldptr != NULL) phree(fieldptr);
332 imap_strout(msg->cm_fields['I']);
339 * Strip any non header information out of a chunk of RFC822 data on disk,
340 * then boil it down to just the fields we want.
342 void imap_strip_headers(FILE *fp, char *section) {
344 char *which_fields = NULL;
345 int doing_headers = 0;
350 char *boiled_headers = NULL;
352 int done_headers = 0;
354 which_fields = strdoop(section);
356 if (!strncasecmp(which_fields, "HEADER.FIELDS", 13))
358 if (!strncasecmp(which_fields, "HEADER.FIELDS.NOT", 17))
361 for (i=0; i<strlen(which_fields); ++i) {
362 if (which_fields[i]=='(')
363 strcpy(which_fields, &which_fields[i+1]);
365 for (i=0; i<strlen(which_fields); ++i) {
366 if (which_fields[i]==')')
369 num_parms = imap_parameterize(parms, which_fields);
371 fseek(fp, 0L, SEEK_END);
372 boiled_headers = mallok((size_t)(ftell(fp) + 256L));
373 strcpy(boiled_headers, "");
377 while ( (done_headers == 0) && (fgets(buf, sizeof buf, fp) != NULL) ) {
378 if (!isspace(buf[0])) {
380 if (doing_headers == 0) ok = 1;
382 if (headers_not) ok = 1;
384 for (i=0; i<num_parms; ++i) {
385 if ( (!strncasecmp(buf, parms[i],
386 strlen(parms[i]))) &&
387 (buf[strlen(parms[i])]==':') ) {
388 if (headers_not) ok = 0;
396 strcat(boiled_headers, buf);
399 if (strlen(buf) == 0) done_headers = 1;
400 if (buf[0]=='\r') done_headers = 1;
401 if (buf[0]=='\n') done_headers = 1;
404 /* Now write it back */
406 fwrite(boiled_headers, strlen(boiled_headers), 1, fp);
408 ftruncate(fileno(fp), ftell(fp));
412 phree(boiled_headers);
417 * Implements the BODY and BODY.PEEK fetch items
419 void imap_fetch_body(long msgnum, char *item, int is_peek,
420 struct CtdlMessage *msg) {
427 long bytes_remaining = 0;
430 struct imap_fetch_part imfp;
432 /* extract section */
433 strcpy(section, item);
434 for (i=0; i<strlen(section); ++i) {
435 if (section[i]=='[') strcpy(section, §ion[i+1]);
437 for (i=0; i<strlen(section); ++i) {
438 if (section[i]==']') section[i] = 0;
440 lprintf(9, "Section is %s\n", section);
442 /* extract partial */
443 strcpy(partial, item);
444 for (i=0; i<strlen(partial); ++i) {
445 if (partial[i]=='<') {
446 strcpy(partial, &partial[i+1]);
450 for (i=0; i<strlen(partial); ++i) {
451 if (partial[i]=='>') partial[i] = 0;
453 if (is_partial == 0) strcpy(partial, "");
454 if (strlen(partial) > 0) lprintf(9, "Partial is %s\n", partial);
458 lprintf(1, "Cannot open temp file: %s\n", strerror(errno));
462 /* Now figure out what the client wants, and get it */
464 if ( (!strcmp(section, "1")) && (msg->cm_format_type != 4) ) {
465 CtdlRedirectOutput(tmp, -1);
466 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
468 CtdlRedirectOutput(NULL, -1);
471 else if (!strcmp(section, "")) {
472 CtdlRedirectOutput(tmp, -1);
473 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
475 CtdlRedirectOutput(NULL, -1);
479 * If the client asked for just headers, or just particular header
480 * fields, strip it down.
482 else if (!strncasecmp(section, "HEADER", 6)) {
483 CtdlRedirectOutput(tmp, -1);
484 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
486 CtdlRedirectOutput(NULL, -1);
487 imap_strip_headers(tmp, section);
491 * Strip it down if the client asked for everything _except_ headers.
493 else if (!strncasecmp(section, "TEXT", 4)) {
494 CtdlRedirectOutput(tmp, -1);
495 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
497 CtdlRedirectOutput(NULL, -1);
501 * Anything else must be a part specifier.
502 * (Note value of 1 passed as 'dont_decode' so client gets it encoded)
505 safestrncpy(imfp.desired_section, section,
506 sizeof(imfp.desired_section));
507 imfp.output_fp = tmp;
509 mime_parser(msg->cm_fields['M'], NULL,
510 *imap_load_part, NULL, NULL,
516 fseek(tmp, 0L, SEEK_END);
517 bytes_remaining = ftell(tmp);
519 if (is_partial == 0) {
521 cprintf("BODY[%s] {%ld}\r\n", section, bytes_remaining);
524 sscanf(partial, "%ld.%ld", &pstart, &pbytes);
525 if ((bytes_remaining - pstart) < pbytes) {
526 pbytes = bytes_remaining - pstart;
528 fseek(tmp, pstart, SEEK_SET);
529 bytes_remaining = pbytes;
530 cprintf("BODY[%s] {%ld}<%ld>\r\n",
531 section, bytes_remaining, pstart);
534 blocksize = sizeof(buf);
535 while (bytes_remaining > 0L) {
536 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
537 fread(buf, blocksize, 1, tmp);
538 client_write(buf, blocksize);
539 bytes_remaining = bytes_remaining - blocksize;
544 /* Mark this message as "seen" *unless* this is a "peek" operation */
546 CtdlSetSeen(msgnum, 1);
551 * Called immediately before outputting a multipart bodystructure
553 void imap_fetch_bodystructure_pre(
554 char *name, char *filename, char *partnum, char *disp,
555 void *content, char *cbtype, size_t length, char *encoding,
565 * Called immediately after outputting a multipart bodystructure
567 void imap_fetch_bodystructure_post(
568 char *name, char *filename, char *partnum, char *disp,
569 void *content, char *cbtype, size_t length, char *encoding,
578 extract_token(subtype, cbtype, 1, '/');
579 imap_strout(subtype);
590 * Output the info for a MIME part in the format required by BODYSTRUCTURE.
593 void imap_fetch_bodystructure_part(
594 char *name, char *filename, char *partnum, char *disp,
595 void *content, char *cbtype, size_t length, char *encoding,
600 int have_encoding = 0;
603 char cbmaintype[SIZ];
606 if (cbtype != NULL) if (strlen(cbtype)>0) have_cbtype = 1;
608 extract_token(cbmaintype, cbtype, 0, '/');
609 extract_token(cbsubtype, cbtype, 1, '/');
612 strcpy(cbmaintype, "TEXT");
613 strcpy(cbsubtype, "PLAIN");
617 imap_strout(cbmaintype);
619 imap_strout(cbsubtype);
622 cprintf("(\"CHARSET\" \"US-ASCII\"");
624 if (name != NULL) if (strlen(name)>0) {
625 cprintf(" \"NAME\" ");
631 cprintf("NIL "); /* Body ID */
632 cprintf("NIL "); /* Body description */
634 if (encoding != NULL) if (strlen(encoding) > 0) have_encoding = 1;
636 imap_strout(encoding);
643 /* The next field is the size of the part in bytes. */
644 cprintf("%ld ", (long)length); /* bytes */
646 /* The next field is the number of lines in the part, if and only
647 * if the part is TEXT. Crispin is a fscking idiot.
649 if (!strcasecmp(cbmaintype, "TEXT")) {
650 if (length) for (i=0; i<length; ++i) {
651 if (((char *)content)[i] == '\n') ++lines;
653 cprintf("%d ", lines);
656 /* More of Crispin being a fscking idiot */
657 if ((!strcasecmp(cbmaintype, "MESSAGE"))
658 && (!strcasecmp(cbsubtype, "RFC822"))) {
660 A body type of type MESSAGE and subtype RFC822
661 contains, immediately after the basic fields, the
662 envelope structure, body structure, and size in
663 text lines of the encapsulated message.
667 /* MD5 value of body part; we can get away with NIL'ing this */
674 else if (strlen(disp) == 0) {
680 if (filename != NULL) if (strlen(filename)>0) {
681 cprintf(" (\"FILENAME\" ");
682 imap_strout(filename);
688 /* Body language (not defined yet) */
695 * Spew the BODYSTRUCTURE data for a message. (Do you need a silencer if
696 * you're going to shoot a MIME? Do you need a reason to shoot Mark Crispin?
700 void imap_fetch_bodystructure (long msgnum, char *item,
701 struct CtdlMessage *msg) {
705 long start_of_body = 0L;
706 long body_bytes = 0L;
708 /* For non-RFC822 (ordinary Citadel) messages, this is short and
711 if (msg->cm_format_type != FMT_RFC822) {
713 /* *sigh* We have to RFC822-format the message just to be able
717 if (tmp == NULL) return;
718 CtdlRedirectOutput(tmp, -1);
719 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822, 0, 0, 1);
720 CtdlRedirectOutput(NULL, -1);
723 while (fgets(buf, sizeof buf, tmp) != NULL) {
725 if ((!strcmp(buf, "\r\n")) && (start_of_body == 0L)) {
726 start_of_body = ftell(tmp);
729 body_bytes = ftell(tmp) - start_of_body;
732 cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" "
733 "(\"CHARSET\" \"US-ASCII\") NIL NIL "
734 "\"7BIT\" %ld %ld)", body_bytes, lines);
739 /* For messages already stored in RFC822 format, we have to parse. */
740 cprintf("BODYSTRUCTURE ");
741 mime_parser(msg->cm_fields['M'],
743 *imap_fetch_bodystructure_part, /* part */
744 *imap_fetch_bodystructure_pre, /* pre-multi */
745 *imap_fetch_bodystructure_post, /* post-multi */
747 1); /* don't decode -- we want it as-is */
756 * imap_do_fetch() calls imap_do_fetch_msg() to output the data of an
757 * individual message, once it has been successfully loaded from disk.
759 void imap_do_fetch_msg(int seq, struct CtdlMessage *msg,
760 int num_items, char **itemlist) {
763 cprintf("* %d FETCH (", seq);
765 for (i=0; i<num_items; ++i) {
767 if (!strncasecmp(itemlist[i], "BODY[", 5)) {
768 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
771 else if (!strncasecmp(itemlist[i], "BODY.PEEK[", 10)) {
772 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
775 else if (!strcasecmp(itemlist[i], "BODYSTRUCTURE")) {
777 imap_fetch_bodystructure(IMAP->msgids[seq-1],
781 else if (!strcasecmp(itemlist[i], "ENVELOPE")) {
783 imap_fetch_envelope(IMAP->msgids[seq-1], msg);
786 else if (!strcasecmp(itemlist[i], "FLAGS")) {
788 imap_fetch_flags(seq-1);
791 else if (!strcasecmp(itemlist[i], "INTERNALDATE")) {
792 imap_fetch_internaldate(msg);
794 else if (!strcasecmp(itemlist[i], "RFC822")) {
795 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
797 else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
798 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
800 else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
801 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
803 else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
804 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
806 else if (!strcasecmp(itemlist[i], "UID")) {
810 if (i != num_items-1) cprintf(" ");
819 * imap_fetch() calls imap_do_fetch() to do its actual work, once it's
820 * validated and boiled down the request a bit.
822 void imap_do_fetch(int num_items, char **itemlist) {
824 struct CtdlMessage *msg;
826 if (IMAP->num_msgs > 0)
827 for (i = 0; i < IMAP->num_msgs; ++i)
828 if (IMAP->flags[i] & IMAP_SELECTED) {
829 msg = CtdlFetchMessage(IMAP->msgids[i]);
831 imap_do_fetch_msg(i+1, msg, num_items, itemlist);
832 CtdlFreeMessage(msg);
835 cprintf("* %d FETCH <internal error>\r\n", i+1);
843 * Back end for imap_handle_macros()
844 * Note that this function *only* looks at the beginning of the string. It
845 * is not a generic search-and-replace function.
847 void imap_macro_replace(char *str, char *find, char *replace) {
850 if (!strncasecmp(str, find, strlen(find))) {
851 if (str[strlen(find)]==' ') {
852 strcpy(holdbuf, &str[strlen(find)+1]);
853 strcpy(str, replace);
855 strcat(str, holdbuf);
857 if (str[strlen(find)]==0) {
858 strcpy(holdbuf, &str[strlen(find)+1]);
859 strcpy(str, replace);
867 * Handle macros embedded in FETCH data items.
868 * (What the heck are macros doing in a wire protocol? Are we trying to save
869 * the computer at the other end the trouble of typing a lot of characters?)
871 void imap_handle_macros(char *str) {
875 for (i=0; i<strlen(str); ++i) {
876 if (str[i]=='(') ++nest;
877 if (str[i]=='[') ++nest;
878 if (str[i]=='<') ++nest;
879 if (str[i]=='{') ++nest;
880 if (str[i]==')') --nest;
881 if (str[i]==']') --nest;
882 if (str[i]=='>') --nest;
883 if (str[i]=='}') --nest;
886 imap_macro_replace(&str[i],
888 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"
890 imap_macro_replace(&str[i],
894 imap_macro_replace(&str[i],
896 "FLAGS INTERNALDATE RFC822.SIZE"
898 imap_macro_replace(&str[i],
900 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"
908 * Break out the data items requested, possibly a parenthesized list.
909 * Returns the number of data items, or -1 if the list is invalid.
910 * NOTE: this function alters the string it is fed, and uses it as a buffer
911 * to hold the data for the pointers it returns.
913 int imap_extract_data_items(char **argv, char *items) {
919 /* Convert all whitespace to ordinary space characters. */
920 for (i=0; i<strlen(items); ++i) {
921 if (isspace(items[i])) items[i]=' ';
924 /* Strip leading and trailing whitespace, then strip leading and
925 * trailing parentheses if it's a list
928 if ( (items[0]=='(') && (items[strlen(items)-1]==')') ) {
929 items[strlen(items)-1] = 0;
930 strcpy(items, &items[1]);
934 /* Parse any macro data items */
935 imap_handle_macros(items);
938 * Now break out the data items. We throw in one trailing space in
939 * order to avoid having to break out the last one manually.
943 initial_len = strlen(items);
944 for (i=0; i<initial_len; ++i) {
945 if (items[i]=='(') ++nest;
946 if (items[i]=='[') ++nest;
947 if (items[i]=='<') ++nest;
948 if (items[i]=='{') ++nest;
949 if (items[i]==')') --nest;
950 if (items[i]==']') --nest;
951 if (items[i]=='>') --nest;
952 if (items[i]=='}') --nest;
954 if (nest <= 0) if (items[i]==' ') {
956 argv[num_items++] = start;
967 * One particularly hideous aspect of IMAP is that we have to allow the client
968 * to specify arbitrary ranges and/or sets of messages to fetch. Citadel IMAP
969 * handles this by setting the IMAP_SELECTED flag for each message specified in
970 * the ranges/sets, then looping through the message array, outputting messages
971 * with the flag set. We don't bother returning an error if an out-of-range
972 * number is specified (we just return quietly) because any client braindead
973 * enough to request a bogus message number isn't going to notice the
976 * This function clears out the IMAP_SELECTED bits, then sets that bit for each
977 * message included in the specified range.
979 * Set is_uid to 1 to fetch by UID instead of sequence number.
981 void imap_pick_range(char *supplied_range, int is_uid) {
985 char setstr[SIZ], lostr[SIZ], histr[SIZ]; /* was 1024 */
987 char actual_range[SIZ];
990 * Handle the "ALL" macro
992 if (!strcasecmp(supplied_range, "ALL")) {
993 safestrncpy(actual_range, "1:*", sizeof actual_range);
996 safestrncpy(actual_range, supplied_range, sizeof actual_range);
1000 * Clear out the IMAP_SELECTED flags for all messages.
1002 for (i = 0; i < IMAP->num_msgs; ++i) {
1003 IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SELECTED;
1007 * Now set it for all specified messages.
1009 num_sets = num_tokens(actual_range, ',');
1010 for (s=0; s<num_sets; ++s) {
1011 extract_token(setstr, actual_range, s, ',');
1013 extract_token(lostr, setstr, 0, ':');
1014 if (num_tokens(setstr, ':') >= 2) {
1015 extract_token(histr, setstr, 1, ':');
1016 if (!strcmp(histr, "*")) snprintf(histr, sizeof histr, "%d", INT_MAX);
1019 strcpy(histr, lostr);
1024 /* Loop through the array, flipping bits where appropriate */
1025 for (i = 1; i <= IMAP->num_msgs; ++i) {
1026 if (is_uid) { /* fetch by sequence number */
1027 if ( (IMAP->msgids[i-1]>=lo)
1028 && (IMAP->msgids[i-1]<=hi)) {
1030 IMAP->flags[i-1] | IMAP_SELECTED;
1033 else { /* fetch by uid */
1034 if ( (i>=lo) && (i<=hi)) {
1036 IMAP->flags[i-1] | IMAP_SELECTED;
1047 * This function is called by the main command loop.
1049 void imap_fetch(int num_parms, char *parms[]) {
1050 char items[SIZ]; /* was 1024 */
1051 char *itemlist[SIZ];
1055 if (num_parms < 4) {
1056 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1060 imap_pick_range(parms[2], 0);
1063 for (i=3; i<num_parms; ++i) {
1064 strcat(items, parms[i]);
1065 if (i < (num_parms-1)) strcat(items, " ");
1068 num_items = imap_extract_data_items(itemlist, items);
1069 if (num_items < 1) {
1070 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1074 imap_do_fetch(num_items, itemlist);
1075 cprintf("%s OK FETCH completed\r\n", parms[0]);
1079 * This function is called by the main command loop.
1081 void imap_uidfetch(int num_parms, char *parms[]) {
1082 char items[SIZ]; /* was 1024 */
1083 char *itemlist[SIZ];
1086 int have_uid_item = 0;
1088 if (num_parms < 5) {
1089 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1093 imap_pick_range(parms[3], 1);
1096 for (i=4; i<num_parms; ++i) {
1097 strcat(items, parms[i]);
1098 if (i < (num_parms-1)) strcat(items, " ");
1101 num_items = imap_extract_data_items(itemlist, items);
1102 if (num_items < 1) {
1103 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1107 /* If the "UID" item was not included, we include it implicitly
1108 * because this is a UID FETCH command
1110 for (i=0; i<num_items; ++i) {
1111 if (!strcasecmp(itemlist[i], "UID")) ++have_uid_item;
1113 if (have_uid_item == 0) itemlist[num_items++] = "UID";
1115 imap_do_fetch(num_items, itemlist);
1116 cprintf("%s OK UID FETCH completed\r\n", parms[0]);