+#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
+*/