4 * Implements the FETCH command in IMAP.
5 * This command is way too convoluted. Marc Crispin is a fscking idiot.
18 #include <sys/types.h>
20 #if TIME_WITH_SYS_TIME
21 # include <sys/time.h>
25 # include <sys/time.h>
37 #include "sysdep_decls.h"
38 #include "citserver.h"
41 #include "serv_extensions.h"
48 #include "internet_addressing.h"
49 #include "mime_parser.h"
50 #include "serv_imap.h"
51 #include "imap_tools.h"
52 #include "imap_fetch.h"
57 struct imap_fetch_part {
58 char desired_section[SIZ];
63 * Individual field functions for imap_do_fetch_msg() ...
68 void imap_fetch_uid(int seq) {
69 cprintf("UID %ld", IMAP->msgids[seq-1]);
72 void imap_fetch_flags(int seq) {
73 int num_flags_printed = 0;
75 if (IMAP->flags[seq] & IMAP_DELETED) {
76 if (num_flags_printed > 0) cprintf(" ");
80 if (IMAP->flags[seq] & IMAP_SEEN) {
81 if (num_flags_printed > 0) cprintf(" ");
88 void imap_fetch_internaldate(struct CtdlMessage *msg) {
92 if (msg->cm_fields['T'] != NULL) {
93 msgdate = atol(msg->cm_fields['T']);
99 datestring(buf, sizeof buf, msgdate, DATESTRING_IMAP);
100 cprintf("INTERNALDATE \"%s\"", buf);
105 * Fetch RFC822-formatted messages.
107 * 'whichfmt' should be set to one of:
108 * "RFC822" entire message
109 * "RFC822.HEADER" headers only (with trailing blank line)
110 * "RFC822.SIZE" size of translated message
111 * "RFC822.TEXT" body only (without leading blank line)
113 void imap_fetch_rfc822(int msgnum, char *whichfmt, struct CtdlMessage *msg) {
117 long headers_size, text_size, total_size;
118 long bytes_remaining = 0;
123 lprintf(1, "Cannot open temp file: %s\n", strerror(errno));
128 * Load the message into a temp file for translation and measurement
130 CtdlRedirectOutput(tmp, -1);
131 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822, HEADERS_ALL, 0, 1);
132 CtdlRedirectOutput(NULL, -1);
133 if (!is_valid_message(msg)) {
134 lprintf(1, "WARNING: output clobbered the message!\n");
138 * Now figure out where the headers/text break is. IMAP considers the
139 * intervening blank line to be part of the headers, not the text.
144 ptr = fgets(buf, sizeof buf, tmp);
147 if (strlen(buf) == 0) headers_size = ftell(tmp);
149 } while ( (headers_size == 0L) && (ptr != NULL) );
150 fseek(tmp, 0L, SEEK_END);
151 total_size = ftell(tmp);
152 text_size = total_size - headers_size;
154 if (!strcasecmp(whichfmt, "RFC822.SIZE")) {
155 cprintf("RFC822.SIZE %ld", total_size);
160 else if (!strcasecmp(whichfmt, "RFC822")) {
161 bytes_remaining = total_size;
165 else if (!strcasecmp(whichfmt, "RFC822.HEADER")) {
166 bytes_remaining = headers_size;
170 else if (!strcasecmp(whichfmt, "RFC822.TEXT")) {
171 bytes_remaining = text_size;
172 fseek(tmp, headers_size, SEEK_SET);
175 cprintf("%s {%ld}\r\n", whichfmt, bytes_remaining);
176 blocksize = sizeof(buf);
177 while (bytes_remaining > 0L) {
178 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
179 fread(buf, blocksize, 1, tmp);
180 client_write(buf, blocksize);
181 bytes_remaining = bytes_remaining - blocksize;
190 * Load a specific part of a message into the temp file to be output to a
191 * client. FIXME we can handle parts like "2" and "2.1" and even "2.MIME"
192 * but we still can't handle "2.HEADER" (which might not be a problem, because
193 * we currently don't have the ability to break out nested RFC822's anyway).
195 * Note: mime_parser() was called with dont_decode set to 1, so we have the
196 * luxury of simply spewing without having to re-encode.
198 void imap_load_part(char *name, char *filename, char *partnum, char *disp,
199 void *content, char *cbtype, size_t length, char *encoding,
202 struct imap_fetch_part *imfp;
205 imfp = (struct imap_fetch_part *)cbuserdata;
207 if (!strcasecmp(partnum, imfp->desired_section)) {
208 fwrite(content, length, 1, imfp->output_fp);
211 snprintf(mbuf2, sizeof mbuf2, "%s.MIME", partnum);
213 if (!strcasecmp(imfp->desired_section, mbuf2)) {
214 fprintf(imfp->output_fp, "Content-type: %s", cbtype);
215 if (strlen(name) > 0)
216 fprintf(imfp->output_fp, "; name=\"%s\"", name);
217 fprintf(imfp->output_fp, "\r\n");
218 if (strlen(encoding) > 0)
219 fprintf(imfp->output_fp,
220 "Content-Transfer-Encoding: %s\r\n", encoding);
221 if (strlen(encoding) > 0) {
222 fprintf(imfp->output_fp, "Content-Disposition: %s",
224 if (strlen(filename) > 0) {
225 fprintf(imfp->output_fp, "; filename=\"%s\"",
228 fprintf(imfp->output_fp, "\r\n");
230 fprintf(imfp->output_fp, "Content-Length: %ld\r\n", (long)length);
231 fprintf(imfp->output_fp, "\r\n");
239 * Called by imap_fetch_envelope() to output the "From" field.
240 * This is in its own function because its logic is kind of complex. We
241 * really need to make this suck less.
243 void imap_output_envelope_from(struct CtdlMessage *msg) {
244 char user[1024], node[1024], name[1024];
246 cprintf("(("); /* open double-parens */
247 imap_strout(msg->cm_fields['A']); /* personal name */
248 cprintf(" NIL "); /* source route (not used) */
250 if (msg->cm_fields['F'] != NULL) {
251 process_rfc822_addr(msg->cm_fields['F'], user, node, name);
252 imap_strout(user); /* mailbox name (user id) */
254 if (!strcasecmp(node, config.c_nodename)) {
255 imap_strout(config.c_fqdn);
258 imap_strout(node); /* host name */
262 imap_strout(msg->cm_fields['A']); /* mailbox name (user id) */
264 imap_strout(msg->cm_fields['N']); /* host name */
267 cprintf(")) "); /* close double-parens */
272 * Implements the ENVELOPE fetch item
274 * FIXME ... we only output some of the fields right now. Definitely need
275 * to do all of them. Accurately, too.
277 * Note that the imap_strout() function can cleverly output NULL fields as NIL,
278 * so we don't have to check for that condition like we do elsewhere.
280 void imap_fetch_envelope(long msgnum, struct CtdlMessage *msg) {
281 char datestringbuf[SIZ];
283 char *fieldptr = NULL;
285 /* Parse the message date into an IMAP-format date string */
286 if (msg->cm_fields['T'] != NULL) {
287 msgdate = atol(msg->cm_fields['T']);
290 msgdate = time(NULL);
292 datestring(datestringbuf, sizeof datestringbuf, msgdate, DATESTRING_IMAP);
294 /* Now start spewing data fields. The order is important, as it is
295 * defined by the protocol specification. Nonexistent fields must
296 * be output as NIL, existent fields must be quoted or literalled.
297 * The imap_strout() function conveniently does all this for us.
299 cprintf("ENVELOPE (");
302 imap_strout(datestringbuf);
306 imap_strout(msg->cm_fields['U']);
310 imap_output_envelope_from(msg);
314 /* FIXME ... check for a *real* Sender: field */
317 imap_output_envelope_from(msg);
322 /* FIXME ... check for a *real* Reply-to: field */
325 imap_output_envelope_from(msg);
328 cprintf("NIL "); /* to */
330 cprintf("NIL "); /* cc */
332 cprintf("NIL "); /* bcc */
335 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "In-reply-to");
336 imap_strout(fieldptr);
338 if (fieldptr != NULL) phree(fieldptr);
341 imap_strout(msg->cm_fields['I']);
348 * Strip any non header information out of a chunk of RFC822 data on disk,
349 * then boil it down to just the fields we want.
351 void imap_strip_headers(FILE *fp, char *section) {
353 char *which_fields = NULL;
354 int doing_headers = 0;
359 char *boiled_headers = NULL;
361 int done_headers = 0;
363 which_fields = strdoop(section);
365 if (!strncasecmp(which_fields, "HEADER.FIELDS", 13))
367 if (!strncasecmp(which_fields, "HEADER.FIELDS.NOT", 17))
370 for (i=0; i<strlen(which_fields); ++i) {
371 if (which_fields[i]=='(')
372 strcpy(which_fields, &which_fields[i+1]);
374 for (i=0; i<strlen(which_fields); ++i) {
375 if (which_fields[i]==')')
378 num_parms = imap_parameterize(parms, which_fields);
380 fseek(fp, 0L, SEEK_END);
381 boiled_headers = mallok((size_t)(ftell(fp) + 256L));
382 strcpy(boiled_headers, "");
386 while ( (done_headers == 0) && (fgets(buf, sizeof buf, fp) != NULL) ) {
387 if (!isspace(buf[0])) {
389 if (doing_headers == 0) ok = 1;
391 if (headers_not) ok = 1;
393 for (i=0; i<num_parms; ++i) {
394 if ( (!strncasecmp(buf, parms[i],
395 strlen(parms[i]))) &&
396 (buf[strlen(parms[i])]==':') ) {
397 if (headers_not) ok = 0;
405 strcat(boiled_headers, buf);
408 if (strlen(buf) == 0) done_headers = 1;
409 if (buf[0]=='\r') done_headers = 1;
410 if (buf[0]=='\n') done_headers = 1;
413 /* Now write it back */
415 fwrite(boiled_headers, strlen(boiled_headers), 1, fp);
417 ftruncate(fileno(fp), ftell(fp));
421 phree(boiled_headers);
426 * Implements the BODY and BODY.PEEK fetch items
428 void imap_fetch_body(long msgnum, char *item, int is_peek,
429 struct CtdlMessage *msg) {
436 long bytes_remaining = 0;
439 struct imap_fetch_part imfp;
441 /* extract section */
442 strcpy(section, item);
443 for (i=0; i<strlen(section); ++i) {
444 if (section[i]=='[') strcpy(section, §ion[i+1]);
446 for (i=0; i<strlen(section); ++i) {
447 if (section[i]==']') section[i] = 0;
449 lprintf(9, "Section is %s\n", section);
451 /* extract partial */
452 strcpy(partial, item);
453 for (i=0; i<strlen(partial); ++i) {
454 if (partial[i]=='<') {
455 strcpy(partial, &partial[i+1]);
459 for (i=0; i<strlen(partial); ++i) {
460 if (partial[i]=='>') partial[i] = 0;
462 if (is_partial == 0) strcpy(partial, "");
463 if (strlen(partial) > 0) lprintf(9, "Partial is %s\n", partial);
467 lprintf(1, "Cannot open temp file: %s\n", strerror(errno));
471 /* Now figure out what the client wants, and get it */
473 if ( (!strcmp(section, "1")) && (msg->cm_format_type != 4) ) {
474 CtdlRedirectOutput(tmp, -1);
475 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
477 CtdlRedirectOutput(NULL, -1);
480 else if (!strcmp(section, "")) {
481 CtdlRedirectOutput(tmp, -1);
482 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
484 CtdlRedirectOutput(NULL, -1);
488 * If the client asked for just headers, or just particular header
489 * fields, strip it down.
491 else if (!strncasecmp(section, "HEADER", 6)) {
492 CtdlRedirectOutput(tmp, -1);
493 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
495 CtdlRedirectOutput(NULL, -1);
496 imap_strip_headers(tmp, section);
500 * Strip it down if the client asked for everything _except_ headers.
502 else if (!strncasecmp(section, "TEXT", 4)) {
503 CtdlRedirectOutput(tmp, -1);
504 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
506 CtdlRedirectOutput(NULL, -1);
510 * Anything else must be a part specifier.
511 * (Note value of 1 passed as 'dont_decode' so client gets it encoded)
514 safestrncpy(imfp.desired_section, section,
515 sizeof(imfp.desired_section));
516 imfp.output_fp = tmp;
518 mime_parser(msg->cm_fields['M'], NULL,
519 *imap_load_part, NULL, NULL,
525 fseek(tmp, 0L, SEEK_END);
526 bytes_remaining = ftell(tmp);
528 if (is_partial == 0) {
530 cprintf("BODY[%s] {%ld}\r\n", section, bytes_remaining);
533 sscanf(partial, "%ld.%ld", &pstart, &pbytes);
534 if ((bytes_remaining - pstart) < pbytes) {
535 pbytes = bytes_remaining - pstart;
537 fseek(tmp, pstart, SEEK_SET);
538 bytes_remaining = pbytes;
539 cprintf("BODY[%s]<%ld> {%ld}\r\n",
540 section, pstart, bytes_remaining);
543 blocksize = sizeof(buf);
544 while (bytes_remaining > 0L) {
545 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
546 fread(buf, blocksize, 1, tmp);
547 client_write(buf, blocksize);
548 bytes_remaining = bytes_remaining - blocksize;
553 /* Mark this message as "seen" *unless* this is a "peek" operation */
555 CtdlSetSeen(msgnum, 1);
560 * Called immediately before outputting a multipart bodystructure
562 void imap_fetch_bodystructure_pre(
563 char *name, char *filename, char *partnum, char *disp,
564 void *content, char *cbtype, size_t length, char *encoding,
574 * Called immediately after outputting a multipart bodystructure
576 void imap_fetch_bodystructure_post(
577 char *name, char *filename, char *partnum, char *disp,
578 void *content, char *cbtype, size_t length, char *encoding,
587 extract_token(subtype, cbtype, 1, '/');
588 imap_strout(subtype);
599 * Output the info for a MIME part in the format required by BODYSTRUCTURE.
602 void imap_fetch_bodystructure_part(
603 char *name, char *filename, char *partnum, char *disp,
604 void *content, char *cbtype, size_t length, char *encoding,
609 int have_encoding = 0;
612 char cbmaintype[SIZ];
615 if (cbtype != NULL) if (strlen(cbtype)>0) have_cbtype = 1;
617 extract_token(cbmaintype, cbtype, 0, '/');
618 extract_token(cbsubtype, cbtype, 1, '/');
621 strcpy(cbmaintype, "TEXT");
622 strcpy(cbsubtype, "PLAIN");
626 imap_strout(cbmaintype);
628 imap_strout(cbsubtype);
631 cprintf("(\"CHARSET\" \"US-ASCII\"");
633 if (name != NULL) if (strlen(name)>0) {
634 cprintf(" \"NAME\" ");
640 cprintf("NIL "); /* Body ID */
641 cprintf("NIL "); /* Body description */
643 if (encoding != NULL) if (strlen(encoding) > 0) have_encoding = 1;
645 imap_strout(encoding);
652 /* The next field is the size of the part in bytes. */
653 cprintf("%ld ", (long)length); /* bytes */
655 /* The next field is the number of lines in the part, if and only
656 * if the part is TEXT. Crispin is a fscking idiot.
658 if (!strcasecmp(cbmaintype, "TEXT")) {
659 if (length) for (i=0; i<length; ++i) {
660 if (((char *)content)[i] == '\n') ++lines;
662 cprintf("%d ", lines);
665 /* More of Crispin being a fscking idiot */
666 if ((!strcasecmp(cbmaintype, "MESSAGE"))
667 && (!strcasecmp(cbsubtype, "RFC822"))) {
669 A body type of type MESSAGE and subtype RFC822
670 contains, immediately after the basic fields, the
671 envelope structure, body structure, and size in
672 text lines of the encapsulated message.
676 /* MD5 value of body part; we can get away with NIL'ing this */
683 else if (strlen(disp) == 0) {
689 if (filename != NULL) if (strlen(filename)>0) {
690 cprintf(" (\"FILENAME\" ");
691 imap_strout(filename);
697 /* Body language (not defined yet) */
704 * Spew the BODYSTRUCTURE data for a message. (Do you need a silencer if
705 * you're going to shoot a MIME? Do you need a reason to shoot Mark Crispin?
709 void imap_fetch_bodystructure (long msgnum, char *item,
710 struct CtdlMessage *msg) {
714 long start_of_body = 0L;
715 long body_bytes = 0L;
717 /* For non-RFC822 (ordinary Citadel) messages, this is short and
720 if (msg->cm_format_type != FMT_RFC822) {
722 /* *sigh* We have to RFC822-format the message just to be able
726 if (tmp == NULL) return;
727 CtdlRedirectOutput(tmp, -1);
728 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822, 0, 0, 1);
729 CtdlRedirectOutput(NULL, -1);
732 while (fgets(buf, sizeof buf, tmp) != NULL) {
734 if ((!strcmp(buf, "\r\n")) && (start_of_body == 0L)) {
735 start_of_body = ftell(tmp);
738 body_bytes = ftell(tmp) - start_of_body;
741 cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" "
742 "(\"CHARSET\" \"US-ASCII\") NIL NIL "
743 "\"7BIT\" %ld %ld)", body_bytes, lines);
748 /* For messages already stored in RFC822 format, we have to parse. */
749 cprintf("BODYSTRUCTURE ");
750 mime_parser(msg->cm_fields['M'],
752 *imap_fetch_bodystructure_part, /* part */
753 *imap_fetch_bodystructure_pre, /* pre-multi */
754 *imap_fetch_bodystructure_post, /* post-multi */
756 1); /* don't decode -- we want it as-is */
765 * imap_do_fetch() calls imap_do_fetch_msg() to output the data of an
766 * individual message, once it has been successfully loaded from disk.
768 void imap_do_fetch_msg(int seq, struct CtdlMessage *msg,
769 int num_items, char **itemlist) {
772 cprintf("* %d FETCH (", seq);
774 for (i=0; i<num_items; ++i) {
776 if (!strncasecmp(itemlist[i], "BODY[", 5)) {
777 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
780 else if (!strncasecmp(itemlist[i], "BODY.PEEK[", 10)) {
781 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
784 else if (!strcasecmp(itemlist[i], "BODYSTRUCTURE")) {
785 imap_fetch_bodystructure(IMAP->msgids[seq-1],
788 else if (!strcasecmp(itemlist[i], "ENVELOPE")) {
789 imap_fetch_envelope(IMAP->msgids[seq-1], msg);
791 else if (!strcasecmp(itemlist[i], "FLAGS")) {
792 imap_fetch_flags(seq-1);
794 else if (!strcasecmp(itemlist[i], "INTERNALDATE")) {
795 imap_fetch_internaldate(msg);
797 else if (!strcasecmp(itemlist[i], "RFC822")) {
798 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
800 else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
801 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
803 else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
804 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
806 else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
807 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
809 else if (!strcasecmp(itemlist[i], "UID")) {
813 if (i != num_items-1) cprintf(" ");
822 * imap_fetch() calls imap_do_fetch() to do its actual work, once it's
823 * validated and boiled down the request a bit.
825 void imap_do_fetch(int num_items, char **itemlist) {
827 struct CtdlMessage *msg;
829 if (IMAP->num_msgs > 0)
830 for (i = 0; i < IMAP->num_msgs; ++i)
831 if (IMAP->flags[i] & IMAP_SELECTED) {
832 msg = CtdlFetchMessage(IMAP->msgids[i]);
834 imap_do_fetch_msg(i+1, msg, num_items, itemlist);
835 CtdlFreeMessage(msg);
838 cprintf("* %d FETCH <internal error>\r\n", i+1);
846 * Back end for imap_handle_macros()
847 * Note that this function *only* looks at the beginning of the string. It
848 * is not a generic search-and-replace function.
850 void imap_macro_replace(char *str, char *find, char *replace) {
853 if (!strncasecmp(str, find, strlen(find))) {
854 if (str[strlen(find)]==' ') {
855 strcpy(holdbuf, &str[strlen(find)+1]);
856 strcpy(str, replace);
858 strcat(str, holdbuf);
860 if (str[strlen(find)]==0) {
861 strcpy(holdbuf, &str[strlen(find)+1]);
862 strcpy(str, replace);
870 * Handle macros embedded in FETCH data items.
871 * (What the heck are macros doing in a wire protocol? Are we trying to save
872 * the computer at the other end the trouble of typing a lot of characters?)
874 void imap_handle_macros(char *str) {
878 for (i=0; i<strlen(str); ++i) {
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;
884 if (str[i]==']') --nest;
885 if (str[i]=='>') --nest;
886 if (str[i]=='}') --nest;
889 imap_macro_replace(&str[i],
891 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"
893 imap_macro_replace(&str[i],
897 imap_macro_replace(&str[i],
899 "FLAGS INTERNALDATE RFC822.SIZE"
901 imap_macro_replace(&str[i],
903 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"
911 * Break out the data items requested, possibly a parenthesized list.
912 * Returns the number of data items, or -1 if the list is invalid.
913 * NOTE: this function alters the string it is fed, and uses it as a buffer
914 * to hold the data for the pointers it returns.
916 int imap_extract_data_items(char **argv, char *items) {
922 /* Convert all whitespace to ordinary space characters. */
923 for (i=0; i<strlen(items); ++i) {
924 if (isspace(items[i])) items[i]=' ';
927 /* Strip leading and trailing whitespace, then strip leading and
928 * trailing parentheses if it's a list
931 if ( (items[0]=='(') && (items[strlen(items)-1]==')') ) {
932 items[strlen(items)-1] = 0;
933 strcpy(items, &items[1]);
937 /* Parse any macro data items */
938 imap_handle_macros(items);
941 * Now break out the data items. We throw in one trailing space in
942 * order to avoid having to break out the last one manually.
946 initial_len = strlen(items);
947 for (i=0; i<initial_len; ++i) {
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;
953 if (items[i]==']') --nest;
954 if (items[i]=='>') --nest;
955 if (items[i]=='}') --nest;
957 if (nest <= 0) if (items[i]==' ') {
959 argv[num_items++] = start;
970 * One particularly hideous aspect of IMAP is that we have to allow the client
971 * to specify arbitrary ranges and/or sets of messages to fetch. Citadel IMAP
972 * handles this by setting the IMAP_SELECTED flag for each message specified in
973 * the ranges/sets, then looping through the message array, outputting messages
974 * with the flag set. We don't bother returning an error if an out-of-range
975 * number is specified (we just return quietly) because any client braindead
976 * enough to request a bogus message number isn't going to notice the
979 * This function clears out the IMAP_SELECTED bits, then sets that bit for each
980 * message included in the specified range.
982 * Set is_uid to 1 to fetch by UID instead of sequence number.
984 void imap_pick_range(char *supplied_range, int is_uid) {
988 char setstr[SIZ], lostr[SIZ], histr[SIZ]; /* was 1024 */
990 char actual_range[SIZ];
993 * Handle the "ALL" macro
995 if (!strcasecmp(supplied_range, "ALL")) {
996 safestrncpy(actual_range, "1:*", sizeof actual_range);
999 safestrncpy(actual_range, supplied_range, sizeof actual_range);
1003 * Clear out the IMAP_SELECTED flags for all messages.
1005 for (i = 0; i < IMAP->num_msgs; ++i) {
1006 IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SELECTED;
1010 * Now set it for all specified messages.
1012 num_sets = num_tokens(actual_range, ',');
1013 for (s=0; s<num_sets; ++s) {
1014 extract_token(setstr, actual_range, s, ',');
1016 extract_token(lostr, setstr, 0, ':');
1017 if (num_tokens(setstr, ':') >= 2) {
1018 extract_token(histr, setstr, 1, ':');
1019 if (!strcmp(histr, "*")) snprintf(histr, sizeof histr, "%d", INT_MAX);
1022 strcpy(histr, lostr);
1027 /* Loop through the array, flipping bits where appropriate */
1028 for (i = 1; i <= IMAP->num_msgs; ++i) {
1029 if (is_uid) { /* fetch by sequence number */
1030 if ( (IMAP->msgids[i-1]>=lo)
1031 && (IMAP->msgids[i-1]<=hi)) {
1033 IMAP->flags[i-1] | IMAP_SELECTED;
1036 else { /* fetch by uid */
1037 if ( (i>=lo) && (i<=hi)) {
1039 IMAP->flags[i-1] | IMAP_SELECTED;
1050 * This function is called by the main command loop.
1052 void imap_fetch(int num_parms, char *parms[]) {
1053 char items[SIZ]; /* was 1024 */
1054 char *itemlist[SIZ];
1058 if (num_parms < 4) {
1059 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1063 imap_pick_range(parms[2], 0);
1066 for (i=3; i<num_parms; ++i) {
1067 strcat(items, parms[i]);
1068 if (i < (num_parms-1)) strcat(items, " ");
1071 num_items = imap_extract_data_items(itemlist, items);
1072 if (num_items < 1) {
1073 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1077 imap_do_fetch(num_items, itemlist);
1078 cprintf("%s OK FETCH completed\r\n", parms[0]);
1082 * This function is called by the main command loop.
1084 void imap_uidfetch(int num_parms, char *parms[]) {
1085 char items[SIZ]; /* was 1024 */
1086 char *itemlist[SIZ];
1089 int have_uid_item = 0;
1091 if (num_parms < 5) {
1092 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1096 imap_pick_range(parms[3], 1);
1099 for (i=4; i<num_parms; ++i) {
1100 strcat(items, parms[i]);
1101 if (i < (num_parms-1)) strcat(items, " ");
1104 num_items = imap_extract_data_items(itemlist, items);
1105 if (num_items < 1) {
1106 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1110 /* If the "UID" item was not included, we include it implicitly
1111 * because this is a UID FETCH command
1113 for (i=0; i<num_items; ++i) {
1114 if (!strcasecmp(itemlist[i], "UID")) ++have_uid_item;
1116 if (have_uid_item == 0) itemlist[num_items++] = "UID";
1118 imap_do_fetch(num_items, itemlist);
1119 cprintf("%s OK UID FETCH completed\r\n", parms[0]);