X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fimap%2Fimap_tools.c;h=e32320d000beabe5286d46f08e4e256b7b8454a4;hb=2b80e75820618944e1c75b9c01aeeefc8b6b0c81;hp=b73bb8b8e47bea5490b6f5908c12c9b3ac957bc9;hpb=b425dda0a48b4a3e24a9f22d25e864d42679feee;p=citadel.git diff --git a/citadel/modules/imap/imap_tools.c b/citadel/modules/imap/imap_tools.c index b73bb8b8e..e32320d00 100644 --- a/citadel/modules/imap/imap_tools.c +++ b/citadel/modules/imap/imap_tools.c @@ -1,35 +1,45 @@ /* - * $Id$ - * * Utility functions for the IMAP module. * - * Note: most of the UTF7 and UTF8 handling in here was lifted from Evolution. + * Copyright (c) 2001-2009 by the citadel.org team and others, except for + * most of the UTF7 and UTF8 handling code which was lifted from Evolution. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define SHOW_ME_VAPPEND_PRINTF #include #include #include #include #include +#include +#include #include "citadel.h" #include "sysdep_decls.h" -#include "tools.h" -#include "room_ops.h" #include "internet_addressing.h" +#include "serv_imap.h" #include "imap_tools.h" - - -#ifndef HAVE_SNPRINTF -#include "snprintf.h" -#endif +#include "ctdl_module.h" /* String handling helpers */ -/* This code uses some pretty narsty string manipulation. To make everything +/* This code uses some pretty nasty string manipulation. To make everything * manageable, we use this semi-high-level string manipulation API. Strings are - * always \0-terminated, despite the fact that we keep track of the size. */ - + * always \0-terminated, despite the fact that we keep track of the size. + */ struct string { char* buffer; int maxsize; @@ -70,7 +80,7 @@ static void string_append_sn(struct string* s, char* p, int len) static void string_append_c(struct string* s, int c) { - char buf[5]; + char UmlChar[5]; int len = 0; /* Don't do anything if there's no room. */ @@ -88,27 +98,27 @@ static void string_append_c(struct string* s, int c) } else if (c <= 0x7FF) { - buf[0] = 0xC0 | (c >> 6); - buf[1] = 0x80 | (c & 0x3F); + UmlChar[0] = 0xC0 | (c >> 6); + UmlChar[1] = 0x80 | (c & 0x3F); len = 2; } else if (c <= 0xFFFF) { - buf[0] = 0xE0 | (c >> 12); - buf[1] = 0x80 | ((c >> 6) & 0x3f); - buf[2] = 0x80 | (c & 0x3f); + UmlChar[0] = 0xE0 | (c >> 12); + UmlChar[1] = 0x80 | ((c >> 6) & 0x3f); + UmlChar[2] = 0x80 | (c & 0x3f); len = 3; } else { - buf[0] = 0xf0 | c >> 18; - buf[1] = 0x80 | ((c >> 12) & 0x3f); - buf[2] = 0x80 | ((c >> 6) & 0x3f); - buf[3] = 0x80 | (c & 0x3f); + UmlChar[0] = 0xf0 | c >> 18; + UmlChar[1] = 0x80 | ((c >> 12) & 0x3f); + UmlChar[2] = 0x80 | ((c >> 6) & 0x3f); + UmlChar[3] = 0x80 | (c & 0x3f); len = 4; } - string_append_sn(s, buf, len); + string_append_sn(s, UmlChar, len); } /* Reads a UTF8 character from a char*, advancing the pointer. */ @@ -211,7 +221,7 @@ static char* toimap(char* destp, char* destend, char* src) *destp = 0; string_init(&dest, destp, destend-destp); - /* lprintf(CTDL_DEBUG, "toimap %s\r\n", src); */ + /* IMAP_syslog(LOG_DEBUG, "toimap %s", src); */ for (;;) { @@ -277,17 +287,17 @@ static char* toimap(char* destp, char* destend, char* src) if (state == 1) utf7_closeb64(&dest, v, i); - /* lprintf(CTDL_DEBUG, " -> %s\r\n", destp); */ + /* IMAP_syslog(LOG_DEBUG, " -> %s", destp); */ return string_end(&dest); } /* Convert from an IMAP-safe name back into a Citadel name. Returns the end of the destination. */ static int cfrommap(int c); -static char* fromimap(char* destp, char* destend, char* src) +static char* fromimap(char* destp, char* destend, const char* src) { struct string dest; - unsigned char *p = (unsigned char*) src; + unsigned const char *p = (unsigned const char*) src; int v = 0; int i = 0; int state = 0; @@ -295,7 +305,7 @@ static char* fromimap(char* destp, char* destend, char* src) *destp = 0; string_init(&dest, destp, destend-destp); - /* lprintf(CTDL_DEBUG, "fromimap %s\r\n", src); */ + /* IMAP_syslog(LOG_DEBUG, "fromimap %s", src); */ do { c = *p++; @@ -353,7 +363,7 @@ static char* fromimap(char* destp, char* destend, char* src) } } while (c != '\0'); - /* lprintf(CTDL_DEBUG, " -> %s\r\n", destp); */ + /* IMAP_syslog(LOG_DEBUG, " -> %s", destp); */ return string_end(&dest); } @@ -369,102 +379,185 @@ static int cfrommap(int c) return c; } -/* Output a string to the IMAP client, either as a literal or quoted. - * (We do a literal if it has any double-quotes or backslashes.) */ -void imap_strout(char *buf) + + +/* Break a command down into tokens, unquoting any escaped characters. */ + +void MakeStringOf(StrBuf *Buf, int skip) { int i; - int is_literal = 0; - long len; + citimap_command *Cmd = &IMAP->Cmd; - if (buf == NULL) { /* yeah, we handle this */ - cprintf("NIL"); - return; + for (i=skip; inum_parms; ++i) { + StrBufAppendBufPlain(Buf, Cmd->Params[i].Key, Cmd->Params[i].len, 0); + if (i < (Cmd->num_parms-1)) StrBufAppendBufPlain(Buf, HKEY(" "), 0); } +} - len = strlen(buf); - for (i = 0; i < len; ++i) { - if ((buf[i] == '\"') || (buf[i] == '\\')) - is_literal = 1; + +void TokenCutRight(citimap_command *Cmd, + ConstStr *CutMe, + int n) +{ + const char *CutAt; + + if (CutMe->len < n) { + CutAt = CutMe->Key; + CutMe->len = 0; + } + else { + CutAt = CutMe->Key + CutMe->len - n; + CutMe->len -= n; } + StrBufPeek(Cmd->CmdBuf, CutAt, -1, '\0'); +} - if (is_literal) { - cprintf("{%ld}\r\n%s", len, buf); - } else { - cprintf("\"%s\"", buf); +void TokenCutLeft(citimap_command *Cmd, + ConstStr *CutMe, + int n) +{ + if (CutMe->len < n) { + CutMe->Key += CutMe->len; + CutMe->len = 0; + } + else { + CutMe->Key += n; + CutMe->len -= n; } } -/* Break a command down into tokens, unquoting any escaped characters. */ -int imap_parameterize(char** args, char* in) + +int CmdAdjust(citimap_command *Cmd, + int nArgs, + int Realloc) { - char* out = in; - int num = 0; + ConstStr *Params; + if (nArgs > Cmd->avail_parms) { + Params = (ConstStr*) malloc(sizeof(ConstStr) * nArgs); + if (Realloc) { + memcpy(Params, + Cmd->Params, + sizeof(ConstStr) * Cmd->avail_parms); + + memset(Cmd->Params + + sizeof(ConstStr) * Cmd->avail_parms, + 0, + sizeof(ConstStr) * nArgs - + sizeof(ConstStr) * Cmd->avail_parms + ); + } + else { + Cmd->num_parms = 0; + memset(Params, 0, + sizeof(ConstStr) * nArgs); + } + Cmd->avail_parms = nArgs; + if (Cmd->Params != NULL) + free (Cmd->Params); + Cmd->Params = Params; + } + else { + if (!Realloc) { + memset(Cmd->Params, + 0, + sizeof(ConstStr) * Cmd->avail_parms); + Cmd->num_parms = 0; + } + } + return Cmd->avail_parms; +} - for (;;) +int imap_parameterize(citimap_command *Cmd) +{ + int nArgs; + const char *In, *End; + + In = ChrPtr(Cmd->CmdBuf); + End = In + StrLength(Cmd->CmdBuf); + + /* we start with 10 chars per arg, maybe we need to realloc later. */ + nArgs = StrLength(Cmd->CmdBuf) / 10 + 10; + nArgs = CmdAdjust(Cmd, nArgs, 0); + while (In < End) { /* Skip whitespace. */ - - while (isspace(*in)) - in++; - if (*in == 0) + while (isspace(*In)) + In++; + if (*In == '\0') break; /* Found the start of a token. */ - args[num++] = out; + Cmd->Params[Cmd->num_parms].Key = In; /* Read in the token. */ for (;;) { - int c = *in++; - if (isspace(c)) + if (isspace(*In)) break; - if (c == '\"') + if (*In == '\"') { /* Found a quoted section. */ + Cmd->Params[Cmd->num_parms].Key++; + //In++; for (;;) { - c = *in++; - if (c == '\"') + In++; + if (*In == '\"') { + StrBufPeek(Cmd->CmdBuf, In, -1, '\0'); break; - else if (c == '\\') - c = *in++; - - *out++ = c; - if (c == 0) - return num; + } + else if (*In == '\\') + In++; + + if (*In == '\0') { + Cmd->Params[Cmd->num_parms].len = + In - Cmd->Params[Cmd->num_parms].Key; + Cmd->num_parms++; + return Cmd->num_parms; + } } + break; } - else if (c == '\\') + else if (*In == '\\') { - c = *in++; - *out++ = c; + In++; } - else - *out++ = c; - if (c == 0) - return num; + if (*In == '\0') { + Cmd->Params[Cmd->num_parms].len = + In - Cmd->Params[Cmd->num_parms].Key; + Cmd->num_parms++; + return Cmd->num_parms; + } + In++; } - *out++ = '\0'; + StrBufPeek(Cmd->CmdBuf, In, -1, '\0'); + Cmd->Params[Cmd->num_parms].len = + In - Cmd->Params[Cmd->num_parms].Key; + if (Cmd->num_parms + 1 >= Cmd->avail_parms) { + nArgs = CmdAdjust(Cmd, nArgs * 2, 1); + } + Cmd->num_parms ++; + In++; } - - return num; + return Cmd->num_parms; } + /* Convert a struct ctdlroom to an IMAP-compatible mailbox name. */ -void imap_mailboxname(char *buf, int bufsize, struct ctdlroom *qrbuf) +long imap_mailboxname(char *buf, int bufsize, struct ctdlroom *qrbuf) { char* bufend = buf+bufsize; struct floor *fl; char* p = buf; + const char *pend; /* For mailboxes, just do it straight. * Do the Cyrus-compatible thing: all private folders are @@ -473,24 +566,29 @@ void imap_mailboxname(char *buf, int bufsize, struct ctdlroom *qrbuf) if (qrbuf->QRflags & QR_MAILBOX) { if (strcasecmp(qrbuf->QRname+11, MAILROOM) == 0) - p = toimap(p, bufend, "INBOX"); + { + pend = toimap(p, bufend, "INBOX"); + return pend - p; + } else { p = toimap(p, bufend, "INBOX"); if (p < bufend) *p++ = '/'; - p = toimap(p, bufend, qrbuf->QRname+11); + pend = toimap(p, bufend, qrbuf->QRname+11); + return pend - p; } } else { /* Otherwise, prefix the floor name as a "public folders" moniker. */ - fl = cgetfloor(qrbuf->QRfloor); + fl = CtdlGetCachedFloor(qrbuf->QRfloor); p = toimap(p, bufend, fl->f_name); if (p < bufend) *p++ = '/'; - p = toimap(p, bufend, qrbuf->QRname); + pend = toimap(p, bufend, qrbuf->QRname); + return pend - p; } } @@ -506,10 +604,11 @@ void imap_mailboxname(char *buf, int bufsize, struct ctdlroom *qrbuf) * */ -int imap_roomname(char *rbuf, int bufsize, char *foldername) +int imap_roomname(char *rbuf, int bufsize, const char *foldername) { + struct CitContext *CCC = CC; int levels; - char floorname[256]; + char floorname[ROOMNAMELEN*2]; char roomname[ROOMNAMELEN]; int i; struct floor *fl; @@ -552,16 +651,18 @@ int imap_roomname(char *rbuf, int bufsize, char *foldername) levels = num_tokens(rbuf, FDELIM); if (levels > 1) { + long len; /* Extract the main room name. */ - extract_token(floorname, rbuf, 0, FDELIM, sizeof floorname); - strcpy(roomname, &rbuf[strlen(floorname)+1]); + len = extract_token(floorname, rbuf, 0, FDELIM, sizeof floorname); + if (len < 0) len = 0; + safestrncpy(roomname, &rbuf[len + 1], sizeof(roomname)); /* Try and find it on any floor. */ for (i = 0; i < MAXFLOORS; ++i) { - fl = cgetfloor(i); + fl = CtdlGetCachedFloor(i); if (fl->f_flags & F_INUSE) { if (strcasecmp(floorname, fl->f_name) == 0) @@ -582,37 +683,10 @@ int imap_roomname(char *rbuf, int bufsize, char *foldername) ret = (0 | IR_MAILBOX); exit: - lprintf(CTDL_DEBUG, "(That translates to \"%s\")\n", rbuf); + IMAP_syslog(LOG_DEBUG, "(That translates to \"%s\")", rbuf); return(ret); } -/* - * Output a struct internet_address_list in the form an IMAP client wants - */ -void imap_ial_out(struct internet_address_list *ialist) -{ - struct internet_address_list *iptr; - - if (ialist == NULL) { - cprintf("NIL"); - return; - } - cprintf("("); - - for (iptr = ialist; iptr != NULL; iptr = iptr->next) { - cprintf("("); - imap_strout(iptr->ial_name); - cprintf(" NIL "); - imap_strout(iptr->ial_user); - cprintf(" "); - imap_strout(iptr->ial_node); - cprintf(")"); - } - - cprintf(")"); -} - - /* * Determine whether the supplied string is a valid message set. @@ -620,7 +694,7 @@ void imap_ial_out(struct internet_address_list *ialist) * return 1 for a valid message set. If any other character is found, * return 0. */ -int imap_is_message_set(char *buf) +int imap_is_message_set(const char *buf) { int i; @@ -632,7 +706,7 @@ int imap_is_message_set(char *buf) if (!strcasecmp(buf, "ALL")) return (1); /* macro? why? */ - for (i = 0; !IsEmptyStr(&buf[i]); ++i) { /* now start the scan */ + for (i = 0; buf[i]; ++i) { /* now start the scan */ if ( (!isdigit(buf[i])) && (buf[i] != ':') @@ -664,19 +738,30 @@ static int do_imap_match(const char *supplied_text, const char *supplied_p) { int matched, i; char lcase_text[SIZ], lcase_p[SIZ]; - char *text = lcase_text; - char *p = lcase_p; - + char *text; + char *p; + /* Copy both strings and lowercase them, in order to * make this entire operation case-insensitive. */ - for (i=0; i<=strlen(supplied_text); ++i) + for (i=0; + ((supplied_text[i] != '\0') && + (i < sizeof(lcase_text))); + ++i) lcase_text[i] = tolower(supplied_text[i]); - for (i=0; i<=strlen(supplied_p); ++i) - p[i] = tolower(supplied_p[i]); + lcase_text[i] = '\0'; + + for (i=0; + ((supplied_p[i] != '\0') && + (i < sizeof(lcase_p))); + ++i) + lcase_p[i] = tolower(supplied_p[i]); + lcase_p[i] = '\0'; /* Start matching */ - for (; *p; text++, p++) { + for (p = lcase_p, text = lcase_text; + !IsEmptyStr(p) && !IsEmptyStr(text); + text++, p++) { if ((*text == '\0') && (*p != '*') && (*p != '%')) { return WILDMAT_ABORT; } @@ -706,7 +791,8 @@ star: } return WILDMAT_ABORT; case '%': - while (++p, ((*p == '*') || (*p == '%'))) { + while (++p, (!IsEmptyStr(p) && ((*p == '*') || (*p == '%')))) + { /* Consecutive %'s act just like one, but even * a single star makes the sequence act like * one star, instead. @@ -721,7 +807,7 @@ star: * Trailing % matches everything * without a delimiter. */ - while (*text) { + while (!IsEmptyStr(text)) { if (*text == WILDMAT_DELIM) { return WILDMAT_FALSE; } @@ -729,7 +815,10 @@ star: } return WILDMAT_TRUE; } - while (*text && (*(text - 1) != WILDMAT_DELIM)) { + while (!IsEmptyStr(text) && + /* make shure texst - 1 isn't before lcase_p */ + ((text == lcase_text) || (*(text - 1) != WILDMAT_DELIM))) + { if ((matched = do_imap_match(text++, p)) != WILDMAT_FALSE) { return matched; @@ -739,7 +828,8 @@ star: } } - return (*text == '\0'); + if ((*text == '\0') && (*p == '\0')) return WILDMAT_TRUE; + else return WILDMAT_FALSE; } @@ -748,7 +838,7 @@ star: * Support function for mailbox pattern name matching in LIST and LSUB * Returns nonzero if the supplied mailbox name matches the supplied pattern. */ -int imap_mailbox_matches_pattern(char *pattern, char *mailboxname) +int imap_mailbox_matches_pattern(const char *pattern, char *mailboxname) { /* handle just-star case quickly */ if ((pattern[0] == '*') && (pattern[1] == '\0')) { @@ -763,7 +853,7 @@ int imap_mailbox_matches_pattern(char *pattern, char *mailboxname) * Compare an IMAP date string (date only, no time) to the date found in * a Unix timestamp. */ -int imap_datecmp(char *datestr, time_t msgtime) { +int imap_datecmp(const char *datestr, time_t msgtime) { char daystr[256]; char monthstr[256]; char yearstr[256]; @@ -772,6 +862,10 @@ int imap_datecmp(char *datestr, time_t msgtime) { int msgday, msgmonth, msgyear; struct tm msgtm; + char *imap_datecmp_ascmonths[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + if (datestr == NULL) return(0); /* Expecting a date in the form dd-Mmm-yyyy */ @@ -783,7 +877,7 @@ int imap_datecmp(char *datestr, time_t msgtime) { year = atoi(yearstr); month = 0; for (i=0; i<12; ++i) { - if (!strcasecmp(monthstr, ascmonths[i])) { + if (!strcasecmp(monthstr, imap_datecmp_ascmonths[i])) { month = i; } } @@ -808,3 +902,101 @@ int imap_datecmp(char *datestr, time_t msgtime) { return(0); } + + + + +void IAPrintf(const char *Format, ...) +{ + va_list arg_ptr; + + va_start(arg_ptr, Format); + StrBufVAppendPrintf(IMAP->Reply, Format, arg_ptr); + va_end(arg_ptr); +} + +void iaputs(const char *Str, long Len) +{ + StrBufAppendBufPlain(IMAP->Reply, Str, Len, 0); +} + +void ireply(const char *Msg, long len) +{ + citimap *Imap = IMAP; + + StrBufAppendBufPlain(Imap->Reply, + CKEY(Imap->Cmd.Params[0]), 0); + StrBufAppendBufPlain(Imap->Reply, + HKEY(" "), 0); + StrBufAppendBufPlain(Imap->Reply, + Msg, len, 0); + + StrBufAppendBufPlain(Imap->Reply, + HKEY("\r\n"), 0); + +} + +void IReplyPrintf(const char *Format, ...) +{ + citimap *Imap = IMAP; + va_list arg_ptr; + + + StrBufAppendBufPlain(Imap->Reply, + CKEY(Imap->Cmd.Params[0]), 0); + + StrBufAppendBufPlain(Imap->Reply, + HKEY(" "), 0); + + va_start(arg_ptr, Format); + StrBufVAppendPrintf(IMAP->Reply, Format, arg_ptr); + va_end(arg_ptr); + + StrBufAppendBufPlain(Imap->Reply, + HKEY("\r\n"), 0); + +} + + +/* Output a string to the IMAP client, either as a literal or quoted. + * (We do a literal if it has any double-quotes or backslashes.) */ + + +void IPutStr(const char *Msg, long Len) +{ + int i; + int is_literal = 0; + citimap *Imap = IMAP; + + + if ((Msg == NULL) || (Len == 0)) + { /* yeah, we handle this */ + StrBufAppendBufPlain(Imap->Reply, HKEY("NIL"), 0); + return; + } + + for (i = 0; i < Len; ++i) { + if ((Msg[i] == '\"') || (Msg[i] == '\\')) + is_literal = 1; + } + + if (is_literal) { + StrBufAppendPrintf(Imap->Reply, "{%ld}\r\n", Len); + StrBufAppendBufPlain(Imap->Reply, Msg, Len, 0); + } else { + StrBufAppendBufPlain(Imap->Reply, + HKEY("\""), 0); + StrBufAppendBufPlain(Imap->Reply, + Msg, Len, 0); + StrBufAppendBufPlain(Imap->Reply, + HKEY("\""), 0); + } +} + +void IUnbuffer (void) +{ + citimap *Imap = IMAP; + + cputbuf(Imap->Reply); + FlushStrBuf(Imap->Reply); +}