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 deta 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")) {
776 imap_fetch_bodystructure(IMAP->msgids[seq-1],
779 else if (!strcasecmp(itemlist[i], "ENVELOPE")) {
780 imap_fetch_envelope(IMAP->msgids[seq-1], msg);
782 else if (!strcasecmp(itemlist[i], "FLAGS")) {
783 imap_fetch_flags(seq-1);
785 else if (!strcasecmp(itemlist[i], "INTERNALDATE")) {
786 imap_fetch_internaldate(msg);
788 else if (!strcasecmp(itemlist[i], "RFC822")) {
789 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
791 else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
792 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
794 else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
795 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
797 else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
798 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
800 else if (!strcasecmp(itemlist[i], "UID")) {
804 if (i != num_items-1) cprintf(" ");
813 * imap_fetch() calls imap_do_fetch() to do its actual work, once it's
814 * validated and boiled down the request a bit.
816 void imap_do_fetch(int num_items, char **itemlist) {
818 struct CtdlMessage *msg;
820 if (IMAP->num_msgs > 0)
821 for (i = 0; i < IMAP->num_msgs; ++i)
822 if (IMAP->flags[i] & IMAP_SELECTED) {
823 msg = CtdlFetchMessage(IMAP->msgids[i]);
825 imap_do_fetch_msg(i+1, msg, num_items, itemlist);
826 CtdlFreeMessage(msg);
829 cprintf("* %d FETCH <internal error>\r\n", i+1);
837 * Back end for imap_handle_macros()
838 * Note that this function *only* looks at the beginning of the string. It
839 * is not a generic search-and-replace function.
841 void imap_macro_replace(char *str, char *find, char *replace) {
844 if (!strncasecmp(str, find, strlen(find))) {
845 if (str[strlen(find)]==' ') {
846 strcpy(holdbuf, &str[strlen(find)+1]);
847 strcpy(str, replace);
849 strcat(str, holdbuf);
851 if (str[strlen(find)]==0) {
852 strcpy(holdbuf, &str[strlen(find)+1]);
853 strcpy(str, replace);
861 * Handle macros embedded in FETCH data items.
862 * (What the heck are macros doing in a wire protocol? Are we trying to save
863 * the computer at the other end the trouble of typing a lot of characters?)
865 void imap_handle_macros(char *str) {
869 for (i=0; i<strlen(str); ++i) {
870 if (str[i]=='(') ++nest;
871 if (str[i]=='[') ++nest;
872 if (str[i]=='<') ++nest;
873 if (str[i]=='{') ++nest;
874 if (str[i]==')') --nest;
875 if (str[i]==']') --nest;
876 if (str[i]=='>') --nest;
877 if (str[i]=='}') --nest;
880 imap_macro_replace(&str[i],
882 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"
884 imap_macro_replace(&str[i],
888 imap_macro_replace(&str[i],
890 "FLAGS INTERNALDATE RFC822.SIZE"
892 imap_macro_replace(&str[i],
894 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"
902 * Break out the data items requested, possibly a parenthesized list.
903 * Returns the number of data items, or -1 if the list is invalid.
904 * NOTE: this function alters the string it is fed, and uses it as a buffer
905 * to hold the data for the pointers it returns.
907 int imap_extract_data_items(char **argv, char *items) {
913 /* Convert all whitespace to ordinary space characters. */
914 for (i=0; i<strlen(items); ++i) {
915 if (isspace(items[i])) items[i]=' ';
918 /* Strip leading and trailing whitespace, then strip leading and
919 * trailing parentheses if it's a list
922 if ( (items[0]=='(') && (items[strlen(items)-1]==')') ) {
923 items[strlen(items)-1] = 0;
924 strcpy(items, &items[1]);
928 /* Parse any macro data items */
929 imap_handle_macros(items);
932 * Now break out the data items. We throw in one trailing space in
933 * order to avoid having to break out the last one manually.
937 initial_len = strlen(items);
938 for (i=0; i<initial_len; ++i) {
939 if (items[i]=='(') ++nest;
940 if (items[i]=='[') ++nest;
941 if (items[i]=='<') ++nest;
942 if (items[i]=='{') ++nest;
943 if (items[i]==')') --nest;
944 if (items[i]==']') --nest;
945 if (items[i]=='>') --nest;
946 if (items[i]=='}') --nest;
948 if (nest <= 0) if (items[i]==' ') {
950 argv[num_items++] = start;
961 * One particularly hideous aspect of IMAP is that we have to allow the client
962 * to specify arbitrary ranges and/or sets of messages to fetch. Citadel IMAP
963 * handles this by setting the IMAP_SELECTED flag for each message specified in
964 * the ranges/sets, then looping through the message array, outputting messages
965 * with the flag set. We don't bother returning an error if an out-of-range
966 * number is specified (we just return quietly) because any client braindead
967 * enough to request a bogus message number isn't going to notice the
970 * This function clears out the IMAP_SELECTED bits, then sets that bit for each
971 * message included in the specified range.
973 * Set is_uid to 1 to fetch by UID instead of sequence number.
975 void imap_pick_range(char *supplied_range, int is_uid) {
979 char setstr[SIZ], lostr[SIZ], histr[SIZ]; /* was 1024 */
981 char actual_range[SIZ];
984 * Handle the "ALL" macro
986 if (!strcasecmp(supplied_range, "ALL")) {
987 safestrncpy(actual_range, "1:*", sizeof actual_range);
990 safestrncpy(actual_range, supplied_range, sizeof actual_range);
994 * Clear out the IMAP_SELECTED flags for all messages.
996 for (i = 0; i < IMAP->num_msgs; ++i) {
997 IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SELECTED;
1001 * Now set it for all specified messages.
1003 num_sets = num_tokens(actual_range, ',');
1004 for (s=0; s<num_sets; ++s) {
1005 extract_token(setstr, actual_range, s, ',');
1007 extract_token(lostr, setstr, 0, ':');
1008 if (num_tokens(setstr, ':') >= 2) {
1009 extract_token(histr, setstr, 1, ':');
1010 if (!strcmp(histr, "*")) snprintf(histr, sizeof histr, "%d", INT_MAX);
1013 strcpy(histr, lostr);
1018 /* Loop through the array, flipping bits where appropriate */
1019 for (i = 1; i <= IMAP->num_msgs; ++i) {
1020 if (is_uid) { /* fetch by sequence number */
1021 if ( (IMAP->msgids[i-1]>=lo)
1022 && (IMAP->msgids[i-1]<=hi)) {
1024 IMAP->flags[i-1] | IMAP_SELECTED;
1027 else { /* fetch by uid */
1028 if ( (i>=lo) && (i<=hi)) {
1030 IMAP->flags[i-1] | IMAP_SELECTED;
1041 * This function is called by the main command loop.
1043 void imap_fetch(int num_parms, char *parms[]) {
1044 char items[SIZ]; /* was 1024 */
1045 char *itemlist[SIZ];
1049 if (num_parms < 4) {
1050 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1054 imap_pick_range(parms[2], 0);
1057 for (i=3; i<num_parms; ++i) {
1058 strcat(items, parms[i]);
1059 if (i < (num_parms-1)) strcat(items, " ");
1062 num_items = imap_extract_data_items(itemlist, items);
1063 if (num_items < 1) {
1064 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1068 imap_do_fetch(num_items, itemlist);
1069 cprintf("%s OK FETCH completed\r\n", parms[0]);
1073 * This function is called by the main command loop.
1075 void imap_uidfetch(int num_parms, char *parms[]) {
1076 char items[SIZ]; /* was 1024 */
1077 char *itemlist[SIZ];
1080 int have_uid_item = 0;
1082 if (num_parms < 5) {
1083 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1087 imap_pick_range(parms[3], 1);
1090 for (i=4; i<num_parms; ++i) {
1091 strcat(items, parms[i]);
1092 if (i < (num_parms-1)) strcat(items, " ");
1095 num_items = imap_extract_data_items(itemlist, items);
1096 if (num_items < 1) {
1097 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1101 /* If the "UID" item was not included, we include it implicitly
1102 * because this is a UID FETCH command
1104 for (i=0; i<num_items; ++i) {
1105 if (!strcasecmp(itemlist[i], "UID")) ++have_uid_item;
1107 if (have_uid_item == 0) itemlist[num_items++] = "UID";
1109 imap_do_fetch(num_items, itemlist);
1110 cprintf("%s OK UID FETCH completed\r\n", parms[0]);