Кодировка пути папки IMAP (IMAP UTF-7) для .NET? - PullRequest
3 голосов
/ 20 февраля 2009

Спецификация 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 (только реализации для других языков / фреймворков ).

Спасибо!

Ответы [ 2 ]

2 голосов
/ 20 февраля 2009

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

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

0 голосов
/ 17 июля 2018

Не тестировалось, но этот код с 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 областях (которые должны не может быть изменено на &).

...