X-Git-Url: https://code.citadel.org/?p=citadel.git;a=blobdiff_plain;f=citadel%2Fmodules%2Fimap%2Fimap_fetch.c;h=293fecca019eec5c160b7c7531a63cfd4c32f818;hp=6a8104b969cf8f927c1a09c4636d91f360b3fa9a;hb=5ac2920028e92a453c686c799327d7a66b3e7b49;hpb=cd75383ed272cab73e4229619f7ba514d39baa69 diff --git a/citadel/modules/imap/imap_fetch.c b/citadel/modules/imap/imap_fetch.c index 6a8104b96..293fecca0 100644 --- a/citadel/modules/imap/imap_fetch.c +++ b/citadel/modules/imap/imap_fetch.c @@ -1,9 +1,18 @@ /* - * $Id$ - * * Implements the FETCH command in IMAP. * This is a good example of the protocol's gratuitous complexity. * + * Copyright (c) 2001-2020 by the citadel.org team + * + * This program is open source software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ @@ -32,24 +41,22 @@ #include #include #include +#include #include "citadel.h" #include "server.h" #include "sysdep_decls.h" #include "citserver.h" #include "support.h" #include "config.h" -#include "room_ops.h" #include "user_ops.h" -#include "policy.h" #include "database.h" #include "msgbase.h" -#include "tools.h" #include "internet_addressing.h" -#include "mime_parser.h" #include "serv_imap.h" #include "imap_tools.h" #include "imap_fetch.h" #include "genstamp.h" +#include "ctdl_module.h" @@ -58,49 +65,56 @@ */ void imap_fetch_uid(int seq) { - cprintf("UID %ld", IMAP->msgids[seq-1]); + IAPrintf("UID %ld", IMAP->msgids[seq-1]); } -void imap_fetch_flags(int seq) { +void imap_fetch_flags(int seq) +{ + citimap *Imap = IMAP; int num_flags_printed = 0; - cprintf("FLAGS ("); - if (IMAP->flags[seq] & IMAP_DELETED) { - if (num_flags_printed > 0) cprintf(" "); - cprintf("\\Deleted"); + IAPuts("FLAGS ("); + if (Imap->flags[seq] & IMAP_DELETED) { + if (num_flags_printed > 0) + IAPuts(" "); + IAPuts("\\Deleted"); ++num_flags_printed; } - if (IMAP->flags[seq] & IMAP_SEEN) { - if (num_flags_printed > 0) cprintf(" "); - cprintf("\\Seen"); + if (Imap->flags[seq] & IMAP_SEEN) { + if (num_flags_printed > 0) + IAPuts(" "); + IAPuts("\\Seen"); ++num_flags_printed; } - if (IMAP->flags[seq] & IMAP_ANSWERED) { - if (num_flags_printed > 0) cprintf(" "); - cprintf("\\Answered"); + if (Imap->flags[seq] & IMAP_ANSWERED) { + if (num_flags_printed > 0) + IAPuts(" "); + IAPuts("\\Answered"); ++num_flags_printed; } - if (IMAP->flags[seq] & IMAP_RECENT) { - if (num_flags_printed > 0) cprintf(" "); - cprintf("\\Recent"); + if (Imap->flags[seq] & IMAP_RECENT) { + if (num_flags_printed > 0) + IAPuts(" "); + IAPuts("\\Recent"); ++num_flags_printed; } - cprintf(")"); + IAPuts(")"); } + void imap_fetch_internaldate(struct CtdlMessage *msg) { - char buf[SIZ]; + char datebuf[64]; time_t msgdate; if (!msg) return; - if (msg->cm_fields['T'] != NULL) { - msgdate = atol(msg->cm_fields['T']); + if (!CM_IsEmpty(msg, eTimestamp)) { + msgdate = atol(msg->cm_fields[eTimestamp]); } else { msgdate = time(NULL); } - datestring(buf, sizeof buf, msgdate, DATESTRING_IMAP); - cprintf("INTERNALDATE \"%s\"", buf); + datestring(datebuf, sizeof datebuf, msgdate, DATESTRING_IMAP); + IAPrintf( "INTERNALDATE \"%s\"", datebuf); } @@ -113,9 +127,10 @@ void imap_fetch_internaldate(struct CtdlMessage *msg) { * "RFC822.SIZE" size of translated message * "RFC822.TEXT" body only (without leading blank line) */ -void imap_fetch_rfc822(long msgnum, char *whichfmt) { - char buf[SIZ]; - char *ptr = NULL; +void imap_fetch_rfc822(long msgnum, const char *whichfmt) { + CitContext *CCC = CC; + citimap *Imap = CCCIMAP; + const char *ptr = NULL; size_t headers_size, text_size, total_size; size_t bytes_to_send = 0; struct MetaData smi; @@ -126,8 +141,7 @@ void imap_fetch_rfc822(long msgnum, char *whichfmt) { * us to fetch the message body from disk. If not, we can save * on some disk operations... */ - if ( (!strcasecmp(whichfmt, "RFC822")) - || (!strcasecmp(whichfmt, "RFC822.TEXT")) ) { + if ( (!strcasecmp(whichfmt, "RFC822")) || (!strcasecmp(whichfmt, "RFC822.TEXT")) ) { need_body = 1; } @@ -137,7 +151,7 @@ void imap_fetch_rfc822(long msgnum, char *whichfmt) { if (!strcasecmp(whichfmt, "RFC822.SIZE")) { GetMetaData(&smi, msgnum); if (smi.meta_rfc822_length > 0L) { - cprintf("RFC822.SIZE %ld", smi.meta_rfc822_length); + IAPrintf("RFC822.SIZE %ld", smi.meta_rfc822_length); return; } need_to_rewrite_metadata = 1; @@ -150,42 +164,39 @@ void imap_fetch_rfc822(long msgnum, char *whichfmt) { * client requests something that involves reading the message * body, but we haven't fetched the body yet. */ - if ((IMAP->cached_rfc822_data != NULL) - && (IMAP->cached_rfc822_msgnum == msgnum) - && (IMAP->cached_rfc822_withbody || (!need_body)) ) { + if ((Imap->cached_rfc822 != NULL) + && (Imap->cached_rfc822_msgnum == msgnum) + && (Imap->cached_rfc822_withbody || (!need_body)) ) { /* Good to go! */ } - else if (IMAP->cached_rfc822_data != NULL) { + else if (Imap->cached_rfc822 != NULL) { /* Some other message is cached -- free it */ - free(IMAP->cached_rfc822_data); - IMAP->cached_rfc822_data = NULL; - IMAP->cached_rfc822_msgnum = (-1); - IMAP->cached_rfc822_len = 0; + FreeStrBuf(&Imap->cached_rfc822); + Imap->cached_rfc822_msgnum = (-1); } /* At this point, we now can fetch and convert the message iff it's not * the one we had cached. */ - if (IMAP->cached_rfc822_data == NULL) { + if (Imap->cached_rfc822 == NULL) { /* * Load the message into memory for translation & measurement */ - CC->redirect_buffer = malloc(SIZ); - CC->redirect_len = 0; - CC->redirect_alloc = SIZ; + CCC->redirect_buffer = NewStrBufPlain(NULL, SIZ); CtdlOutputMsg(msgnum, MT_RFC822, - (need_body ? HEADERS_ALL : HEADERS_ONLY), - 0, 1, NULL); - if (!need_body) cprintf("\r\n"); /* extra trailing newline */ - IMAP->cached_rfc822_data = CC->redirect_buffer; - IMAP->cached_rfc822_len = CC->redirect_len; - IMAP->cached_rfc822_msgnum = msgnum; - IMAP->cached_rfc822_withbody = need_body; - CC->redirect_buffer = NULL; - CC->redirect_len = 0; - CC->redirect_alloc = 0; - if ( (need_to_rewrite_metadata) && (IMAP->cached_rfc822_len > 0) ) { - smi.meta_rfc822_length = (long)IMAP->cached_rfc822_len; + (need_body ? HEADERS_ALL : HEADERS_FAST), + 0, 1, NULL, SUPPRESS_ENV_TO, NULL, NULL, NULL + ); + if (!need_body) { + client_write(HKEY("\r\n")); // extra trailing newline -- to the redirect_buffer, *not* to the client + } + Imap->cached_rfc822 = CCC->redirect_buffer; + CCC->redirect_buffer = NULL; + Imap->cached_rfc822_msgnum = msgnum; + Imap->cached_rfc822_withbody = need_body; + if ( (need_to_rewrite_metadata) && + (StrLength(Imap->cached_rfc822) > 0) ) { + smi.meta_rfc822_length = StrLength(Imap->cached_rfc822); PutMetaData(&smi); } } @@ -195,62 +206,61 @@ void imap_fetch_rfc822(long msgnum, char *whichfmt) { * intervening blank line to be part of the headers, not the text. */ headers_size = 0; - text_size = 0; - total_size = 0; if (need_body) { - ptr = IMAP->cached_rfc822_data; + StrBuf *Line = NewStrBuf(); + ptr = NULL; do { - ptr = memreadline(ptr, buf, sizeof buf); - if (*ptr != 0) { - striplt(buf); - if (IsEmptyStr(buf)) { - headers_size = ptr - IMAP->cached_rfc822_data; + StrBufSipLine(Line, Imap->cached_rfc822, &ptr); + + if ((StrLength(Line) != 0) && (ptr != StrBufNOTNULL)) + { + StrBufTrim(Line); + if ((StrLength(Line) != 0) && + (ptr != StrBufNOTNULL) ) + { + headers_size = ptr - ChrPtr(Imap->cached_rfc822); } } - } while ( (headers_size == 0) && (*ptr != 0) ); + } while ( (headers_size == 0) && + (ptr != StrBufNOTNULL) ); - total_size = IMAP->cached_rfc822_len; + total_size = StrLength(Imap->cached_rfc822); text_size = total_size - headers_size; + FreeStrBuf(&Line); } else { - headers_size = IMAP->cached_rfc822_len; - total_size = IMAP->cached_rfc822_len; + headers_size = total_size = StrLength(Imap->cached_rfc822); text_size = 0; } - lprintf(CTDL_DEBUG, - "RFC822: headers=" SIZE_T_FMT - ", text=" SIZE_T_FMT - ", total=" SIZE_T_FMT "\n", - headers_size, text_size, total_size); + syslog(LOG_DEBUG, "imap: RFC822 headers=" SIZE_T_FMT ", text=" SIZE_T_FMT ", total=" SIZE_T_FMT, headers_size, text_size, total_size); if (!strcasecmp(whichfmt, "RFC822.SIZE")) { - cprintf("RFC822.SIZE " SIZE_T_FMT, total_size); + IAPrintf("RFC822.SIZE " SIZE_T_FMT, total_size); return; } else if (!strcasecmp(whichfmt, "RFC822")) { - ptr = IMAP->cached_rfc822_data; + ptr = ChrPtr(Imap->cached_rfc822); bytes_to_send = total_size; } else if (!strcasecmp(whichfmt, "RFC822.HEADER")) { - ptr = IMAP->cached_rfc822_data; + ptr = ChrPtr(Imap->cached_rfc822); bytes_to_send = headers_size; } else if (!strcasecmp(whichfmt, "RFC822.TEXT")) { - ptr = &IMAP->cached_rfc822_data[headers_size]; + ptr = &ChrPtr(Imap->cached_rfc822)[headers_size]; bytes_to_send = text_size; } - cprintf("%s {" SIZE_T_FMT "}\r\n", whichfmt, bytes_to_send); - client_write(ptr, bytes_to_send); + IAPrintf("%s {" SIZE_T_FMT "}\r\n", whichfmt, bytes_to_send); + iaputs(ptr, bytes_to_send); } - /* * Load a specific part of a message into the temp file to be output to a * client. FIXME we can handle parts like "2" and "2.1" and even "2.MIME" @@ -261,40 +271,52 @@ void imap_fetch_rfc822(long msgnum, char *whichfmt) { */ void imap_load_part(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - void *cbuserdata) + char *cbid, void *cbuserdata) { - char mbuf2[SIZ]; - char *desired_section; + char mimebuf2[SIZ]; + StrBuf *desired_section; - desired_section = (char *)cbuserdata; + desired_section = (StrBuf *)cbuserdata; + syslog(LOG_DEBUG, "imap: imap_load_part() looking for %s, found %s", ChrPtr(desired_section), partnum); - if (!strcasecmp(partnum, desired_section)) { + if (!strcasecmp(partnum, ChrPtr(desired_section))) { client_write(content, length); } - snprintf(mbuf2, sizeof mbuf2, "%s.MIME", partnum); + snprintf(mimebuf2, sizeof mimebuf2, "%s.MIME", partnum); - if (!strcasecmp(desired_section, mbuf2)) { - cprintf("Content-type: %s", cbtype); - if (!IsEmptyStr(cbcharset)) - cprintf("; charset=\"%s\"", cbcharset); - if (!IsEmptyStr(name)) - cprintf("; name=\"%s\"", name); - cprintf("\r\n"); - if (!IsEmptyStr(encoding)) - cprintf("Content-Transfer-Encoding: %s\r\n", encoding); + if (!strcasecmp(ChrPtr(desired_section), mimebuf2)) { + client_write(HKEY("Content-type: ")); + client_write(cbtype, strlen(cbtype)); + if (!IsEmptyStr(cbcharset)) { + client_write(HKEY("; charset=\"")); + client_write(cbcharset, strlen(cbcharset)); + client_write(HKEY("\"")); + } + if (!IsEmptyStr(name)) { + client_write(HKEY("; name=\"")); + client_write(name, strlen(name)); + client_write(HKEY("\"")); + } + client_write(HKEY("\r\n")); if (!IsEmptyStr(encoding)) { - cprintf("Content-Disposition: %s", disp); + client_write(HKEY("Content-Transfer-Encoding: ")); + client_write(encoding, strlen(encoding)); + client_write(HKEY("\r\n")); + } + if (!IsEmptyStr(encoding)) { + client_write(HKEY("Content-Disposition: ")); + client_write(disp, strlen(disp)); + if (!IsEmptyStr(filename)) { - cprintf("; filename=\"%s\"", filename); + client_write(HKEY("; filename=\"")); + client_write(filename, strlen(filename)); + client_write(HKEY("\"")); } - cprintf("\r\n"); + client_write(HKEY("\r\n")); } - cprintf("Content-Length: %ld\r\n", (long)length); - cprintf("\r\n"); + cprintf("Content-Length: %ld\r\n\r\n", (long)length); } - - } @@ -310,45 +332,38 @@ void imap_output_envelope_from(struct CtdlMessage *msg) { /* For anonymous messages, it's so easy! */ if (!is_room_aide() && (msg->cm_anon_type == MES_ANONONLY)) { - cprintf("((\"----\" NIL \"x\" \"x.org\")) "); + IAPuts("((\"----\" NIL \"x\" \"x.org\")) "); return; } if (!is_room_aide() && (msg->cm_anon_type == MES_ANONOPT)) { - cprintf("((\"anonymous\" NIL \"x\" \"x.org\")) "); + IAPuts("((\"anonymous\" NIL \"x\" \"x.org\")) "); return; } /* For everything else, we do stuff. */ - cprintf("(("); /* open double-parens */ - imap_strout(msg->cm_fields['A']); /* personal name */ - cprintf(" NIL "); /* source route (not used) */ + IAPuts("(("); // open double-parens + IPutMsgField(eAuthor); // display name + IAPuts(" NIL "); // source route (not used) - - if (msg->cm_fields['F'] != NULL) { - process_rfc822_addr(msg->cm_fields['F'], user, node, name); - imap_strout(user); /* mailbox name (user id) */ - cprintf(" "); - if (!strcasecmp(node, config.c_nodename)) { - imap_strout(config.c_fqdn); - } - else { - imap_strout(node); /* host name */ - } + if (!CM_IsEmpty(msg, erFc822Addr)) { + process_rfc822_addr(msg->cm_fields[erFc822Addr], user, node, name); + IPutStr(user, strlen(user)); /* mailbox name (user id) */ + IAPuts(" "); + IPutStr(node, strlen(node)); /* host name */ } else { - imap_strout(msg->cm_fields['A']); /* mailbox name (user id) */ - cprintf(" "); - imap_strout(msg->cm_fields['N']); /* host name */ + IPutMsgField(eAuthor); /* Make up a synthetic address */ + IAPuts(" "); + IPutStr(CtdlGetConfigStr("c_fqdn"), strlen(CtdlGetConfigStr("c_fqdn"))); } - cprintf(")) "); /* close double-parens */ + IAPuts(")) "); /* close double-parens */ } - /* * Output an envelope address (or set of addresses) in the official, - * convuluted, braindead format. (Note that we can't use this for + * convoluted, braindead format. (Note that we can't use this for * the "From" address because its data may come from a number of different * fields. But we can use it for "To" and possibly others. */ @@ -361,16 +376,16 @@ void imap_output_envelope_addr(char *addr) { char name[256]; if (addr == NULL) { - cprintf("NIL "); + IAPuts("NIL "); return; } if (IsEmptyStr(addr)) { - cprintf("NIL "); + IAPuts("NIL "); return; } - cprintf("("); + IAPuts("("); /* How many addresses are listed here? */ num_addrs = num_tokens(addr, ','); @@ -380,17 +395,18 @@ void imap_output_envelope_addr(char *addr) { extract_token(individual_addr, addr, i, ',', sizeof individual_addr); striplt(individual_addr); process_rfc822_addr(individual_addr, user, node, name); - cprintf("("); - imap_strout(name); - cprintf(" NIL "); - imap_strout(user); - cprintf(" "); - imap_strout(node); - cprintf(")"); - if (i < (num_addrs-1)) cprintf(" "); - } - - cprintf(") "); + IAPuts("("); + IPutStr(name, strlen(name)); + IAPuts(" NIL "); + IPutStr(user, strlen(user)); + IAPuts(" "); + IPutStr(node, strlen(node)); + IAPuts(")"); + if (i < (num_addrs-1)) + IAPuts(" "); + } + + IAPuts(") "); } @@ -404,39 +420,39 @@ void imap_fetch_envelope(struct CtdlMessage *msg) { char datestringbuf[SIZ]; time_t msgdate; char *fieldptr = NULL; + long len; if (!msg) return; /* Parse the message date into an IMAP-format date string */ - if (msg->cm_fields['T'] != NULL) { - msgdate = atol(msg->cm_fields['T']); + if (!CM_IsEmpty(msg, eTimestamp)) { + msgdate = atol(msg->cm_fields[eTimestamp]); } else { msgdate = time(NULL); } - datestring(datestringbuf, sizeof datestringbuf, - msgdate, DATESTRING_IMAP); + len = datestring(datestringbuf, sizeof datestringbuf, msgdate, DATESTRING_IMAP); /* Now start spewing data fields. The order is important, as it is * defined by the protocol specification. Nonexistent fields must * be output as NIL, existent fields must be quoted or literalled. * The imap_strout() function conveniently does all this for us. */ - cprintf("ENVELOPE ("); + IAPuts("ENVELOPE ("); /* Date */ - imap_strout(datestringbuf); - cprintf(" "); + IPutStr(datestringbuf, len); + IAPuts(" "); /* Subject */ - imap_strout(msg->cm_fields['U']); - cprintf(" "); + IPutMsgField(eMsgSubject); + IAPuts(" "); /* From */ imap_output_envelope_from(msg); /* Sender (default to same as 'From' if not present) */ - fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Sender"); + fieldptr = rfc822_fetch_field(msg->cm_fields[eMesageText], "Sender"); if (fieldptr != NULL) { imap_output_envelope_addr(fieldptr); free(fieldptr); @@ -446,7 +462,7 @@ void imap_fetch_envelope(struct CtdlMessage *msg) { } /* Reply-to */ - fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Reply-to"); + fieldptr = rfc822_fetch_field(msg->cm_fields[eMesageText], "Reply-to"); if (fieldptr != NULL) { imap_output_envelope_addr(fieldptr); free(fieldptr); @@ -456,192 +472,222 @@ void imap_fetch_envelope(struct CtdlMessage *msg) { } /* To */ - imap_output_envelope_addr(msg->cm_fields['R']); + imap_output_envelope_addr(msg->cm_fields[eRecipient]); /* Cc (we do it this way because there might be a legacy non-Citadel Cc: field present) */ - fieldptr = msg->cm_fields['Y']; + fieldptr = msg->cm_fields[eCarbonCopY]; if (fieldptr != NULL) { imap_output_envelope_addr(fieldptr); } else { - fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Cc"); + fieldptr = rfc822_fetch_field(msg->cm_fields[eMesageText], "Cc"); imap_output_envelope_addr(fieldptr); if (fieldptr != NULL) free(fieldptr); } /* Bcc */ - fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "Bcc"); + fieldptr = rfc822_fetch_field(msg->cm_fields[eMesageText], "Bcc"); imap_output_envelope_addr(fieldptr); if (fieldptr != NULL) free(fieldptr); /* In-reply-to */ - fieldptr = rfc822_fetch_field(msg->cm_fields['M'], "In-reply-to"); - imap_strout(fieldptr); - cprintf(" "); + fieldptr = rfc822_fetch_field(msg->cm_fields[eMesageText], "In-reply-to"); + IPutStr(fieldptr, (fieldptr)?strlen(fieldptr):0); + IAPuts(" "); if (fieldptr != NULL) free(fieldptr); /* message ID */ - imap_strout(msg->cm_fields['I']); - - cprintf(")"); + len = msg->cm_lengths[emessageId]; + + if ((len == 0) || ( + (msg->cm_fields[emessageId][0] == '<') && + (msg->cm_fields[emessageId][len - 1] == '>')) + ) { + IPutMsgField(emessageId); + } + else + { + char *Buf = malloc(len + 3); + long pos = 0; + + if (msg->cm_fields[emessageId][0] != '<') + { + Buf[pos] = '<'; + pos ++; + } + memcpy(&Buf[pos], msg->cm_fields[emessageId], len); + pos += len; + if (msg->cm_fields[emessageId][len] != '>') + { + Buf[pos] = '>'; + pos++; + } + Buf[pos] = '\0'; + IPutStr(Buf, pos); + free(Buf); + } + IAPuts(")"); } + /* * This function is called only when CC->redirect_buffer contains a set of * RFC822 headers with no body attached. Its job is to strip that set of * headers down to *only* the ones we're interested in. */ -void imap_strip_headers(char *section) { - char buf[SIZ]; - char *which_fields = NULL; +void imap_strip_headers(StrBuf *section) { + citimap_command Cmd; + StrBuf *which_fields = NULL; int doing_headers = 0; int headers_not = 0; - char *parms[SIZ]; - int num_parms = 0; + int num_parms = 0; int i; - char *boiled_headers = NULL; + StrBuf *boiled_headers = NULL; + StrBuf *Line; int ok = 0; int done_headers = 0; - char *ptr = NULL; + const char *Ptr = NULL; + CitContext *CCC = CC; - if (CC->redirect_buffer == NULL) return; + if (CCC->redirect_buffer == NULL) return; - which_fields = strdup(section); + which_fields = NewStrBufDup(section); - if (!strncasecmp(which_fields, "HEADER.FIELDS", 13)) + if (!strncasecmp(ChrPtr(which_fields), "HEADER.FIELDS", 13)) doing_headers = 1; - if (!strncasecmp(which_fields, "HEADER.FIELDS.NOT", 17)) + if (doing_headers && + !strncasecmp(ChrPtr(which_fields), "HEADER.FIELDS.NOT", 17)) headers_not = 1; - for (i=0; which_fields[i]; ++i) { - if (which_fields[i]=='(') - strcpy(which_fields, &which_fields[i+1]); + for (i=0; i < StrLength(which_fields); ++i) { + if (ChrPtr(which_fields)[i]=='(') + StrBufReplaceToken(which_fields, i, 1, HKEY("")); } - for (i=0; which_fields[i]; ++i) { - if (which_fields[i]==')') { - which_fields[i] = 0; + for (i=0; i < StrLength(which_fields); ++i) { + if (ChrPtr(which_fields)[i]==')') { + StrBufCutAt(which_fields, i, NULL); break; } } - num_parms = imap_parameterize(parms, which_fields); - - boiled_headers = malloc(CC->redirect_alloc); - strcpy(boiled_headers, ""); + memset(&Cmd, 0, sizeof(citimap_command)); + Cmd.CmdBuf = which_fields; + num_parms = imap_parameterize(&Cmd); - ptr = CC->redirect_buffer; + boiled_headers = NewStrBufPlain(NULL, StrLength(CCC->redirect_buffer)); + Line = NewStrBufPlain(NULL, SIZ); + Ptr = NULL; ok = 0; - while ( (done_headers == 0) && (ptr = memreadline(ptr, buf, sizeof buf), *ptr != 0) ) { - if (!isspace(buf[0])) { - ok = 0; + do { + StrBufSipLine(Line, CCC->redirect_buffer, &Ptr); + + if (!isspace(ChrPtr(Line)[0])) { + if (doing_headers == 0) ok = 1; else { - if (headers_not) ok = 1; - else ok = 0; - for (i=0; iredirect_buffer, boiled_headers); - CC->redirect_len = strlen(boiled_headers); + FreeStrBuf(&CCC->redirect_buffer); + CCC->redirect_buffer = boiled_headers; - free(which_fields); - free(boiled_headers); + free(Cmd.Params); + FreeStrBuf(&which_fields); + FreeStrBuf(&Line); } /* * Implements the BODY and BODY.PEEK fetch items */ -void imap_fetch_body(long msgnum, char *item, int is_peek) { +void imap_fetch_body(long msgnum, ConstStr item, int is_peek) { struct CtdlMessage *msg = NULL; - char section[SIZ]; - char partial[SIZ]; + StrBuf *section; + StrBuf *partial; int is_partial = 0; size_t pstart, pbytes; int loading_body_now = 0; int need_body = 1; int burn_the_cache = 0; + CitContext *CCC = CC; + citimap *Imap = CCCIMAP; /* extract section */ - safestrncpy(section, item, sizeof section); - if (strchr(section, '[') != NULL) { - stripallbut(section, '[', ']'); + section = NewStrBufPlain(CKEY(item)); + + if (strchr(ChrPtr(section), '[') != NULL) { + StrBufStripAllBut(section, '[', ']'); } - lprintf(CTDL_DEBUG, "Section is: %s%s\n", - section, - IsEmptyStr(section) ? "(empty)" : ""); - - /* - * We used to have this great optimization in place that would avoid - * fetching the entire RFC822 message from disk if the client was only - * asking for the headers. Unfortunately, fetching only the Citadel - * headers omits "Content-type:" and this behavior breaks the iPhone - * email client. So we have to fetch the whole message from disk. The - * - * if (!strncasecmp(section, "HEADER", 6)) { - * need_body = 0; - * } - * - */ + syslog(LOG_DEBUG, "imap: selected section is [%s]", (StrLength(section) == 0) ? "(empty)" : ChrPtr(section)); /* Burn the cache if we don't have the same section of the * same message again. */ - if (IMAP->cached_body != NULL) { - if (IMAP->cached_bodymsgnum != msgnum) { + if (Imap->cached_body != NULL) { + if (Imap->cached_bodymsgnum != msgnum) { burn_the_cache = 1; } - else if ( (!IMAP->cached_body_withbody) && (need_body) ) { + else if ( (!Imap->cached_body_withbody) && (need_body) ) { burn_the_cache = 1; } - else if (strcasecmp(IMAP->cached_bodypart, section)) { + else if (strcasecmp(Imap->cached_bodypart, ChrPtr(section))) { burn_the_cache = 1; } if (burn_the_cache) { /* Yup, go ahead and burn the cache. */ - free(IMAP->cached_body); - IMAP->cached_body_len = 0; - IMAP->cached_body = NULL; - IMAP->cached_bodymsgnum = (-1); - strcpy(IMAP->cached_bodypart, ""); + free(Imap->cached_body); + Imap->cached_body_len = 0; + Imap->cached_body = NULL; + Imap->cached_bodymsgnum = (-1); + strcpy(Imap->cached_bodypart, ""); } } /* extract partial */ - safestrncpy(partial, item, sizeof partial); - if (strchr(partial, '<') != NULL) { - stripallbut(partial, '<', '>'); + partial = NewStrBufPlain(CKEY(item)); + if (strchr(ChrPtr(partial), '<') != NULL) { + StrBufStripAllBut(partial, '<', '>'); is_partial = 1; } - if (is_partial == 0) strcpy(partial, ""); - /* if (!IsEmptyStr(partial)) lprintf(CTDL_DEBUG, "Partial is %s\n", partial); */ + if ( (is_partial == 1) && (StrLength(partial) > 0) ) { + syslog(LOG_DEBUG, "imap: selected partial is <%s>", ChrPtr(partial)); + } - if (IMAP->cached_body == NULL) { - CC->redirect_buffer = malloc(SIZ); - CC->redirect_len = 0; - CC->redirect_alloc = SIZ; + if (Imap->cached_body == NULL) { + CCC->redirect_buffer = NewStrBufPlain(NULL, SIZ); loading_body_now = 1; - msg = CtdlFetchMessage(msgnum, (need_body ? 1 : 0)); + msg = CtdlFetchMessage(msgnum, (need_body ? 1 : 0), 1); } /* Now figure out what the client wants, and get it */ @@ -650,28 +696,32 @@ void imap_fetch_body(long msgnum, char *item, int is_peek) { /* What we want is already in memory */ } - else if ( (!strcmp(section, "1")) && (msg->cm_format_type != 4) ) { - CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_NONE, 0, 1); + else if ( (!strcmp(ChrPtr(section), "1")) && (msg->cm_format_type != 4) ) { + CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_NONE, 0, 1, SUPPRESS_ENV_TO); } - else if (!strcmp(section, "")) { - CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1); + else if (StrLength(section) == 0) { + CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1, SUPPRESS_ENV_TO); } /* * If the client asked for just headers, or just particular header * fields, strip it down. */ - else if (!strncasecmp(section, "HEADER", 6)) { - CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ONLY, 0, 1); + else if (!strncasecmp(ChrPtr(section), "HEADER", 6)) { + /* This used to work with HEADERS_FAST, but then Apple got stupid with their + * IMAP library and this broke Mail.App and iPhone Mail, so we had to change it + * to HEADERS_ONLY so the trendy hipsters with their iPhones can read mail. + */ + CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ONLY, 0, 1, SUPPRESS_ENV_TO); imap_strip_headers(section); } /* * Strip it down if the client asked for everything _except_ headers. */ - else if (!strncasecmp(section, "TEXT", 4)) { - CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_NONE, 0, 1); + else if (!strncasecmp(ChrPtr(section), "TEXT", 4)) { + CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_NONE, 0, 1, SUPPRESS_ENV_TO); } /* @@ -679,47 +729,52 @@ void imap_fetch_body(long msgnum, char *item, int is_peek) { * (Note value of 1 passed as 'dont_decode' so client gets it encoded) */ else { - mime_parser(msg->cm_fields['M'], NULL, - *imap_load_part, NULL, NULL, - section, - 1); + mime_parser(CM_RANGE(msg, eMesageText), + *imap_load_part, NULL, NULL, + section, + 1 + ); } if (loading_body_now) { - IMAP->cached_body = CC->redirect_buffer; - IMAP->cached_body_len = CC->redirect_len; - IMAP->cached_bodymsgnum = msgnum; - IMAP->cached_body_withbody = need_body; - strcpy(IMAP->cached_bodypart, section); - CC->redirect_buffer = NULL; - CC->redirect_len = 0; - CC->redirect_alloc = 0; + Imap->cached_body_len = StrLength(CCC->redirect_buffer); + Imap->cached_body = SmashStrBuf(&CCC->redirect_buffer); + Imap->cached_bodymsgnum = msgnum; + Imap->cached_body_withbody = need_body; + strcpy(Imap->cached_bodypart, ChrPtr(section)); } if (is_partial == 0) { - cprintf("BODY[%s] {" SIZE_T_FMT "}\r\n", section, IMAP->cached_body_len); + IAPuts("BODY["); + iaputs(SKEY(section)); + IAPrintf("] {" SIZE_T_FMT "}\r\n", Imap->cached_body_len); pstart = 0; - pbytes = IMAP->cached_body_len; + pbytes = Imap->cached_body_len; } else { - sscanf(partial, SIZE_T_FMT "." SIZE_T_FMT, &pstart, &pbytes); - if (pbytes > (IMAP->cached_body_len - pstart)) { - pbytes = IMAP->cached_body_len - pstart; + sscanf(ChrPtr(partial), SIZE_T_FMT "." SIZE_T_FMT, &pstart, &pbytes); + if (pbytes > (Imap->cached_body_len - pstart)) { + pbytes = Imap->cached_body_len - pstart; } - cprintf("BODY[%s]<" SIZE_T_FMT "> {" SIZE_T_FMT "}\r\n", section, pstart, pbytes); + IAPuts("BODY["); + iaputs(SKEY(section)); + IAPrintf("]<" SIZE_T_FMT "> {" SIZE_T_FMT "}\r\n", pstart, pbytes); } + FreeStrBuf(&partial); + /* Here we go -- output it */ - client_write(&IMAP->cached_body[pstart], pbytes); + iaputs(&Imap->cached_body[pstart], pbytes); if (msg != NULL) { - CtdlFreeMessage(msg); + CM_Free(msg); } /* Mark this message as "seen" *unless* this is a "peek" operation */ if (is_peek == 0) { CtdlSetSeen(&msgnum, 1, 1, ctdlsetseen_seen, NULL, NULL); } + FreeStrBuf(§ion); } /* @@ -728,10 +783,10 @@ void imap_fetch_body(long msgnum, char *item, int is_peek) { void imap_fetch_bodystructure_pre( char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - void *cbuserdata + char *cbid, void *cbuserdata ) { - cprintf("("); + IAPuts("("); } @@ -742,21 +797,21 @@ void imap_fetch_bodystructure_pre( void imap_fetch_bodystructure_post( char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - void *cbuserdata + char *cbid, void *cbuserdata ) { - + long len; char subtype[128]; - cprintf(" "); + IAPuts(" "); /* disposition */ - extract_token(subtype, cbtype, 1, '/', sizeof subtype); - imap_strout(subtype); + len = extract_token(subtype, cbtype, 1, '/', sizeof subtype); + IPutStr(subtype, len); /* body language */ - /* cprintf(" NIL"); We thought we needed this at one point, but maybe we don't... */ + /* IAPuts(" NIL"); We thought we needed this at one point, but maybe we don't... */ - cprintf(")"); + IAPuts(")"); } @@ -768,7 +823,7 @@ void imap_fetch_bodystructure_post( void imap_fetch_bodystructure_part( char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - void *cbuserdata + char *cbid, void *cbuserdata ) { int have_cbtype = 0; @@ -777,61 +832,62 @@ void imap_fetch_bodystructure_part( size_t i; char cbmaintype[128]; char cbsubtype[128]; + long cbmaintype_len; + long cbsubtype_len; if (cbtype != NULL) if (!IsEmptyStr(cbtype)) have_cbtype = 1; if (have_cbtype) { - extract_token(cbmaintype, cbtype, 0, '/', sizeof cbmaintype); - extract_token(cbsubtype, cbtype, 1, '/', sizeof cbsubtype); + cbmaintype_len = extract_token(cbmaintype, cbtype, 0, '/', sizeof cbmaintype); + cbsubtype_len = extract_token(cbsubtype, cbtype, 1, '/', sizeof cbsubtype); } else { strcpy(cbmaintype, "TEXT"); + cbmaintype_len = 4; strcpy(cbsubtype, "PLAIN"); + cbsubtype_len = 5; } - cprintf("("); - imap_strout(cbmaintype); /* body type */ - cprintf(" "); - imap_strout(cbsubtype); /* body subtype */ - cprintf(" "); + IAPuts("("); + IPutStr(cbmaintype, cbmaintype_len); /* body type */ + IAPuts(" "); + IPutStr(cbsubtype, cbsubtype_len); /* body subtype */ + IAPuts(" "); - cprintf("("); /* begin body parameter list */ + IAPuts("("); /* begin body parameter list */ /* "NAME" must appear as the first parameter. This is not required by IMAP, * but the Asterisk voicemail application blindly assumes that NAME will be in * the first position. If it isn't, it rejects the message. */ - if (name != NULL) if (!IsEmptyStr(name)) { - cprintf("\"NAME\" "); - imap_strout(name); - cprintf(" "); + if ((name != NULL) && (!IsEmptyStr(name))) { + IAPuts("\"NAME\" "); + IPutStr(name, strlen(name)); + IAPuts(" "); } - cprintf("\"CHARSET\" "); - if (cbcharset == NULL) { - imap_strout("US-ASCII"); - } - else if (cbcharset[0] == 0) { - imap_strout("US-ASCII"); + IAPuts("\"CHARSET\" "); + if ((cbcharset == NULL) || (cbcharset[0] == 0)){ + IPutStr(HKEY("US-ASCII")); } else { - imap_strout(cbcharset); + IPutStr(cbcharset, strlen(cbcharset)); } - cprintf(") "); /* end body parameter list */ + IAPuts(") "); /* end body parameter list */ - cprintf("NIL "); /* Body ID */ - cprintf("NIL "); /* Body description */ + IAPuts("NIL "); /* Body ID */ + IAPuts("NIL "); /* Body description */ - if (encoding != NULL) if (encoding[0] != 0) have_encoding = 1; + if ((encoding != NULL) && (encoding[0] != 0)) have_encoding = 1; if (have_encoding) { - imap_strout(encoding); + IPutStr(encoding, strlen(encoding)); } else { - imap_strout("7BIT"); + IPutStr(HKEY("7BIT")); } - cprintf(" "); + IAPuts(" "); /* The next field is the size of the part in bytes. */ - cprintf("%ld ", (long)length); /* bytes */ + IAPrintf("%ld ", (long)length); /* bytes */ /* The next field is the number of lines in the part, if and only * if the part is TEXT. More gratuitous complexity. @@ -840,43 +896,39 @@ void imap_fetch_bodystructure_part( if (length) for (i=0; iredirect_buffer = malloc(SIZ); - CC->redirect_len = 0; - CC->redirect_alloc = SIZ; - CtdlOutputPreLoadedMsg(msg, MT_RFC822, 0, 0, 1); - rfc822 = CC->redirect_buffer; - rfc822_len = CC->redirect_len; - CC->redirect_buffer = NULL; - CC->redirect_len = 0; - CC->redirect_alloc = 0; + CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); + CtdlOutputPreLoadedMsg(msg, MT_RFC822, 0, 0, 1, SUPPRESS_ENV_TO); + rfc822_len = StrLength(CC->redirect_buffer); + rfc822 = pch = SmashStrBuf(&CC->redirect_buffer); ptr = rfc822; - while (ptr = memreadline(ptr, buf, sizeof buf), *ptr != 0) { + do { + ptr = cmemreadline(ptr, buf, sizeof buf); ++lines; if ((IsEmptyStr(buf)) && (rfc822_body == NULL)) { rfc822_body = ptr; } - } + } while (*ptr != 0); rfc822_headers_len = rfc822_body - rfc822; rfc822_body_len = rfc822_len - rfc822_headers_len; - free(rfc822); + free(pch); - cprintf("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" " - "(\"CHARSET\" \"US-ASCII\") NIL NIL " - "\"7BIT\" " SIZE_T_FMT " %d)", rfc822_body_len, lines); + IAPuts("BODYSTRUCTURE (\"TEXT\" \"PLAIN\" " + "(\"CHARSET\" \"US-ASCII\") NIL NIL " + "\"7BIT\" "); + IAPrintf(SIZE_T_FMT " %d)", rfc822_body_len, lines); return; } /* For messages already stored in RFC822 format, we have to parse. */ - cprintf("BODYSTRUCTURE "); - mime_parser(msg->cm_fields['M'], - NULL, - *imap_fetch_bodystructure_part, /* part */ - *imap_fetch_bodystructure_pre, /* pre-multi */ - *imap_fetch_bodystructure_post, /* post-multi */ - NULL, - 1); /* don't decode -- we want it as-is */ + IAPuts("BODYSTRUCTURE "); + mime_parser(CM_RANGE(msg, eMesageText), + *imap_fetch_bodystructure_part, /* part */ + *imap_fetch_bodystructure_pre, /* pre-multi */ + *imap_fetch_bodystructure_post, /* post-multi */ + NULL, + 1); /* don't decode -- we want it as-is */ } @@ -958,88 +1007,89 @@ void imap_fetch_bodystructure (long msgnum, char *item, * imap_do_fetch() calls imap_do_fetch_msg() to output the data of an * individual message, once it has been selected for output. */ -void imap_do_fetch_msg(int seq, int num_items, char **itemlist) { +void imap_do_fetch_msg(int seq, citimap_command *Cmd) { int i; + citimap *Imap = IMAP; struct CtdlMessage *msg = NULL; int body_loaded = 0; /* Don't attempt to fetch bogus messages or UID's */ if (seq < 1) return; - if (IMAP->msgids[seq-1] < 1L) return; + if (Imap->msgids[seq-1] < 1L) return; buffer_output(); - cprintf("* %d FETCH (", seq); + IAPrintf("* %d FETCH (", seq); - for (i=0; inum_parms; ++i) { /* Fetchable without going to the message store at all */ - if (!strcasecmp(itemlist[i], "UID")) { + if (!strcasecmp(Cmd->Params[i].Key, "UID")) { imap_fetch_uid(seq); } - else if (!strcasecmp(itemlist[i], "FLAGS")) { + else if (!strcasecmp(Cmd->Params[i].Key, "FLAGS")) { imap_fetch_flags(seq-1); } /* Potentially fetchable from cache, if the client requests * stuff from the same message several times in a row. */ - else if (!strcasecmp(itemlist[i], "RFC822")) { - imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]); + else if (!strcasecmp(Cmd->Params[i].Key, "RFC822")) { + imap_fetch_rfc822(Imap->msgids[seq-1], Cmd->Params[i].Key); } - else if (!strcasecmp(itemlist[i], "RFC822.HEADER")) { - imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]); + else if (!strcasecmp(Cmd->Params[i].Key, "RFC822.HEADER")) { + imap_fetch_rfc822(Imap->msgids[seq-1], Cmd->Params[i].Key); } - else if (!strcasecmp(itemlist[i], "RFC822.SIZE")) { - imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]); + else if (!strcasecmp(Cmd->Params[i].Key, "RFC822.SIZE")) { + imap_fetch_rfc822(Imap->msgids[seq-1], Cmd->Params[i].Key); } - else if (!strcasecmp(itemlist[i], "RFC822.TEXT")) { - imap_fetch_rfc822(IMAP->msgids[seq-1], itemlist[i]); + else if (!strcasecmp(Cmd->Params[i].Key, "RFC822.TEXT")) { + imap_fetch_rfc822(Imap->msgids[seq-1], Cmd->Params[i].Key); } /* BODY fetches do their own fetching and caching too. */ - else if (!strncasecmp(itemlist[i], "BODY[", 5)) { - imap_fetch_body(IMAP->msgids[seq-1], itemlist[i], 0); + else if (!strncasecmp(Cmd->Params[i].Key, "BODY[", 5)) { + imap_fetch_body(Imap->msgids[seq-1], Cmd->Params[i], 0); } - else if (!strncasecmp(itemlist[i], "BODY.PEEK[", 10)) { - imap_fetch_body(IMAP->msgids[seq-1], itemlist[i], 1); + else if (!strncasecmp(Cmd->Params[i].Key, "BODY.PEEK[", 10)) { + imap_fetch_body(Imap->msgids[seq-1], Cmd->Params[i], 1); } /* Otherwise, load the message into memory. */ - else if (!strcasecmp(itemlist[i], "BODYSTRUCTURE")) { + else if (!strcasecmp(Cmd->Params[i].Key, "BODYSTRUCTURE")) { if ((msg != NULL) && (!body_loaded)) { - CtdlFreeMessage(msg); /* need the whole thing */ + CM_Free(msg); /* need the whole thing */ msg = NULL; } if (msg == NULL) { - msg = CtdlFetchMessage(IMAP->msgids[seq-1], 1); + msg = CtdlFetchMessage(Imap->msgids[seq-1], 1, 1); body_loaded = 1; } - imap_fetch_bodystructure(IMAP->msgids[seq-1], - itemlist[i], msg); + imap_fetch_bodystructure(Imap->msgids[seq-1], + Cmd->Params[i].Key, msg); } - else if (!strcasecmp(itemlist[i], "ENVELOPE")) { + else if (!strcasecmp(Cmd->Params[i].Key, "ENVELOPE")) { if (msg == NULL) { - msg = CtdlFetchMessage(IMAP->msgids[seq-1], 0); + msg = CtdlFetchMessage(Imap->msgids[seq-1], 0, 1); body_loaded = 0; } imap_fetch_envelope(msg); } - else if (!strcasecmp(itemlist[i], "INTERNALDATE")) { + else if (!strcasecmp(Cmd->Params[i].Key, "INTERNALDATE")) { if (msg == NULL) { - msg = CtdlFetchMessage(IMAP->msgids[seq-1], 0); + msg = CtdlFetchMessage(Imap->msgids[seq-1], 0, 1); body_loaded = 0; } imap_fetch_internaldate(msg); } - if (i != num_items-1) cprintf(" "); + if (i != Cmd->num_parms-1) IAPuts(" "); } - cprintf(")\r\n"); + IAPuts(")\r\n"); unbuffer_output(); if (msg != NULL) { - CtdlFreeMessage(msg); + CM_Free(msg); } } @@ -1049,11 +1099,31 @@ void imap_do_fetch_msg(int seq, int num_items, char **itemlist) { * imap_fetch() calls imap_do_fetch() to do its actual work, once it's * validated and boiled down the request a bit. */ -void imap_do_fetch(int num_items, char **itemlist) { +void imap_do_fetch(citimap_command *Cmd) { + citimap *Imap = IMAP; int i; +#if 0 +/* debug output the parsed vector */ + { + int i; + syslog(LOG_DEBUG, "imap: ----- %ld params", Cmd->num_parms); + + for (i=0; i < Cmd->num_parms; i++) { + if (Cmd->Params[i].len != strlen(Cmd->Params[i].Key)) + syslog(LOG_DEBUG, "imap: *********** %ld != %ld : %s", + Cmd->Params[i].len, + strlen(Cmd->Params[i].Key), + Cmd->Params[i].Key); + else + syslog(LOG_DEBUG, "imap: %ld : %s", + Cmd->Params[i].len, + Cmd->Params[i].Key); + }} - if (IMAP->num_msgs > 0) { - for (i = 0; i < IMAP->num_msgs; ++i) { +#endif + + if (Imap->num_msgs > 0) { + for (i = 0; i < Imap->num_msgs; ++i) { /* Abort the fetch loop if the session breaks. * This is important for users who keep mailboxes @@ -1063,8 +1133,8 @@ void imap_do_fetch(int num_items, char **itemlist) { if (CC->kill_me) return; /* Get any message marked for fetch. */ - if (IMAP->flags[i] & IMAP_SELECTED) { - imap_do_fetch_msg(i+1, num_items, itemlist); + if (Imap->flags[i] & IMAP_SELECTED) { + imap_do_fetch_msg(i+1, Cmd); } } } @@ -1077,22 +1147,25 @@ void imap_do_fetch(int num_items, char **itemlist) { * Note that this function *only* looks at the beginning of the string. It * is not a generic search-and-replace function. */ -void imap_macro_replace(char *str, char *find, char *replace) { - char holdbuf[SIZ]; - int findlen; - - findlen = strlen(find); - - if (!strncasecmp(str, find, findlen)) { - if (str[findlen]==' ') { - strcpy(holdbuf, &str[findlen+1]); - strcpy(str, replace); - strcat(str, " "); - strcat(str, holdbuf); +void imap_macro_replace(StrBuf *Buf, long where, + StrBuf *TmpBuf, + char *find, long findlen, + char *replace, long replacelen) +{ + + if (StrLength(Buf) - where > findlen) + return; + + if (!strncasecmp(ChrPtr(Buf) + where, find, findlen)) { + if (ChrPtr(Buf)[where + findlen] == ' ') { + StrBufPlain(TmpBuf, replace, replacelen); + StrBufAppendBufPlain(TmpBuf, HKEY(" "), 0); + StrBufReplaceToken(Buf, where, findlen, + SKEY(TmpBuf)); } - if (str[findlen]==0) { - strcpy(holdbuf, &str[findlen+1]); - strcpy(str, replace); + if (where + findlen == StrLength(Buf)) { + StrBufReplaceToken(Buf, where, findlen, + replace, replacelen); } } } @@ -1104,39 +1177,46 @@ void imap_macro_replace(char *str, char *find, char *replace) { * (What the heck are macros doing in a wire protocol? Are we trying to save * the computer at the other end the trouble of typing a lot of characters?) */ -void imap_handle_macros(char *str) { - int i; +void imap_handle_macros(citimap_command *Cmd) { + long i; int nest = 0; - - for (i=0; str[i]; ++i) { - if (str[i]=='(') ++nest; - if (str[i]=='[') ++nest; - if (str[i]=='<') ++nest; - if (str[i]=='{') ++nest; - if (str[i]==')') --nest; - if (str[i]==']') --nest; - if (str[i]=='>') --nest; - if (str[i]=='}') --nest; + StrBuf *Tmp = NewStrBuf(); + + for (i=0; i < StrLength(Cmd->CmdBuf); ++i) { + char ch = ChrPtr(Cmd->CmdBuf)[i]; + if ((ch=='(') || + (ch=='[') || + (ch=='<') || + (ch=='{')) ++nest; + else if ((ch==')') || + (ch==']') || + (ch=='>') || + (ch=='}')) --nest; if (nest <= 0) { - imap_macro_replace(&str[i], - "ALL", - "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE" + imap_macro_replace(Cmd->CmdBuf, i, + Tmp, + HKEY("ALL"), + HKEY("FLAGS INTERNALDATE RFC822.SIZE ENVELOPE") ); - imap_macro_replace(&str[i], - "BODY", - "BODYSTRUCTURE" + imap_macro_replace(Cmd->CmdBuf, i, + Tmp, + HKEY("BODY"), + HKEY("BODYSTRUCTURE") ); - imap_macro_replace(&str[i], - "FAST", - "FLAGS INTERNALDATE RFC822.SIZE" + imap_macro_replace(Cmd->CmdBuf, i, + Tmp, + HKEY("FAST"), + HKEY("FLAGS INTERNALDATE RFC822.SIZE") ); - imap_macro_replace(&str[i], - "FULL", - "FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY" + imap_macro_replace(Cmd->CmdBuf, i, + Tmp, + HKEY("FULL"), + HKEY("FLAGS INTERNALDATE RFC822.SIZE ENVELOPE BODY") ); } } + FreeStrBuf(&Tmp); } @@ -1146,56 +1226,83 @@ void imap_handle_macros(char *str) { * NOTE: this function alters the string it is fed, and uses it as a buffer * to hold the data for the pointers it returns. */ -int imap_extract_data_items(char **argv, char *items) { - int num_items = 0; +int imap_extract_data_items(citimap_command *Cmd) +{ + int nArgs; int nest = 0; - int i; - char *start; - long initial_len; + const char *pch, *end; /* Convert all whitespace to ordinary space characters. */ - for (i=0; items[i]; ++i) { - if (isspace(items[i])) items[i]=' '; + pch = ChrPtr(Cmd->CmdBuf); + end = pch + StrLength(Cmd->CmdBuf); + + while (pch < end) + { + if (isspace(*pch)) + StrBufPeek(Cmd->CmdBuf, pch, 0, ' '); + pch++; } /* Strip leading and trailing whitespace, then strip leading and * trailing parentheses if it's a list */ - striplt(items); - if ( (items[0]=='(') && (items[strlen(items)-1]==')') ) { - items[strlen(items)-1] = 0; - strcpy(items, &items[1]); - striplt(items); + StrBufTrim(Cmd->CmdBuf); + pch = ChrPtr(Cmd->CmdBuf); + if ( (pch[0]=='(') && + (pch[StrLength(Cmd->CmdBuf)-1]==')') ) + { + StrBufCutRight(Cmd->CmdBuf, 1); + StrBufCutLeft(Cmd->CmdBuf, 1); + StrBufTrim(Cmd->CmdBuf); } /* Parse any macro data items */ - imap_handle_macros(items); + imap_handle_macros(Cmd); /* * Now break out the data items. We throw in one trailing space in * order to avoid having to break out the last one manually. */ - strcat(items, " "); - start = items; - initial_len = strlen(items); - for (i=0; i') --nest; - if (items[i]=='}') --nest; - - if (nest <= 0) if (items[i]==' ') { - items[i] = 0; - argv[num_items++] = start; - start = &items[i+1]; + nArgs = StrLength(Cmd->CmdBuf) / 10 + 10; + nArgs = CmdAdjust(Cmd, nArgs, 0); + Cmd->num_parms = 0; + Cmd->Params[Cmd->num_parms].Key = pch = ChrPtr(Cmd->CmdBuf); + end = Cmd->Params[Cmd->num_parms].Key + StrLength(Cmd->CmdBuf); + + while (pch < end) + { + if ((*pch=='(') || + (*pch=='[') || + (*pch=='<') || + (*pch=='{')) + ++nest; + + else if ((*pch==')') || + (*pch==']') || + (*pch=='>') || + (*pch=='}')) + --nest; + + if ((nest <= 0) && (*pch==' ')) { + StrBufPeek(Cmd->CmdBuf, pch, 0, '\0'); + Cmd->Params[Cmd->num_parms].len = + pch - Cmd->Params[Cmd->num_parms].Key; + + if (Cmd->num_parms + 1 >= Cmd->avail_parms) { + nArgs = CmdAdjust(Cmd, nArgs * 2, 1); + } + Cmd->num_parms++; + Cmd->Params[Cmd->num_parms].Key = ++pch; } - } + else if (pch + 1 == end) { + Cmd->Params[Cmd->num_parms].len = + pch - Cmd->Params[Cmd->num_parms].Key + 1; - return(num_items); + Cmd->num_parms++; + } + pch ++; + } + return Cmd->num_parms; } @@ -1215,14 +1322,14 @@ int imap_extract_data_items(char **argv, char *items) { * * Set is_uid to 1 to fetch by UID instead of sequence number. */ -void imap_pick_range(char *supplied_range, int is_uid) { +void imap_pick_range(const char *supplied_range, int is_uid) { + citimap *Imap = IMAP; int i; int num_sets; int s; char setstr[SIZ], lostr[SIZ], histr[SIZ]; long lo, hi; char actual_range[SIZ]; - struct citimap *Imap; /* * Handle the "ALL" macro @@ -1234,7 +1341,6 @@ void imap_pick_range(char *supplied_range, int is_uid) { safestrncpy(actual_range, supplied_range, sizeof actual_range); } - Imap = IMAP; /* * Clear out the IMAP_SELECTED flags for all messages. */ @@ -1275,7 +1381,6 @@ void imap_pick_range(char *supplied_range, int is_uid) { } } } - } @@ -1283,61 +1388,63 @@ void imap_pick_range(char *supplied_range, int is_uid) { /* * This function is called by the main command loop. */ -void imap_fetch(int num_parms, char *parms[]) { - char items[SIZ]; - char *itemlist[512]; +void imap_fetch(int num_parms, ConstStr *Params) { + citimap_command Cmd; int num_items; - int i; - + if (num_parms < 4) { - cprintf("%s BAD invalid parameters\r\n", parms[0]); + IReply("BAD invalid parameters"); return; } - imap_pick_range(parms[2], 0); + imap_pick_range(Params[2].Key, 0); - strcpy(items, ""); - for (i=3; iCmd.CmdBuf)); + MakeStringOf(Cmd.CmdBuf, 3); - num_items = imap_extract_data_items(itemlist, items); + num_items = imap_extract_data_items(&Cmd); if (num_items < 1) { - cprintf("%s BAD invalid data item list\r\n", parms[0]); + IReply("BAD invalid data item list"); + FreeStrBuf(&Cmd.CmdBuf); + free(Cmd.Params); return; } - imap_do_fetch(num_items, itemlist); - cprintf("%s OK FETCH completed\r\n", parms[0]); + imap_do_fetch(&Cmd); + IReply("OK FETCH completed"); + FreeStrBuf(&Cmd.CmdBuf); + free(Cmd.Params); } /* * This function is called by the main command loop. */ -void imap_uidfetch(int num_parms, char *parms[]) { - char items[SIZ]; - char *itemlist[512]; +void imap_uidfetch(int num_parms, ConstStr *Params) { + citimap_command Cmd; int num_items; int i; int have_uid_item = 0; if (num_parms < 5) { - cprintf("%s BAD invalid parameters\r\n", parms[0]); + IReply("BAD invalid parameters"); return; } - imap_pick_range(parms[3], 1); + imap_pick_range(Params[3].Key, 1); - strcpy(items, ""); - for (i=4; iCmd.CmdBuf)); - num_items = imap_extract_data_items(itemlist, items); + MakeStringOf(Cmd.CmdBuf, 4); +#if 0 + syslog(LOG_DEBUG, "imap: -------%s--------", ChrPtr(Cmd.CmdBuf)); +#endif + num_items = imap_extract_data_items(&Cmd); if (num_items < 1) { - cprintf("%s BAD invalid data item list\r\n", parms[0]); + IReply("BAD invalid data item list"); + FreeStrBuf(&Cmd.CmdBuf); + free(Cmd.Params); return; } @@ -1345,16 +1452,23 @@ void imap_uidfetch(int num_parms, char *parms[]) { * (at the beginning) because this is a UID FETCH command */ for (i=0; i= Cmd.avail_parms) + CmdAdjust(&Cmd, Cmd.avail_parms + 1, 1); + memmove(&Cmd.Params[1], + &Cmd.Params[0], + sizeof(ConstStr) * Cmd.num_parms); + + Cmd.num_parms++; + Cmd.Params[0] = (ConstStr){HKEY("UID")}; } - imap_do_fetch(num_items, itemlist); - cprintf("%s OK UID FETCH completed\r\n", parms[0]); + imap_do_fetch(&Cmd); + IReply("OK UID FETCH completed"); + FreeStrBuf(&Cmd.CmdBuf); + free(Cmd.Params); }