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) {
116 long headers_size, text_size, total_size;
117 long bytes_remaining = 0;
123 lprintf(1, "Cannot open temp file: %s\n",
129 * Load the message into a temp file for translation
132 CtdlRedirectOutput(tmp, -1);
133 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
135 CtdlRedirectOutput(NULL, -1);
136 if (!is_valid_message(msg)) {
137 lprintf(1, "WARNING: output clobbered the message!\n");
141 * Now figure out where the headers/text break is. IMAP considers the
142 * intervening blank line to be part of the headers, not the text.
147 ptr = fgets(buf, sizeof buf, tmp);
150 if (strlen(buf) == 0) {
151 headers_size = ftell(tmp);
154 } while ( (headers_size == 0L) && (ptr != NULL) );
155 fseek(tmp, 0L, SEEK_END);
156 total_size = ftell(tmp);
157 text_size = total_size - headers_size;
159 if (!strcasecmp(whichfmt, "RFC822.SIZE")) {
160 cprintf("RFC822.SIZE %ld", total_size);
165 else if (!strcasecmp(whichfmt, "RFC822")) {
166 bytes_remaining = total_size;
170 else if (!strcasecmp(whichfmt, "RFC822.HEADER")) {
171 bytes_remaining = headers_size;
175 else if (!strcasecmp(whichfmt, "RFC822.TEXT")) {
176 bytes_remaining = text_size;
177 fseek(tmp, headers_size, SEEK_SET);
180 cprintf("%s {%ld}\r\n", whichfmt, bytes_remaining);
181 blocksize = sizeof(buf);
182 while (bytes_remaining > 0L) {
183 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
184 fread(buf, blocksize, 1, tmp);
185 client_write(buf, blocksize);
186 bytes_remaining = bytes_remaining - blocksize;
195 * Load a specific part of a message into the temp file to be output to a
196 * client. FIXME we can handle parts like "2" and "2.1" and even "2.MIME"
197 * but we still can't handle "2.HEADER" (which might not be a problem, because
198 * we currently don't have the ability to break out nested RFC822's anyway).
200 * Note: mime_parser() was called with dont_decode set to 1, so we have the
201 * luxury of simply spewing without having to re-encode.
203 void imap_load_part(char *name, char *filename, char *partnum, char *disp,
204 void *content, char *cbtype, size_t length, char *encoding,
207 struct imap_fetch_part *imfp;
210 imfp = (struct imap_fetch_part *)cbuserdata;
212 if (!strcasecmp(partnum, imfp->desired_section)) {
213 fwrite(content, length, 1, imfp->output_fp);
216 snprintf(mbuf2, sizeof mbuf2, "%s.MIME", partnum);
218 if (!strcasecmp(imfp->desired_section, mbuf2)) {
219 fprintf(imfp->output_fp, "Content-type: %s", cbtype);
220 if (strlen(name) > 0)
221 fprintf(imfp->output_fp, "; name=\"%s\"", name);
222 fprintf(imfp->output_fp, "\r\n");
223 if (strlen(encoding) > 0)
224 fprintf(imfp->output_fp,
225 "Content-Transfer-Encoding: %s\r\n", encoding);
226 if (strlen(encoding) > 0) {
227 fprintf(imfp->output_fp, "Content-Disposition: %s",
229 if (strlen(filename) > 0) {
230 fprintf(imfp->output_fp, "; filename=\"%s\"",
233 fprintf(imfp->output_fp, "\r\n");
235 fprintf(imfp->output_fp, "Content-Length: %ld\r\n", (long)length);
236 fprintf(imfp->output_fp, "\r\n");
244 * Called by imap_fetch_envelope() to output the "From" field.
245 * This is in its own function because its logic is kind of complex. We
246 * really need to make this suck less.
248 void imap_output_envelope_from(struct CtdlMessage *msg) {
249 char user[1024], node[1024], name[1024];
251 cprintf("(("); /* open double-parens */
252 imap_strout(msg->cm_fields['A']); /* personal name */
253 cprintf(" NIL "); /* source route (not used) */
255 if (msg->cm_fields['F'] != NULL) {
256 process_rfc822_addr(msg->cm_fields['F'], user, node, name);
257 imap_strout(user); /* mailbox name (user id) */
259 if (!strcasecmp(node, config.c_nodename)) {
260 imap_strout(config.c_fqdn);
263 imap_strout(node); /* host name */
267 imap_strout(msg->cm_fields['A']); /* mailbox name (user id) */
269 imap_strout(msg->cm_fields['N']); /* host name */
272 cprintf(")) "); /* close double-parens */
277 * Implements the ENVELOPE fetch item
279 * FIXME ... we only output some of the fields right now. Definitely need
280 * to do all of them. Accurately, too.
282 * Note that the imap_strout() function can cleverly output NULL fields as NIL,
283 * so we don't have to check for that condition like we do elsewhere.
285 void imap_fetch_envelope(long msgnum, struct CtdlMessage *msg) {
286 char datestringbuf[SIZ];
288 char *fieldptr = NULL;
290 /* Parse the message date into an IMAP-format date string */
291 if (msg->cm_fields['T'] != NULL) {
292 msgdate = atol(msg->cm_fields['T']);
295 msgdate = time(NULL);
297 datestring(datestringbuf, sizeof datestringbuf,
298 msgdate, DATESTRING_IMAP);
300 /* Now start spewing data fields. The order is important, as it is
301 * defined by the protocol specification. Nonexistent fields must
302 * be output as NIL, existent fields must be quoted or literalled.
303 * The imap_strout() function conveniently does all this for us.
305 cprintf("ENVELOPE (");
308 imap_strout(datestringbuf);
312 imap_strout(msg->cm_fields['U']);
316 imap_output_envelope_from(msg);
319 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Sender");
320 if (fieldptr != NULL) {
321 imap_strout(fieldptr);
325 imap_output_envelope_from(msg);
329 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Reply-to");
330 if (fieldptr != NULL) {
331 imap_strout(fieldptr);
335 imap_output_envelope_from(msg);
338 cprintf("NIL "); /* to */
340 cprintf("NIL "); /* cc */
342 cprintf("NIL "); /* bcc */
345 fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "In-reply-to");
346 imap_strout(fieldptr);
348 if (fieldptr != NULL) phree(fieldptr);
351 imap_strout(msg->cm_fields['I']);
358 * Strip any non header information out of a chunk of RFC822 data on disk,
359 * then boil it down to just the fields we want.
361 void imap_strip_headers(FILE *fp, char *section) {
363 char *which_fields = NULL;
364 int doing_headers = 0;
369 char *boiled_headers = NULL;
371 int done_headers = 0;
373 which_fields = strdoop(section);
375 if (!strncasecmp(which_fields, "HEADER.FIELDS", 13))
377 if (!strncasecmp(which_fields, "HEADER.FIELDS.NOT", 17))
380 for (i=0; i<strlen(which_fields); ++i) {
381 if (which_fields[i]=='(')
382 strcpy(which_fields, &which_fields[i+1]);
384 for (i=0; i<strlen(which_fields); ++i) {
385 if (which_fields[i]==')')
388 num_parms = imap_parameterize(parms, which_fields);
390 fseek(fp, 0L, SEEK_END);
391 boiled_headers = mallok((size_t)(ftell(fp) + 256L));
392 strcpy(boiled_headers, "");
396 while ( (done_headers == 0) && (fgets(buf, sizeof buf, fp) != NULL) ) {
397 if (!isspace(buf[0])) {
399 if (doing_headers == 0) ok = 1;
401 if (headers_not) ok = 1;
403 for (i=0; i<num_parms; ++i) {
404 if ( (!strncasecmp(buf, parms[i],
405 strlen(parms[i]))) &&
406 (buf[strlen(parms[i])]==':') ) {
407 if (headers_not) ok = 0;
415 strcat(boiled_headers, buf);
418 if (strlen(buf) == 0) done_headers = 1;
419 if (buf[0]=='\r') done_headers = 1;
420 if (buf[0]=='\n') done_headers = 1;
423 /* Now write it back */
425 fwrite(boiled_headers, strlen(boiled_headers), 1, fp);
427 ftruncate(fileno(fp), ftell(fp));
431 phree(boiled_headers);
436 * Implements the BODY and BODY.PEEK fetch items
438 void imap_fetch_body(long msgnum, char *item, int is_peek,
439 struct CtdlMessage *msg) {
446 long bytes_remaining = 0;
449 struct imap_fetch_part imfp;
451 /* extract section */
452 strcpy(section, item);
453 for (i=0; i<strlen(section); ++i) {
454 if (section[i]=='[') strcpy(section, §ion[i+1]);
456 for (i=0; i<strlen(section); ++i) {
457 if (section[i]==']') section[i] = 0;
459 lprintf(9, "Section is %s\n", section);
461 /* extract partial */
462 strcpy(partial, item);
463 for (i=0; i<strlen(partial); ++i) {
464 if (partial[i]=='<') {
465 strcpy(partial, &partial[i+1]);
469 for (i=0; i<strlen(partial); ++i) {
470 if (partial[i]=='>') partial[i] = 0;
472 if (is_partial == 0) strcpy(partial, "");
473 if (strlen(partial) > 0) lprintf(9, "Partial is %s\n", partial);
477 lprintf(1, "Cannot open temp file: %s\n", strerror(errno));
481 /* Now figure out what the client wants, and get it */
483 if ( (!strcmp(section, "1")) && (msg->cm_format_type != 4) ) {
484 CtdlRedirectOutput(tmp, -1);
485 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
487 CtdlRedirectOutput(NULL, -1);
490 else if (!strcmp(section, "")) {
491 CtdlRedirectOutput(tmp, -1);
492 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
494 CtdlRedirectOutput(NULL, -1);
498 * If the client asked for just headers, or just particular header
499 * fields, strip it down.
501 else if (!strncasecmp(section, "HEADER", 6)) {
502 CtdlRedirectOutput(tmp, -1);
503 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
505 CtdlRedirectOutput(NULL, -1);
506 imap_strip_headers(tmp, section);
510 * Strip it down if the client asked for everything _except_ headers.
512 else if (!strncasecmp(section, "TEXT", 4)) {
513 CtdlRedirectOutput(tmp, -1);
514 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822,
516 CtdlRedirectOutput(NULL, -1);
520 * Anything else must be a part specifier.
521 * (Note value of 1 passed as 'dont_decode' so client gets it encoded)
524 safestrncpy(imfp.desired_section, section,
525 sizeof(imfp.desired_section));
526 imfp.output_fp = tmp;
528 mime_parser(msg->cm_fields['M'], NULL,
529 *imap_load_part, NULL, NULL,
535 fseek(tmp, 0L, SEEK_END);
536 bytes_remaining = ftell(tmp);
538 if (is_partial == 0) {
540 cprintf("BODY[%s] {%ld}\r\n", section, bytes_remaining);
543 sscanf(partial, "%ld.%ld", &pstart, &pbytes);
544 if ((bytes_remaining - pstart) < pbytes) {
545 pbytes = bytes_remaining - pstart;
547 fseek(tmp, pstart, SEEK_SET);
548 bytes_remaining = pbytes;
549 cprintf("BODY[%s]<%ld> {%ld}\r\n",
550 section, pstart, bytes_remaining);
553 blocksize = sizeof(buf);
554 while (bytes_remaining > 0L) {
555 if (blocksize > bytes_remaining) blocksize = bytes_remaining;
556 fread(buf, blocksize, 1, tmp);
557 client_write(buf, blocksize);
558 bytes_remaining = bytes_remaining - blocksize;
563 /* Mark this message as "seen" *unless* this is a "peek" operation */
565 CtdlSetSeen(msgnum, 1);
570 * Called immediately before outputting a multipart bodystructure
572 void imap_fetch_bodystructure_pre(
573 char *name, char *filename, char *partnum, char *disp,
574 void *content, char *cbtype, size_t length, char *encoding,
584 * Called immediately after outputting a multipart bodystructure
586 void imap_fetch_bodystructure_post(
587 char *name, char *filename, char *partnum, char *disp,
588 void *content, char *cbtype, size_t length, char *encoding,
597 extract_token(subtype, cbtype, 1, '/');
598 imap_strout(subtype);
609 * Output the info for a MIME part in the format required by BODYSTRUCTURE.
612 void imap_fetch_bodystructure_part(
613 char *name, char *filename, char *partnum, char *disp,
614 void *content, char *cbtype, size_t length, char *encoding,
619 int have_encoding = 0;
622 char cbmaintype[SIZ];
625 if (cbtype != NULL) if (strlen(cbtype)>0) have_cbtype = 1;
627 extract_token(cbmaintype, cbtype, 0, '/');
628 extract_token(cbsubtype, cbtype, 1, '/');
631 strcpy(cbmaintype, "TEXT");
632 strcpy(cbsubtype, "PLAIN");
636 imap_strout(cbmaintype);
638 imap_strout(cbsubtype);
641 cprintf("(\"CHARSET\" \"US-ASCII\"");
643 if (name != NULL) if (strlen(name)>0) {
644 cprintf(" \"NAME\" ");
650 cprintf("NIL "); /* Body ID */
651 cprintf("NIL "); /* Body description */
653 if (encoding != NULL) if (strlen(encoding) > 0) have_encoding = 1;
655 imap_strout(encoding);
662 /* The next field is the size of the part in bytes. */
663 cprintf("%ld ", (long)length); /* bytes */
665 /* The next field is the number of lines in the part, if and only
666 * if the part is TEXT. Crispin is a fscking idiot.
668 if (!strcasecmp(cbmaintype, "TEXT")) {
669 if (length) for (i=0; i<length; ++i) {
670 if (((char *)content)[i] == '\n') ++lines;
672 cprintf("%d ", lines);
675 /* More of Crispin being a fscking idiot */
676 if ((!strcasecmp(cbmaintype, "MESSAGE"))
677 && (!strcasecmp(cbsubtype, "RFC822"))) {
679 A body type of type MESSAGE and subtype RFC822
680 contains, immediately after the basic fields, the
681 envelope structure, body structure, and size in
682 text lines of the encapsulated message.
686 /* MD5 value of body part; we can get away with NIL'ing this */
693 else if (strlen(disp) == 0) {
699 if (filename != NULL) if (strlen(filename)>0) {
700 cprintf(" (\"FILENAME\" ");
701 imap_strout(filename);
707 /* Body language (not defined yet) */
714 * Spew the BODYSTRUCTURE data for a message. (Do you need a silencer if
715 * you're going to shoot a MIME? Do you need a reason to shoot Mark Crispin?
719 void imap_fetch_bodystructure (long msgnum, char *item,
720 struct CtdlMessage *msg) {
724 long start_of_body = 0L;
725 long body_bytes = 0L;
727 /* For non-RFC822 (ordinary Citadel) messages, this is short and
730 if (msg->cm_format_type != FMT_RFC822) {
732 /* *sigh* We have to RFC822-format the message just to be able
736 if (tmp == NULL) return;
737 CtdlRedirectOutput(tmp, -1);
738 CtdlOutputPreLoadedMsg(msg, msgnum, MT_RFC822, 0, 0, 1);
739 CtdlRedirectOutput(NULL, -1);
742 while (fgets(buf, sizeof buf, tmp) != NULL) {
744 if ((!strcmp(buf, "\r\n")) && (start_of_body == 0L)) {
745 start_of_body = ftell(tmp);
748 body_bytes = ftell(tmp) - start_of_body;
751 cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" "
752 "(\"CHARSET\" \"US-ASCII\") NIL NIL "
753 "\"7BIT\" %ld %ld)", body_bytes, lines);
758 /* For messages already stored in RFC822 format, we have to parse. */
759 cprintf("BODYSTRUCTURE ");
760 mime_parser(msg->cm_fields['M'],
762 *imap_fetch_bodystructure_part, /* part */
763 *imap_fetch_bodystructure_pre, /* pre-multi */
764 *imap_fetch_bodystructure_post, /* post-multi */
766 1); /* don't decode -- we want it as-is */
775 * imap_do_fetch() calls imap_do_fetch_msg() to output the data of an
776 * individual message, once it has been successfully loaded from disk.
778 void imap_do_fetch_msg(int seq, struct CtdlMessage *msg,
779 int num_items, char **itemlist) {
782 cprintf("* %d FETCH (", seq);
784 for (i=0; i<num_items; ++i) {
786 if (!strncasecmp(itemlist[i], "BODY[", 5)) {
787 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
790 else if (!strncasecmp(itemlist[i], "BODY.PEEK[", 10)) {
791 imap_fetch_body(IMAP->msgids[seq-1], itemlist[i],
794 else if (!strcasecmp(itemlist[i], "BODYSTRUCTURE")) {
795 imap_fetch_bodystructure(IMAP->msgids[seq-1],
798 else if (!strcasecmp(itemlist[i], "ENVELOPE")) {
799 imap_fetch_envelope(IMAP->msgids[seq-1], msg);
801 else if (!strcasecmp(itemlist[i], "FLAGS")) {
802 imap_fetch_flags(seq-1);
804 else if (!strcasecmp(itemlist[i], "INTERNALDATE")) {
805 imap_fetch_internaldate(msg);
807 else if (!strcasecmp(itemlist[i], "RFC822")) {
808 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
810 else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) {
811 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
813 else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) {
814 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
816 else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) {
817 imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i], msg);
819 else if (!strcasecmp(itemlist[i], "UID")) {
823 if (i != num_items-1) cprintf(" ");
832 * imap_fetch() calls imap_do_fetch() to do its actual work, once it's
833 * validated and boiled down the request a bit.
835 void imap_do_fetch(int num_items, char **itemlist) {
837 struct CtdlMessage *msg;
839 if (IMAP->num_msgs > 0)
840 for (i = 0; i < IMAP->num_msgs; ++i)
841 if (IMAP->flags[i] & IMAP_SELECTED) {
842 msg = CtdlFetchMessage(IMAP->msgids[i]);
844 imap_do_fetch_msg(i+1, msg, num_items, itemlist);
845 CtdlFreeMessage(msg);
848 cprintf("* %d FETCH <internal error>\r\n", i+1);
856 * Back end for imap_handle_macros()
857 * Note that this function *only* looks at the beginning of the string. It
858 * is not a generic search-and-replace function.
860 void imap_macro_replace(char *str, char *find, char *replace) {
863 if (!strncasecmp(str, find, strlen(find))) {
864 if (str[strlen(find)]==' ') {
865 strcpy(holdbuf, &str[strlen(find)+1]);
866 strcpy(str, replace);
868 strcat(str, holdbuf);
870 if (str[strlen(find)]==0) {
871 strcpy(holdbuf, &str[strlen(find)+1]);
872 strcpy(str, replace);
880 * Handle macros embedded in FETCH data items.
881 * (What the heck are macros doing in a wire protocol? Are we trying to save
882 * the computer at the other end the trouble of typing a lot of characters?)
884 void imap_handle_macros(char *str) {
888 for (i=0; i<strlen(str); ++i) {
889 if (str[i]=='(') ++nest;
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;
899 imap_macro_replace(&str[i],
901 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE"
903 imap_macro_replace(&str[i],
907 imap_macro_replace(&str[i],
909 "FLAGS INTERNALDATE RFC822.SIZE"
911 imap_macro_replace(&str[i],
913 "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY"
921 * Break out the data items requested, possibly a parenthesized list.
922 * Returns the number of data items, or -1 if the list is invalid.
923 * NOTE: this function alters the string it is fed, and uses it as a buffer
924 * to hold the data for the pointers it returns.
926 int imap_extract_data_items(char **argv, char *items) {
932 /* Convert all whitespace to ordinary space characters. */
933 for (i=0; i<strlen(items); ++i) {
934 if (isspace(items[i])) items[i]=' ';
937 /* Strip leading and trailing whitespace, then strip leading and
938 * trailing parentheses if it's a list
941 if ( (items[0]=='(') && (items[strlen(items)-1]==')') ) {
942 items[strlen(items)-1] = 0;
943 strcpy(items, &items[1]);
947 /* Parse any macro data items */
948 imap_handle_macros(items);
951 * Now break out the data items. We throw in one trailing space in
952 * order to avoid having to break out the last one manually.
956 initial_len = strlen(items);
957 for (i=0; i<initial_len; ++i) {
958 if (items[i]=='(') ++nest;
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;
967 if (nest <= 0) if (items[i]==' ') {
969 argv[num_items++] = start;
980 * One particularly hideous aspect of IMAP is that we have to allow the client
981 * to specify arbitrary ranges and/or sets of messages to fetch. Citadel IMAP
982 * handles this by setting the IMAP_SELECTED flag for each message specified in
983 * the ranges/sets, then looping through the message array, outputting messages
984 * with the flag set. We don't bother returning an error if an out-of-range
985 * number is specified (we just return quietly) because any client braindead
986 * enough to request a bogus message number isn't going to notice the
989 * This function clears out the IMAP_SELECTED bits, then sets that bit for each
990 * message included in the specified range.
992 * Set is_uid to 1 to fetch by UID instead of sequence number.
994 void imap_pick_range(char *supplied_range, int is_uid) {
998 char setstr[SIZ], lostr[SIZ], histr[SIZ]; /* was 1024 */
1000 char actual_range[SIZ];
1003 * Handle the "ALL" macro
1005 if (!strcasecmp(supplied_range, "ALL")) {
1006 safestrncpy(actual_range, "1:*", sizeof actual_range);
1009 safestrncpy(actual_range, supplied_range, sizeof actual_range);
1013 * Clear out the IMAP_SELECTED flags for all messages.
1015 for (i = 0; i < IMAP->num_msgs; ++i) {
1016 IMAP->flags[i] = IMAP->flags[i] & ~IMAP_SELECTED;
1020 * Now set it for all specified messages.
1022 num_sets = num_tokens(actual_range, ',');
1023 for (s=0; s<num_sets; ++s) {
1024 extract_token(setstr, actual_range, s, ',');
1026 extract_token(lostr, setstr, 0, ':');
1027 if (num_tokens(setstr, ':') >= 2) {
1028 extract_token(histr, setstr, 1, ':');
1029 if (!strcmp(histr, "*")) snprintf(histr, sizeof histr, "%d", INT_MAX);
1032 strcpy(histr, lostr);
1037 /* Loop through the array, flipping bits where appropriate */
1038 for (i = 1; i <= IMAP->num_msgs; ++i) {
1039 if (is_uid) { /* fetch by sequence number */
1040 if ( (IMAP->msgids[i-1]>=lo)
1041 && (IMAP->msgids[i-1]<=hi)) {
1043 IMAP->flags[i-1] | IMAP_SELECTED;
1046 else { /* fetch by uid */
1047 if ( (i>=lo) && (i<=hi)) {
1049 IMAP->flags[i-1] | IMAP_SELECTED;
1060 * This function is called by the main command loop.
1062 void imap_fetch(int num_parms, char *parms[]) {
1063 char items[SIZ]; /* was 1024 */
1064 char *itemlist[SIZ];
1068 if (num_parms < 4) {
1069 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1073 imap_pick_range(parms[2], 0);
1076 for (i=3; i<num_parms; ++i) {
1077 strcat(items, parms[i]);
1078 if (i < (num_parms-1)) strcat(items, " ");
1081 num_items = imap_extract_data_items(itemlist, items);
1082 if (num_items < 1) {
1083 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1087 imap_do_fetch(num_items, itemlist);
1088 cprintf("%s OK FETCH completed\r\n", parms[0]);
1092 * This function is called by the main command loop.
1094 void imap_uidfetch(int num_parms, char *parms[]) {
1095 char items[SIZ]; /* was 1024 */
1096 char *itemlist[SIZ];
1099 int have_uid_item = 0;
1101 if (num_parms < 5) {
1102 cprintf("%s BAD invalid parameters\r\n", parms[0]);
1106 imap_pick_range(parms[3], 1);
1109 for (i=4; i<num_parms; ++i) {
1110 strcat(items, parms[i]);
1111 if (i < (num_parms-1)) strcat(items, " ");
1114 num_items = imap_extract_data_items(itemlist, items);
1115 if (num_items < 1) {
1116 cprintf("%s BAD invalid data item list\r\n", parms[0]);
1120 /* If the "UID" item was not included, we include it implicitly
1121 * because this is a UID FETCH command
1123 for (i=0; i<num_items; ++i) {
1124 if (!strcasecmp(itemlist[i], "UID")) ++have_uid_item;
1126 if (have_uid_item == 0) itemlist[num_items++] = "UID";
1128 imap_do_fetch(num_items, itemlist);
1129 cprintf("%s OK UID FETCH completed\r\n", parms[0]);