From fe26284795c623afb2448bf19b116d5ce4c70b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 18 Dec 2011 23:49:53 +0100 Subject: [PATCH] Added support for decoding RFC-3501 mailbox names. * Added new RFC3501Encoding class that implements modified UTF-7 as specified in RFC-3501; only decoding is implemented right now. * ListCommand now uses this new class to encode, and decode the folder names. --- .../imap/imap_lib/Commands.cpp | 12 +- .../imap/imap_lib/Commands.h | 1 + .../imap/imap_lib/Response.cpp | 117 ++++++++++++++++++ .../imap/imap_lib/Response.h | 13 ++ 4 files changed, 141 insertions(+), 2 deletions(-) diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.cpp b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.cpp index aef6ba3fef..50c2ab71b1 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.cpp @@ -642,7 +642,7 @@ ListCommand::CommandString() BString command = _Command(); command += " \"\" \""; if (fPrefix != NULL) - command << fPrefix << "%"; + command << fEncoding.Encode(fPrefix) << "%"; else command += "*"; command += "\""; @@ -655,7 +655,15 @@ bool ListCommand::HandleUntagged(Response& response) { if (response.IsCommand(_Command()) && response.IsStringAt(3)) { - fFolders.push_back(response.StringAt(3)); + BString folder = response.StringAt(3); + try { + fFolders.push_back(fEncoding.Decode(folder)); + } catch (ParseException& exception) { + // Decoding failed, just add the plain text + fprintf(stderr, "Decoding \"%s\" failed: %s\n", folder.String(), + exception.Message()); + fFolders.push_back(folder); + } return true; } diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.h b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.h index 8d01cc7551..6dbf2cff2e 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Commands.h @@ -258,6 +258,7 @@ private: const char* _Command() const; private: + RFC3501Encoding fEncoding; const char* fPrefix; StringList fFolders; bool fSubscribedOnly; diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.cpp b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.cpp index 372831fa4e..46d83f37fb 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.cpp +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.cpp @@ -20,6 +20,123 @@ namespace IMAP { +// Note, the following alphabet is a modified base64; the '/' is replaced by +// a ',' here. +static const char kBase64Alphabet[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '+', ',' +}; +static char kInverseBase64Alphabet[128]; +static bool kInverseBase64Initialized = false; + + +RFC3501Encoding::RFC3501Encoding() +{ + if (!kInverseBase64Initialized) { + // This is not thread safe, but it's not harmful + for (size_t i = 0; i < sizeof(kBase64Alphabet); i++) + kInverseBase64Alphabet[kBase64Alphabet[i]] = i + 1; + kInverseBase64Initialized = true; + } +} + + +RFC3501Encoding::~RFC3501Encoding() +{ +} + + +BString +RFC3501Encoding::Encode(const BString& clearText) const +{ + // TODO! + return clearText; +} + + +BString +RFC3501Encoding::Decode(const BString& encodedText) const +{ + int32 end = encodedText.Length(); + BString buffer; + for (int32 i = 0; i < end; i++) { + uint8 c = (uint8)encodedText.ByteAt(i); + if (c == '&') { + if (i < end - 1 && encodedText.ByteAt(i + 1) == '-') { + // just add an ampersand + buffer += '&'; + } else { + // base64 encoded chunk + uint32 value = 0; + int32 bitsRead = 0; + while (true) { + if (++i >= end) + throw ParseException("Malformed base64!"); + + c = encodedText.ByteAt(i); + if (c == '-') { + if (value != 0 || bitsRead >= 6) + throw ParseException("Base64 encoding ends early!"); + break; + } + if (c >= 128) + throw ParseException("Malformed base64!"); + int32 sextet = kInverseBase64Alphabet[c] - 1; + if (sextet >= 0) { + bitsRead += 6; + if (bitsRead < 16) { + value += sextet << (16 - bitsRead); + } else { + bitsRead -= 16; + value += sextet >> bitsRead; + _ToUTF8(buffer, value); + + // Move on to next character + value = (sextet << (16 - bitsRead)) & 0xffff; + } + } else { + buffer += c; + if (value != 0 || bitsRead >= 6) + throw ParseException("Malformed base64!"); + break; + } + } + } + } else + buffer += c; + } + return buffer; +} + + +void +RFC3501Encoding::_ToUTF8(BString& string, uint32 c) const +{ + if (c < 0x80) + string += (char)c; + else if (c < 0x800) { + string += 0xc0 | (c >> 6); + string += 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + string += 0xe0 | (c >> 12); + string += 0x80 | ((c >> 6) & 0x3f); + string += 0x80 | (c & 0x3f); + } else if (c <= 0x10ffff) { + string += 0xf0 | (c >> 18); + string += 0x80 | ((c >> 12) & 0x3f); + string += 0x80 | ((c >> 6) & 0x3f); + string += 0x80 | (c & 0x3f); + } +} + + +// #pragma mark - + + ArgumentList::ArgumentList() : BObjectList(5, true) diff --git a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.h b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.h index 6797f15b95..87c00591df 100644 --- a/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.h +++ b/src/add-ons/mail_daemon/inbound_protocols/imap/imap_lib/Response.h @@ -20,6 +20,19 @@ class Argument; class Response; +class RFC3501Encoding { +public: + RFC3501Encoding(); + ~RFC3501Encoding(); + + BString Encode(const BString& clearText) const; + BString Decode(const BString& encodedText) const; + +private: + void _ToUTF8(BString& string, uint32 c) const; +}; + + class ArgumentList : public BObjectList { public: ArgumentList();