Кодировка пути к папке IMAP (IMAP UTF-7) для .NET?

Спецификация IMAP (RFC 2060, 5.1.3. Международное соглашение об именах почтовых ящиков) описывает, как для обработки символов, отличных от ASCII, в именах папок. Он определяет модифицированную кодировку UTF-7:

По соглашению международные имена почтовых ящиков указываются с использованием модифицированной версии кодировки UTF-7, описанной в [UTF-7]. Целью этих модификаций является исправление следующих проблем с UTF-7:

  1. UTF-7 использует символ «+» для сдвига; это противоречит обычному использованию «+» в именах почтовых ящиков, в частности, в именах групп новостей USENET.

  2. Кодировка UTF-7 — BASE64, в которой используется символ «/»; это противоречит использованию «/» в качестве популярного разделителя иерархии.

  3. UTF-7 запрещает незакодированное использование «\»; это противоречит использованию «\» в качестве популярного разделителя иерархии.

  4. UTF-7 запрещает незакодированное использование «~»; это противоречит использованию «~» на некоторых серверах в качестве индикатора домашнего каталога.

  5. UTF-7 позволяет использовать несколько альтернативных форм для представления одной и той же строки; в частности, печатные символы US-ASCII могут быть представлены в закодированной форме.

В модифицированном UTF-7 печатные символы US-ASCII, кроме «&», представляют сами себя; то есть символы со значениями октетов 0x20-0x25 и 0x27-0x7e. Символ «&» (0x26) представлен последовательностью из двух октетов «&-».

Все остальные символы (значения октетов 0x00-0x1f, 0x7f-0xff и все 16-битные октеты Unicode) представлены в модифицированном BASE64 с дальнейшим изменением из [UTF-7], в котором вместо «/» используется «,».
Модифицированный BASE64 НЕ ДОЛЖЕН использоваться для представления любого печатного символа US-ASCII, который может представлять сам себя.

«&» используется для перехода к модифицированному BASE64 и «-» для возврата к US-ASCII. Все имена начинаются с US-ASCII и ДОЛЖНЫ заканчиваться на US-ASCII (то есть имя, заканчивающееся 16-битным октетом Unicode, ДОЛЖНО заканчиваться знаком «-»).

Прежде чем я начну его реализовывать, мой вопрос: есть ли какой-нибудь код/библиотека .NET (или даже в фреймворке), который выполняет эту работу? Мне не удалось найти ресурсы .NET (только реализации для других языков/фреймворков).

Спасибо!


person splattne    schedule 20.02.2009    source источник
comment
У меня такая же проблема. Подробнее здесь.... http://www.google.com/support/forum/p/gmail/thread?tid=79f75c2d2df9e825&hl=en   -  person    schedule 09.07.2011


Ответы (2)


Это слишком специализировано, чтобы присутствовать в рамках. В кодеплексе может быть что-то, хотя многие неполные «реализации», которые я видел, вообще не беспокоятся о преобразовании и с радостью передают все символы, отличные от us-ascii, на сервер IMAP.

Однако я реализовал это в прошлом, и на самом деле это всего 30 строк кода. Вы просматриваете все символы в строке и выводите их, если они попадают в диапазон от 0x20 до 0x7e (не забудьте добавить «-» после «&»), в противном случае собираете все не-us-ascii и конвертируете их с помощью UTF7. (или UTF8 + base64, здесь я не совсем уверен), заменив «/» на «,». Кроме того, вам необходимо поддерживать «сдвинутое состояние», например. независимо от того, кодируете ли вы в настоящее время не-us-ascii или выводите us-ascii и добавляете токены перехода «&» и «-» при изменении состояния.

person liggett78    schedule 20.02.2009

Не тестировалось, но этот код с лицензией MIT выглядит хорошо, если исправление Алексея применяется:

    /// <summary>
    /// Takes a UTF-16 encoded string and encodes it as modified UTF-7.
    /// </summary>
    /// <param name="s">The string to encode.</param>
    /// <returns>A UTF-7 encoded string</returns>
    /// <remarks>IMAP uses a modified version of UTF-7 for encoding international mailbox names. For
    /// details, refer to RFC 3501 section 5.1.3 (Mailbox International Naming Convention).</remarks>
    internal static string UTF7Encode(string s) {
        StringReader reader = new StringReader(s);
        StringBuilder builder = new StringBuilder();
        while (reader.Peek() != -1) {
            char c = (char)reader.Read();
            int codepoint = Convert.ToInt32(c);
            // It's a printable ASCII character.
            if (codepoint > 0x1F && codepoint < 0x7F) {
                builder.Append(c == '&' ? "&-" : c.ToString());
            } else {
                // The character sequence needs to be encoded.
                StringBuilder sequence = new StringBuilder(c.ToString());
                while (reader.Peek() != -1) {
                    codepoint = Convert.ToInt32((char)reader.Peek());
                    if (codepoint > 0x1F && codepoint < 0x7F)
                        break;
                    sequence.Append((char)reader.Read());
                }
                byte[] buffer = Encoding.BigEndianUnicode.GetBytes(
                    sequence.ToString());
                string encoded = Convert.ToBase64String(buffer).Replace('/', ',').
                    TrimEnd('=');
                builder.Append("&" + encoded + "-");
            }
        }
        return builder.ToString();
    }

    /// <summary>
    /// Takes a modified UTF-7 encoded string and decodes it.
    /// </summary>
    /// <param name="s">The UTF-7 encoded string to decode.</param>
    /// <returns>A UTF-16 encoded "standard" C# string</returns>
    /// <exception cref="FormatException">The input string is not a properly UTF-7 encoded
    /// string.</exception>
    /// <remarks>IMAP uses a modified version of UTF-7 for encoding international mailbox names. For
    /// details, refer to RFC 3501 section 5.1.3 (Mailbox International Naming Convention).</remarks>
    internal static string UTF7Decode(string s) {
        StringReader reader = new StringReader(s);
        StringBuilder builder = new StringBuilder();
        while (reader.Peek() != -1) {
            char c = (char)reader.Read();
            if (c == '&' && reader.Peek() != '-') {
                // The character sequence needs to be decoded.
                StringBuilder sequence = new StringBuilder();
                while (reader.Peek() != -1) {
                    if ((c = (char)reader.Read()) == '-')
                        break;
                    sequence.Append(c);
                }
                string encoded = sequence.ToString().Replace(',', '/');
                int pad = encoded.Length % 4;
                if (pad > 0)
                    encoded = encoded.PadRight(encoded.Length + (4 - pad), '=');
                try {
                    byte[] buffer = Convert.FromBase64String(encoded);
                    builder.Append(Encoding.BigEndianUnicode.GetString(buffer));
                } catch (Exception e) {
                    throw new FormatException(
                        "The input string is not in the correct Format.", e);
                }
            } else {
                if (c == '&' && reader.Peek() == '-')
                    reader.Read();
                builder.Append(c);
            }
        }
        return builder.ToString();
    }

Не используйте этот код в его текущей версии. состояние, он содержит [...] UTF7.GetBytes([...]) [...] .Replace('+', '&') - он использует существующую процедуру кодирования .Net UTF-7 и (среди прочего) заменяет + на & в результате. Это неправильно, потому что он меняет не только "символ сдвига" с + на & (что правильно и задумано), но также и все символы + в регионах с кодировкой base64 (которые нельзя менять на &). ).

person T S    schedule 17.07.2018
comment
Согласно RFC-3501, if (codepoint > 0x1F && codepoint < 0x80) должно быть if (codepoint > 0x1F && codepoint < 0x7F). Из RFC-3501: все остальные символы (значения октетов 0x00-0x1f и 0x7f-0xff) представлены в модифицированном BASE64... - person Aleksey; 26.09.2018
comment
Да, вы правы, 0x7F не является печатным символом и должен быть закодирован как BASE64. Я изменил оба вхождения оператора if в своем ответе и создал проблему. на гитхабе. - person T S; 27.09.2018
comment
или вы можете просто использовать реализацию MailKit: github.com/ jstedfast/MailKit/blob/master/MailKit/Net/Imap/ - person jstedfast; 30.11.2018