bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen#include "lib.h"
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen#include "str.h"
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen#include "unichar.h"
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen#include "imap-utf7.h"
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainenstatic const char imap_b64enc[] =
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen#define XX 0xff
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainenstatic const unsigned char imap_b64dec[256] = {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,62, 63,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen 52,53,54,55, 56,57,58,59, 60,61,XX,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen 15,16,17,18, 19,20,21,22, 23,24,25,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen 41,42,43,44, 45,46,47,48, 49,50,51,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen};
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainenstatic void
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainenmbase64_encode(string_t *dest, const unsigned char *in, size_t len)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen{
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, '&');
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen while (len >= 3) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, imap_b64enc[in[0] >> 2]);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, imap_b64enc[((in[0] & 3) << 4) |
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen (in[1] >> 4)]);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, imap_b64enc[((in[1] & 0x0f) << 2) |
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen ((in[2] & 0xc0) >> 6)]);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, imap_b64enc[in[2] & 0x3f]);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen in += 3;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen len -= 3;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (len > 0) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, imap_b64enc[in[0] >> 2]);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (len == 1)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, imap_b64enc[(in[0] & 0x03) << 4]);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen else {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, imap_b64enc[((in[0] & 0x03) << 4) |
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen (in[1] >> 4)]);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, imap_b64enc[(in[1] & 0x0f) << 2]);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, '-');
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen}
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainenstatic const char *imap_utf8_first_encode_char(const char *str)
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen{
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen const char *p;
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen for (p = str; *p != '\0'; p++) {
4de99500799c3518fa697cbc249bea4b266153adPhil Carmody if (*p == '&' || *p < 0x20 || *p >= 0x7f)
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen return p;
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen }
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen return NULL;
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen}
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainenint imap_utf8_to_utf7(const char *src, string_t *dest)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen{
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen const char *p;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen unichar_t chr;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen uint8_t *utf16, *u;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen uint16_t u16;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen p = imap_utf8_first_encode_char(src);
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen if (p == NULL) {
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen /* no characters that need to be encoded */
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append(dest, src);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return 0;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen /* at least one encoded character */
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_n(dest, src, p-src);
e7d0bea63a08b08c47c4b5c187d2cb7127859657Timo Sirainen utf16 = t_malloc0(MALLOC_MULTIPLY(strlen(p), 2));
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen while (*p != '\0') {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (*p == '&') {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append(dest, "&-");
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen p++;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen continue;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
4de99500799c3518fa697cbc249bea4b266153adPhil Carmody if (*p >= 0x20 && *p < 0x7f) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, *p);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen p++;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen continue;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen u = utf16;
4de99500799c3518fa697cbc249bea4b266153adPhil Carmody while (*p != '\0' && (*p < 0x20 || *p >= 0x7f)) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (uni_utf8_get_char(p, &chr) <= 0)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen /* @UNSAFE */
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (chr < UTF16_SURROGATE_BASE) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen *u++ = chr >> 8;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen *u++ = chr & 0xff;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen } else {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen u16 = UTF16_SURROGATE_HIGH(chr);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen *u++ = u16 >> 8;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen *u++ = u16 & 0xff;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen u16 = UTF16_SURROGATE_LOW(chr);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen *u++ = u16 >> 8;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen *u++ = u16 & 0xff;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen p += uni_utf8_char_bytes(*p);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen mbase64_encode(dest, utf16, u-utf16);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return 0;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen}
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainenint t_imap_utf8_to_utf7(const char *src, const char **dest_r)
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen{
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen string_t *str;
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen int ret;
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen if (imap_utf8_first_encode_char(src) == NULL) {
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen *dest_r = src;
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen return 0;
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen }
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen str = t_str_new(64);
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen ret = imap_utf8_to_utf7(src, str);
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen *dest_r = str_c(str);
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen return ret;
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen}
09c8ed62f3460ed6c6b8fdd97f54ad5d93894857Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainenstatic int utf16buf_to_utf8(string_t *dest, const unsigned char output[4],
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen unsigned int *_pos, unsigned int len)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen{
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen unsigned int pos = *_pos;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen uint16_t high, low;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen unichar_t chr;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (len % 2 != 0)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen high = (output[pos % 4] << 8) | output[(pos+1) % 4];
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (high < UTF16_SURROGATE_HIGH_FIRST ||
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen high > UTF16_SURROGATE_HIGH_MAX) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen /* single byte */
585017cae29021070c7eba527720e91a64e7538fPhil Carmody size_t oldlen = str_len(dest);
3fbc12d56ede063e9e049496b620a1e2934e418eTimo Sirainen
3fbc12d56ede063e9e049496b620a1e2934e418eTimo Sirainen if (high == 0) {
3fbc12d56ede063e9e049496b620a1e2934e418eTimo Sirainen /* Encoded NUL isn't going to work in Dovecot code,
3fbc12d56ede063e9e049496b620a1e2934e418eTimo Sirainen even though it's technically valid. Return failure
3fbc12d56ede063e9e049496b620a1e2934e418eTimo Sirainen so the callers don't even get a chance to handle the
3fbc12d56ede063e9e049496b620a1e2934e418eTimo Sirainen NUL in the string inconsistently. */
3fbc12d56ede063e9e049496b620a1e2934e418eTimo Sirainen return -1;
3fbc12d56ede063e9e049496b620a1e2934e418eTimo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen uni_ucs4_to_utf8_c(high, dest);
585017cae29021070c7eba527720e91a64e7538fPhil Carmody if (str_len(dest) - oldlen == 1) {
585017cae29021070c7eba527720e91a64e7538fPhil Carmody unsigned char last = str_data(dest)[oldlen];
585017cae29021070c7eba527720e91a64e7538fPhil Carmody if (last >= 0x20 && last < 0x7f)
585017cae29021070c7eba527720e91a64e7538fPhil Carmody return -1;
585017cae29021070c7eba527720e91a64e7538fPhil Carmody }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen *_pos = (pos + 2) % 4;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return 0;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (high > UTF16_SURROGATE_HIGH_LAST)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (len != 4) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen /* missing the second character */
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen low = (output[(pos+2)%4] << 8) | output[(pos+3) % 4];
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (low < UTF16_SURROGATE_LOW_FIRST || low > UTF16_SURROGATE_LOW_LAST)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen chr = UTF16_SURROGATE_BASE +
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen (((high & UTF16_SURROGATE_MASK) << UTF16_SURROGATE_SHIFT) |
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen (low & UTF16_SURROGATE_MASK));
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen uni_ucs4_to_utf8_c(chr, dest);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return 0;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen}
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainenstatic int mbase64_decode_to_utf8(string_t *dest, const char **_src)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen{
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen const char *src = *_src;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen unsigned char input[4], output[4];
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen unsigned int outstart = 0, outpos = 0;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen while (*src != '-') {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen input[0] = imap_b64dec[(uint8_t)src[0]];
ba0ca241ea5003d9d5c1a0045473d1bf7949e83bTimo Sirainen if (input[0] == 0xff)
ba0ca241ea5003d9d5c1a0045473d1bf7949e83bTimo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen input[1] = imap_b64dec[(uint8_t)src[1]];
ba0ca241ea5003d9d5c1a0045473d1bf7949e83bTimo Sirainen if (input[1] == 0xff)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen output[outpos % 4] = (input[0] << 2) | (input[1] >> 4);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (++outpos % 4 == outstart) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (utf16buf_to_utf8(dest, output, &outstart, 4) < 0)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen input[2] = imap_b64dec[(uint8_t)src[2]];
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (input[2] == 0xff) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (src[2] != '-')
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen src += 2;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen break;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen output[outpos % 4] = (input[1] << 4) | (input[2] >> 2);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (++outpos % 4 == outstart) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (utf16buf_to_utf8(dest, output, &outstart, 4) < 0)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen input[3] = imap_b64dec[(uint8_t)src[3]];
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (input[3] == 0xff) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (src[3] != '-')
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen src += 3;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen break;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen output[outpos % 4] = ((input[2] << 6) & 0xc0) | input[3];
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (++outpos % 4 == outstart) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (utf16buf_to_utf8(dest, output, &outstart, 4) < 0)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen src += 4;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (outstart != outpos % 4) {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (utf16buf_to_utf8(dest, output, &outstart,
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen (4 + outpos - outstart) % 4) < 0)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen /* found ending '-' */
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen *_src = src + 1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return 0;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen}
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainenint imap_utf7_to_utf8(const char *src, string_t *dest)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen{
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen const char *p;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen for (p = src; *p != '\0'; p++) {
807d831b2625cc45b55f9576aa744e8a038b1bfcPhil Carmody if (*p < 0x20 || *p >= 0x7f)
807d831b2625cc45b55f9576aa744e8a038b1bfcPhil Carmody return -1;
807d831b2625cc45b55f9576aa744e8a038b1bfcPhil Carmody if (*p == '&')
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen break;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (*p == '\0') {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen /* no IMAP-UTF-7 encoded characters */
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append(dest, src);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return 0;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen /* at least one encoded character */
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_n(dest, src, p-src);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen while (*p != '\0') {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (*p == '&') {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (*++p == '-') {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, '&');
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen p++;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen } else {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (mbase64_decode_to_utf8(dest, &p) < 0)
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen if (p[0] == '&' && p[1] != '-') {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen /* &...-& */
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return -1;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen } else {
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen str_append_c(dest, *p++);
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen }
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen return 0;
250eaa188a7c95a4fe25525943830bda35030ba6Timo Sirainen}
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainenbool imap_utf7_is_valid(const char *src)
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen{
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen const char *p;
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen int ret;
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen for (p = src; *p != '\0'; p++) {
807d831b2625cc45b55f9576aa744e8a038b1bfcPhil Carmody if (*p < 0x20 || *p >= 0x7f)
807d831b2625cc45b55f9576aa744e8a038b1bfcPhil Carmody return FALSE;
807d831b2625cc45b55f9576aa744e8a038b1bfcPhil Carmody if (*p == '&') {
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen /* slow scan */
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen T_BEGIN {
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen string_t *tmp = t_str_new(128);
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen ret = imap_utf7_to_utf8(p, tmp);
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen } T_END;
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen if (ret < 0)
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen return FALSE;
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen }
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen }
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen return TRUE;
fba71f7f73279a9d65c4eef0c3eb9c7fa715c3e5Timo Sirainen}