/*
- * $Id$
- *
* Utility functions for the IMAP module.
*
* 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 free software; you can redistribute it and/or modify
+ * 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.
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#define SHOW_ME_VAPPEND_PRINTF
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
+#include <stdarg.h>
#include <libcitadel.h>
#include "citadel.h"
#include "sysdep_decls.h"
#include "internet_addressing.h"
+#include "serv_imap.h"
#include "imap_tools.h"
#include "ctdl_module.h"
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. */
}
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. */
*destp = 0;
string_init(&dest, destp, destend-destp);
- /* CtdlLogPrintf(CTDL_DEBUG, "toimap %s\r\n", src); */
+ /* IMAP_syslog(LOG_DEBUG, "toimap %s", src); */
for (;;)
{
if (state == 1)
utf7_closeb64(&dest, v, i);
- /* CtdlLogPrintf(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;
*destp = 0;
string_init(&dest, destp, destend-destp);
- /* CtdlLogPrintf(CTDL_DEBUG, "fromimap %s\r\n", src); */
+ /* IMAP_syslog(LOG_DEBUG, "fromimap %s", src); */
do {
c = *p++;
}
} while (c != '\0');
- /* CtdlLogPrintf(CTDL_DEBUG, " -> %s\r\n", destp); */
+ /* IMAP_syslog(LOG_DEBUG, " -> %s", destp); */
return string_end(&dest);
}
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; i<Cmd->num_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++;
+ }
+ 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);
}
- *out++ = '\0';
+ 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)
if (qrbuf->QRflags & QR_MAILBOX)
{
if (strcasecmp(qrbuf->QRname+11, MAILROOM) == 0)
- p = toimap(p, bufend, "INBOX");
+ toimap(p, bufend, "INBOX");
else
{
p = toimap(p, bufend, "INBOX");
if (p < bufend)
*p++ = '/';
- p = toimap(p, bufend, qrbuf->QRname+11);
+ toimap(p, bufend, qrbuf->QRname+11);
}
}
else
p = toimap(p, bufend, fl->f_name);
if (p < bufend)
*p++ = '/';
- p = toimap(p, bufend, qrbuf->QRname);
+ toimap(p, bufend, qrbuf->QRname);
}
}
*
*/
-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;
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. */
ret = (0 | IR_MAILBOX);
exit:
- CtdlLogPrintf(CTDL_DEBUG, "(That translates to \"%s\")\n", rbuf);
+ IMAP_syslog(LOG_DEBUG, "(That translates to \"%s\")", rbuf);
return(ret);
}
struct internet_address_list *iptr;
if (ialist == NULL) {
- cprintf("NIL");
+ IAPuts("NIL");
return;
}
- cprintf("(");
+ IAPuts("(");
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(")");
+ IAPuts("(");
+ plain_imap_strout(iptr->ial_name);
+ IAPuts(" NIL ");
+ plain_imap_strout(iptr->ial_user);
+ IAPuts(" ");
+ plain_imap_strout(iptr->ial_node);
+ IAPuts(")");
}
- cprintf(")");
+ IAPuts(")");
}
* 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;
{
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;
}
}
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.
* Trailing % matches everything
* without a delimiter.
*/
- while (*text) {
+ while (!IsEmptyStr(text)) {
if (*text == WILDMAT_DELIM) {
return WILDMAT_FALSE;
}
}
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;
}
}
- return (*text == '\0');
+ if ((*text == '\0') && (*p == '\0')) return WILDMAT_TRUE;
+ else return WILDMAT_FALSE;
}
* 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')) {
* 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];
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 plain_imap_strout(char *buf)
+{
+ int i;
+ int is_literal = 0;
+ long Len;
+ citimap *Imap = IMAP;
+
+ if (buf == NULL) { /* yeah, we handle this */
+ IAPuts("NIL");
+ return;
+ }
+
+ Len = strlen(buf);
+ for (i = 0; i < Len; ++i) {
+ if ((buf[i] == '\"') || (buf[i] == '\\'))
+ is_literal = 1;
+ }
+
+ if (is_literal) {
+ StrBufAppendPrintf(Imap->Reply, "{%ld}\r\n", Len);
+ StrBufAppendBufPlain(Imap->Reply, buf, Len, 0);
+ } else {
+ StrBufAppendBufPlain(Imap->Reply,
+ HKEY("\""), 0);
+ StrBufAppendBufPlain(Imap->Reply,
+ buf, Len, 0);
+ StrBufAppendBufPlain(Imap->Reply,
+ HKEY("\""), 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);
+}