summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2015-05-04 17:02:27 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2015-05-04 23:46:20 +0100
commited0512a1a151a4108d7fe309055219c2da3b2bbc (patch)
treea4d87b17e71328661eb324a8a04a2bf5d9473f86 /src
parent0368847fd98dcc3f6b757da53a86519bb16c9681 (diff)
I18N: new ${imapfolder_<sep>:<string>} expansion item. Bug 420
Diffstat (limited to 'src')
-rw-r--r--src/OS/Makefile-Base24
-rwxr-xr-xsrc/scripts/MakeLinks9
-rw-r--r--src/src/expand.c45
-rw-r--r--src/src/functions.h3
-rw-r--r--src/src/imap_utf7.c210
5 files changed, 279 insertions, 12 deletions
diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base
index b7413e201..1d5a5f6f4 100644
--- a/src/OS/Makefile-Base
+++ b/src/OS/Makefile-Base
@@ -295,7 +295,14 @@ convert4r4: Makefile ../src/convert4r4.src
OBJ_WITH_CONTENT_SCAN = malware.o mime.o regex.o spam.o spool_mbox.o
OBJ_WITH_OLD_DEMIME = demime.o
-OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dcc.o dmarc.o dane.o utf8.o
+OBJ_EXPERIMENTAL = bmi_spam.o \
+ dane.o \
+ dcc.o \
+ dmarc.o \
+ imap_utf7.o \
+ spf.o \
+ srs.o \
+ utf8.o
# Targets for final binaries; the main one has a build number which is
# updated each time. We don't bother with that for the auxiliaries.
@@ -618,13 +625,14 @@ demime.o: $(HDRS) demime.c
# Dependencies for EXPERIMENTAL_* modules
-bmi_spam.o: $(HDRS) bmi_spam.c
-dane.o: $(HDRS) dane.c dane-gnu.c dane-openssl.c
-dcc.o: $(HDRS) dcc.h dcc.c
-dmarc.o: $(HDRS) dmarc.h dmarc.c
-spf.o: $(HDRS) spf.h spf.c
-srs.o: $(HDRS) srs.h srs.c
-utf8.o: $(HDRS) utf8.c
+bmi_spam.o: $(HDRS) bmi_spam.c
+dane.o: $(HDRS) dane.c dane-gnu.c dane-openssl.c
+dcc.o: $(HDRS) dcc.h dcc.c
+dmarc.o: $(HDRS) dmarc.h dmarc.c
+imap_utf7.o: $(HDRS) imap_utf7.c
+spf.o: $(HDRS) spf.h spf.c
+srs.o: $(HDRS) srs.h srs.c
+utf8.o: $(HDRS) utf8.c
# The module containing tables of available lookups, routers, auths, and
# transports must be rebuilt if any of them are. However, because the makefiles
diff --git a/src/scripts/MakeLinks b/src/scripts/MakeLinks
index f9cc27c2e..2ec572db5 100755
--- a/src/scripts/MakeLinks
+++ b/src/scripts/MakeLinks
@@ -266,16 +266,17 @@ ln -s ../src/demime.h demime.h
# EXPERIMENTAL_*
ln -s ../src/bmi_spam.c bmi_spam.c
ln -s ../src/bmi_spam.h bmi_spam.h
-ln -s ../src/spf.c spf.c
-ln -s ../src/spf.h spf.h
-ln -s ../src/srs.c srs.c
-ln -s ../src/srs.h srs.h
ln -s ../src/dcc.c dcc.c
ln -s ../src/dcc.h dcc.h
ln -s ../src/dane.c dane.c
ln -s ../src/dane-gnu.c dane-gnu.c
ln -s ../src/dane-openssl.c dane-openssl.c
ln -s ../src/danessl.h danessl.h
+ln -s ../src/imap_utf7.c imap_utf7.c
+ln -s ../src/spf.c spf.c
+ln -s ../src/spf.h spf.h
+ln -s ../src/srs.c srs.c
+ln -s ../src/srs.h srs.h
ln -s ../src/utf8.c utf8.c
diff --git a/src/src/expand.c b/src/src/expand.c
index ad97f6fef..209270163 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -114,6 +114,9 @@ static uschar *item_table[] = {
US"hash",
US"hmac",
US"if",
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ US"imapfolder",
+#endif
US"length",
US"listextract",
US"lookup",
@@ -140,6 +143,9 @@ enum {
EITEM_HASH,
EITEM_HMAC,
EITEM_IF,
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ EITEM_IMAPFOLDER,
+#endif
EITEM_LENGTH,
EITEM_LISTEXTRACT,
EITEM_LOOKUP,
@@ -4070,6 +4076,45 @@ while (*s != 0)
continue;
}
+#ifdef EXPERIMENTAL_INTERNATIONAL
+ case EITEM_IMAPFOLDER:
+ { /* ${imapfolder {name}{sep]{specials}} */
+ uschar *sub_arg[3];
+ uschar *encoded;
+
+ switch(read_subs(sub_arg, 3, 1, &s, skipping, TRUE, name, &resetok))
+ {
+ case 1: goto EXPAND_FAILED_CURLY;
+ case 2:
+ case 3: goto EXPAND_FAILED;
+ }
+
+ if (sub_arg[1] == NULL) /* One argument */
+ {
+ sub_arg[1] = "/"; /* default separator */
+ sub_arg[2] = NULL;
+ }
+ else if (sub_arg[2] == NULL) /* Two arguments */
+ sub_arg[2] = NULL;
+
+ if (Ustrlen(sub_arg[1]) != 1)
+ {
+ expand_string_message =
+ string_sprintf(
+ "IMAP folder separator must be one character, found \"%s\"",
+ sub_arg[1]);
+ goto EXPAND_FAILED;
+ }
+
+ if (!(encoded = imap_utf7_encode(sub_arg[0], headers_charset,
+ sub_arg[1][0], sub_arg[2], &expand_string_message)))
+ goto EXPAND_FAILED;
+ if (!skipping)
+ yield = string_cat(yield, &size, &ptr, encoded, Ustrlen(encoded));
+ continue;
+ }
+#endif
+
/* Handle database lookups unless locked out. If "skipping" is TRUE, we are
expanding an internal string that isn't actually going to be used. All we
need to do is check the syntax, so don't do a lookup at all. Preserve the
diff --git a/src/src/functions.h b/src/src/functions.h
index 1708b7a07..74198a52c 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -210,6 +210,9 @@ extern int host_nmtoa(int, int *, int, uschar *, int);
extern uschar *host_ntoa(int, const void *, uschar *, int *);
extern int host_scan_for_local_hosts(host_item *, host_item **, BOOL *);
+extern uschar *imap_utf7_encode(uschar *, const uschar *,
+ uschar, uschar *, uschar **);
+
extern void invert_address(uschar *, uschar *);
extern int ip_addr(void *, int, const uschar *, int);
extern int ip_bind(int, int, uschar *, int);
diff --git a/src/src/imap_utf7.c b/src/src/imap_utf7.c
new file mode 100644
index 000000000..10cc1f7fa
--- /dev/null
+++ b/src/src/imap_utf7.c
@@ -0,0 +1,210 @@
+#include "exim.h"
+
+#ifdef EXPERIMENTAL_INTERNATIONAL
+
+uschar *
+imap_utf7_encode(uschar *string, const uschar *charset, uschar sep,
+ uschar *specials, uschar **error)
+{
+static uschar encode_base64[64] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
+int ptr = 0;
+int size = 0;
+size_t slen;
+uschar *sptr, *yield = NULL;
+int i, j;
+uschar c;
+BOOL base64mode = FALSE;
+BOOL lastsep = FALSE;
+uschar utf16buf[256];
+uschar *utf16ptr;
+uschar *s;
+uschar outbuf[256];
+uschar *outptr = outbuf;
+#if HAVE_ICONV
+iconv_t icd;
+#endif
+
+if (!specials) specials = "";
+
+/* Pass over the string. If it consists entirely of "normal" characters
+ (possibly with leading seps), return it as is. */
+for (s = string; *s; s++)
+ {
+ if (s == string && *s == sep)
+ string++;
+ if ( *s >= 0x7f
+ || *s < 0x20
+ || strchr("./&", *s)
+ || *s == sep
+ || strchr(specials, *s)
+ )
+ break;
+ }
+
+if (!*s)
+ return string;
+
+sptr = string;
+slen = Ustrlen(string);
+
+#if HAVE_ICONV
+if ((icd = iconv_open(US"UTF-16BE", charset)) == (iconv_t)-1)
+ {
+ *error = string_sprintf(
+ "imapfolder: iconv_open(\"UTF-16BE\", \"%s\") failed: %s%s",
+ charset, strerror(errno),
+ errno == EINVAL ? " (maybe unsupported conversion)" : "");
+ return NULL;
+ }
+#endif
+
+while (slen > 0)
+ {
+#if HAVE_ICONV
+ size_t left = sizeof(utf16buf);
+ utf16ptr = utf16buf;
+
+ if ( iconv(icd, (ICONV_ARG2_TYPE)&sptr, &slen, CSS &utf16ptr, &left)
+ == (size_t)-1
+ && errno != E2BIG
+ )
+ {
+ *error = string_sprintf("imapfolder: iconv() failed to convert from %s: %s",
+ charset, strerror(errno));
+ iconv_close(icd);
+ return NULL;
+ }
+#else
+ for (utf16ptr = utf16buf;
+ slen > 0 && (utf16ptr - utf16buf) < sizeof(utf16buf);
+ utf16ptr += 2, slen--, sptr++)
+ {
+ *utf16ptr = *sptr;
+ *(utf16ptr+1) = '\0';
+ }
+#endif
+
+ s = utf16buf;
+ while (s < utf16ptr)
+ {
+ /* Now encode utf16buf as modified UTF-7 */
+ if ( s[0] != 0
+ || s[1] >= 0x7f
+ || s[1] < 0x20
+ || (strchr(specials, s[1]) && s[1] != sep)
+ )
+ {
+ lastsep = FALSE;
+ /* Encode as modified BASE64 */
+ if (!base64mode)
+ {
+ *outptr++ = '&';
+ base64mode = TRUE;
+ i = 0;
+ }
+
+ for (j = 0; j < 2; j++, s++) switch (i++)
+ {
+ case 0:
+ /* Top 6 bits of the first octet */
+ *outptr++ = encode_base64[(*s >> 2) & 0x3F];
+ c = (*s & 0x03); break;
+ case 1:
+ /* Bottom 2 bits of the first octet, and top 4 bits of the second */
+ *outptr++ = encode_base64[(c << 4) | ((*s >> 4) & 0x0F)];
+ c = (*s & 0x0F); break;
+ case 2:
+ /* Bottom 4 bits of the second octet and top 2 bits of the third */
+ *outptr++ = encode_base64[(c << 2) | ((*s >> 6) & 0x03)];
+ /* Bottom 6 bits of the third octet */
+ *outptr++ = encode_base64[*s & 0x3F];
+ i = 0;
+ }
+ }
+
+ else if ( (s[1] != '.' && s[1] != '/')
+ || s[1] == sep
+ )
+ {
+ /* Encode as self (almost) */
+ if (base64mode)
+ {
+ switch (i)
+ {
+ case 1:
+ /* Remaining bottom 2 bits of the last octet */
+ *outptr++ = encode_base64[c << 4];
+ break;
+ case 2:
+ /* Remaining bottom 4 bits of the last octet */
+ *outptr++ = encode_base64[c << 2];
+ }
+ *outptr++ = '-';
+ base64mode = FALSE;
+ }
+
+ if (*++s == sep)
+ {
+ if (!lastsep)
+ {
+ *outptr++ = '.';
+ lastsep = TRUE;
+ }
+ }
+ else
+ {
+ *outptr++ = *s;
+ if (*s == '&')
+ *outptr++ = '-';
+ lastsep = FALSE;
+ }
+
+ s++;
+ }
+ else
+ {
+ *error = string_sprintf("imapfolder: illegal character '%c'", s[1]);
+ if (yield) store_reset(yield);
+ return NULL;
+ }
+
+ if (outptr > outbuf + sizeof(outbuf) - 3)
+ {
+ yield = string_cat(yield, &size, &ptr, outbuf, outptr - outbuf);
+ outptr = outbuf;
+ }
+
+ }
+ } /* End of input string */
+
+if (base64mode)
+ {
+ switch (i)
+ {
+ case 1:
+ /* Remaining bottom 2 bits of the last octet */
+ *outptr++ = encode_base64[c << 4];
+ break;
+ case 2:
+ /* Remaining bottom 4 bits of the last octet */
+ *outptr++ = encode_base64[c << 2];
+ }
+ *outptr++ = '-';
+ }
+
+#if HAVE_ICONV
+iconv_close(icd);
+#endif
+
+yield = string_cat(yield, &size, &ptr, outbuf, outptr - outbuf);
+if (yield[ptr-1] == '.')
+ ptr--;
+yield[ptr] = '\0';
+
+return yield;
+}
+
+#endif /* whole file */
+/* vi: aw ai sw=2
+*/