diff --git a/src/imapfilter.c b/src/imapfilter.c index 6206e78..78d4c37 100644 --- a/src/imapfilter.c +++ b/src/imapfilter.c @@ -21,7 +21,7 @@ #endif -extern buffer ibuf, obuf, nbuf; +extern buffer ibuf, obuf, nbuf, cbuf; extern regexp responses[]; options opts; /* Program options. */ @@ -98,6 +98,7 @@ main(int argc, char *argv[]) buffer_init(&ibuf, INPUT_BUF); buffer_init(&obuf, OUTPUT_BUF); buffer_init(&nbuf, NAMESPACE_BUF); + buffer_init(&cbuf, CONVERSION_BUF); if (opts.config == NULL) opts.config = get_filepath("config.lua"); @@ -137,6 +138,7 @@ main(int argc, char *argv[]) buffer_free(&ibuf); buffer_free(&obuf); buffer_free(&nbuf); + buffer_free(&cbuf); xfree(env.home); diff --git a/src/imapfilter.h b/src/imapfilter.h index 5fb85ee..d08b513 100644 --- a/src/imapfilter.h +++ b/src/imapfilter.h @@ -53,7 +53,8 @@ /* Initial buffer size for input, output and namespace buffers. */ #define INPUT_BUF 4096 #define OUTPUT_BUF 1024 -#define NAMESPACE_BUF 128 +#define NAMESPACE_BUF 512 +#define CONVERSION_BUF 512 /* Maximum length, in bytes, of a utility's input line. */ #ifndef LINE_MAX diff --git a/src/namespace.c b/src/namespace.c index 6f0e0b5..779a5a4 100644 --- a/src/namespace.c +++ b/src/namespace.c @@ -1,11 +1,18 @@ #include #include +#include +#include #include "imapfilter.h" #include "buffer.h" buffer nbuf; /* Namespace buffer. */ +buffer cbuf; /* Conversion buffer. */ + + +const char *apply_conversion(const char *mbox); +const char *reverse_conversion(const char *mbox); /* @@ -17,26 +24,29 @@ apply_namespace(const char *mbox, char *prefix, char delim) { int n; char *c; + const char *m; + + if (!strcasecmp(mbox, "INBOX")) + return mbox; + + m = apply_conversion(mbox); if ((prefix == NULL && delim == '\0') || - (prefix == NULL && delim == '/') || - !strcasecmp(mbox, "INBOX")) - return mbox; + (prefix == NULL && delim == '/')) + return m; buffer_reset(&nbuf); n = snprintf(nbuf.data, nbuf.size + 1, "%s%s", (prefix ? prefix : ""), - mbox); + m); if (n > (int)nbuf.size) { buffer_check(&nbuf, n); - snprintf(nbuf.data, nbuf.size + 1, "%s%s", - (prefix ? prefix : ""), mbox); + snprintf(nbuf.data, nbuf.size + 0, "%s%s", + (prefix ? prefix : ""), m); } - c = nbuf.data; - while ((c = strchr(c, '/'))) - *(c++) = delim; + for (c = nbuf.data; (c = strchr(c, '/')) != NULL; *(c++) = delim); - debug("namespace: '%s' -> '%s'\n", mbox, nbuf.data); + debug("namespace: '%s' -> '%s'\n", m, nbuf.data); return nbuf.data; } @@ -52,11 +62,13 @@ reverse_namespace(const char *mbox, char *prefix, char delim) int n, o; char *c; - if ((prefix == NULL && delim == '\0') || - (prefix == NULL && delim == '/') || - !strcasecmp(mbox, "INBOX")) + if (!strcasecmp(mbox, "INBOX")) return mbox; + if ((prefix == NULL && delim == '\0') || + (prefix == NULL && delim == '/')) + return reverse_conversion(mbox); + buffer_reset(&nbuf); o = strlen(prefix ? prefix : ""); @@ -68,11 +80,161 @@ reverse_namespace(const char *mbox, char *prefix, char delim) buffer_check(&nbuf, n); snprintf(nbuf.data, nbuf.size + 1, "%s", mbox + o); } - c = nbuf.data; - while ((c = strchr(c, delim))) - *(c++) = '/'; + for (c = nbuf.data; (c = strchr(c, delim)) != NULL; *(c++) = '/'); debug("namespace: '%s' <- '%s'\n", mbox, nbuf.data); + return reverse_conversion(nbuf.data); +} + + +/* + * Convert a mailbox name to the modified UTF-7 encoding, according to RFC 3501 + * Section 5.1.3. + */ +const char * +apply_conversion(const char *mbox) +{ + size_t n; + iconv_t cd; + char *inbuf, *outbuf; + size_t inlen, outlen; + char *c, *shift; + + n = strlen(mbox); + buffer_check(&nbuf, n); + buffer_reset(&nbuf); + xstrncpy(nbuf.data, mbox, *(mbox + n - 1) == '*' || + *(mbox + n - 1) == '%' ? n - 1 : n); + for (c = nbuf.data; (c = strchr(c, '&')) != NULL; *c = '+'); + + do { + inbuf = nbuf.data; + inlen = strlen(nbuf.data); + + buffer_check(&cbuf, inlen); + buffer_reset(&cbuf); + + outbuf = cbuf.data; + outlen = cbuf.size; + + cd = iconv_open("UTF-7", ""); + if (cd == (iconv_t)-1) { + error("converting mailbox name; %s\n", strerror(errno)); + return mbox; + } + while (inlen > 0) { + if (iconv(cd, &inbuf, &inlen, &outbuf, &outlen) == -1) { + if (errno == E2BIG) { + buffer_check(&cbuf, cbuf.size * 2); + break; + } else { + error("converting mailbox name; %s\n", + strerror(errno)); + return mbox; + } + } else { + iconv(cd, NULL, NULL, &outbuf, &outlen); + } + } + iconv_close(cd); + } while (inlen > 0); + + if (*outbuf != '\0') + *outbuf = '\0'; + for (c = cbuf.data, shift = NULL; *c != '\0'; c++) + switch (*c) { + case '+': + *c = '&'; + shift = c; + break; + case '-': + shift = NULL; + break; + case '/': + if (shift != NULL) + *c = ','; + break; + } + if (shift != NULL) { + *outbuf++ = '-'; + *outbuf = '\0'; + } + if (*(mbox + n - 1) == '*' || *(mbox + n - 1) == '%') { + *outbuf++ = *(mbox + n - 1); + *outbuf = '\0'; + } + + debug("conversion: '%s' -> '%s'\n", mbox, cbuf.data); + + return cbuf.data; +} + +/* + * Convert a mailbox name from the modified UTF-7 encoding, according to RFC + * 3501 Section 5.1.3. + */ +const char *reverse_conversion(const char *mbox) +{ + iconv_t cd; + char *inbuf, *outbuf; + size_t inlen, outlen; + char *c, *shift; + + buffer_check(&cbuf, strlen(mbox)); + buffer_reset(&cbuf); + xstrncpy(cbuf.data, mbox, cbuf.size); + for (c = cbuf.data, shift = NULL; *c != '\0'; c++) + switch (*c) { + case '&': + *c = '+'; + shift = c; + break; + case '-': + shift = NULL; + break; + case ',': + if (shift != NULL) + *c = '/'; + break; + } + + do { + inbuf = cbuf.data; + inlen = strlen(cbuf.data); + + buffer_check(&nbuf, inlen); + buffer_reset(&nbuf); + + outbuf = nbuf.data; + outlen = nbuf.size; + + cd = iconv_open("", "UTF-7"); + if (cd == (iconv_t)-1) { + error("converting mailbox name; %s\n", strerror(errno)); + return mbox; + } + while (inlen > 0) { + if (iconv(cd, &inbuf, &inlen, &outbuf, &outlen) == -1) { + if (errno == E2BIG) { + buffer_check(&nbuf, nbuf.size * 2); + break; + } else { + error("converting mailbox name; %s\n", + strerror(errno)); + return mbox; + } + } else { + iconv(cd, NULL, NULL, &outbuf, &outlen); + } + } + iconv_close(cd); + } while (inlen > 0); + + *outbuf = '\0'; + for (c = nbuf.data; (c = strchr(c,'+')) != NULL; *c = '&'); + + debug("conversion: '%s' <- '%s'\n", mbox, nbuf.data); + return nbuf.data; }