encodeHeader method

  1. @override
String encodeHeader(
  1. String text, {
  2. int nameLength = 0,
  3. Codec codec = utf8,
  4. bool fromStart = false,
  5. bool isPhrase = false,
})
override

Encodes the header text in Q encoding only if required.

Compare https://tools.ietf.org/html/rfc2047#section-4.2 for details. text specifies the text to be encoded. nameLength the length of the header name, for calculating the wrapping point. codec the optional codec, which defaults to utf8. Set the optional fromStart to true in case the encoding should start at the beginning of the text and not in the middle. Set isPhrase to true when the text replaces a phrase, e.g. the display name of an address: the whole text is then always encoded and only the characters allowed by RFC 2047 section 5 (3) are left as is.

Implementation

@override
String encodeHeader(
  final String text, {
  int nameLength = 0,
  Codec codec = utf8,
  bool fromStart = false,
  bool isPhrase = false,
}) {
  final runes = List.from(text.runes, growable: false);
  var numberOfRunesAbove7Bit = 0;
  var startIndex = -1;
  var endIndex = -1;
  final runeCount = runes.length;

  for (var runeIndex = 0; runeIndex < runeCount; runeIndex++) {
    final rune = runes[runeIndex];
    if (rune > 127) {
      numberOfRunesAbove7Bit++;
      if (startIndex == -1) {
        startIndex = runeIndex;
        endIndex = runeIndex;
      } else {
        endIndex = runeIndex;
      }
    }
  }
  if (numberOfRunesAbove7Bit == 0 && !isPhrase) {
    return text;
  } else {
    // TODO Set the correct encoding
    const qpWordHead = '=?UTF-8?Q?';
    const qpWordTail = '?=';
    const qpWordDelimiterSize = qpWordHead.length + qpWordTail.length;
    if (fromStart || isPhrase) {
      startIndex = 0;
      // the loop below works on runes, not on UTF-16 code units
      endIndex = runeCount - 1;
    }
    // Available space for the current encoded word
    var qpWordSize =
        MailConventions.encodedWordMaxLength -
        qpWordDelimiterSize -
        startIndex -
        (nameLength + 2);
    // Counts the characters of the current encoded word
    var wordCounter = 0;
    // True when reached the end of the current word available space
    var isWordSplit = false;
    final buffer = StringBuffer();
    for (var runeIndex = 0; runeIndex < runeCount; runeIndex++) {
      final rune = runes[runeIndex];
      if (runeIndex < startIndex || runeIndex > endIndex) {
        buffer.writeCharCode(rune);
        continue;
      }
      if (runeIndex == startIndex || isWordSplit) {
        // Adds the line terminator
        if (isWordSplit) {
          buffer
            ..write(qpWordTail)
            // NOTE Per specification, a CRLF should be inserted here,
            // but the folding occurs on the rendering function.
            // Here we leave only the WSP marker to separate each q-encode
            // word.
            // ..writeCharCode(AsciiRunes.runeCarriageReturn)
            // ..writeCharCode(AsciiRunes.runeLineFeed)
            // Assumes per default a single leading space for header folding
            ..writeCharCode(AsciiRunes.runeSpace);
          // Resets the split flag
          isWordSplit = false;
          // Calculates the new encoded word size
          qpWordSize =
              MailConventions.encodedWordMaxLength - qpWordDelimiterSize - 1;
        }
        buffer.write(qpWordHead);
      }
      if (_isLiteralInEncodedWord(rune, isPhrase: isPhrase)) {
        wordCounter++;
        isWordSplit = wordCounter > qpWordSize;
        if (!isWordSplit) {
          buffer.writeCharCode(rune);
        }
      } else if (rune == AsciiRunes.runeSpace) {
        wordCounter++;
        isWordSplit = wordCounter > qpWordSize;
        if (!isWordSplit) {
          buffer.write('_');
        }
      } else {
        // _writeQuotedPrintable(rune, buffer, codec);
        final quoted = _encodeQuotedPrintableChar(rune, codec);
        wordCounter += quoted.length;
        isWordSplit = wordCounter > qpWordSize;
        if (!isWordSplit) {
          buffer.write(quoted);
        }
      }
      if (isWordSplit) {
        wordCounter = 0;
        runeIndex--;
      }
      if (runeIndex == endIndex) {
        buffer.write(qpWordTail);
      }
    }

    return buffer.toString();
  }
}