diff options
Diffstat (limited to '')
-rw-r--r-- | src/imap_utf7.c | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/src/imap_utf7.c b/src/imap_utf7.c new file mode 100644 index 0000000..aac0fef --- /dev/null +++ b/src/imap_utf7.c @@ -0,0 +1,211 @@ +/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* See the file NOTICE for conditions of use and distribution. */ + +#include "exim.h" + +#ifdef SUPPORT_I18N + +uschar * +imap_utf7_encode(uschar *string, const uschar *charset, uschar sep, + uschar *specials, uschar **error) +{ +static uschar encode_base64[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,"; +size_t slen; +uschar *sptr; +gstring * yield = NULL; +int i = 0; /* compiler quietening */ +uschar c = 0; /* compiler quietening */ +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 = US""; + +/* 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 + || Ustrchr(specials, *s) + ) + break; + } + +if (!*s) + return string; + +sptr = string; +slen = Ustrlen(string); + +#if HAVE_ICONV +if ((icd = iconv_open("UTF-16BE", CCS 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 + || (Ustrchr(specials, s[1]) && s[1] != sep) + ) + { + lastsep = FALSE; + /* Encode as modified BASE64 */ + if (!base64mode) + { + *outptr++ = '&'; + base64mode = TRUE; + i = 0; + } + + for (int 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]); + return NULL; + } + + if (outptr > outbuf + sizeof(outbuf) - 3) + { + yield = string_catn(yield, 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_catn(yield, outbuf, outptr - outbuf); + +if (yield->s[yield->ptr-1] == '.') + yield->ptr--; + +return string_from_gstring(yield); +} + +#endif /* whole file */ +/* vi: aw ai sw=2 +*/ |