File indexing completed on 2024-04-14 14:18:28

0001 /*
0002     SPDX-FileCopyrightText: 2000-2001 Dawit Alemayehu <adawit@kde.org>
0003     SPDX-FileCopyrightText: 2001 Rik Hemsley (rikkus) <rik@kde.org>
0004     SPDX-FileCopyrightText: 2001-2002 Marc Mutz <mutz@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-only
0007 
0008     RFC 1321 "MD5 Message-Digest Algorithm" Copyright (C) 1991-1992.             // krazy:exclude=copyright
0009     RSA Data Security, Inc. Created 1991. All rights reserved.
0010 
0011     The KMD5 class is based on a C++ implementation of
0012     "RSA Data Security, Inc. MD5 Message-Digest Algorithm" by
0013     Mordechai T. Abzug,  Copyright (c) 1995.  This implementation                // krazy:exclude=copyright
0014     passes the test-suite as defined in RFC 1321.
0015 
0016     The encoding and decoding utilities in KCodecs with the exception of
0017     quoted-printable are based on the java implementation in HTTPClient
0018     package by Ronald Tschalär Copyright (C) 1996-1999.                          // krazy:exclude=copyright
0019 
0020     The quoted-printable codec as described in RFC 2045, section 6.7. is by
0021     Rik Hemsley (C) 2001.
0022 */
0023 
0024 #include "kcodecs.h"
0025 #include "kcharsets.h"
0026 #include "kcharsets_p.h"
0027 #include "kcodecs_debug.h"
0028 #include "kcodecs_p.h"
0029 #include "kcodecsbase64.h"
0030 #include "kcodecsidentity.h"
0031 #include "kcodecsqp.h"
0032 #include "kcodecsuuencode.h"
0033 
0034 #include <QMutex>
0035 
0036 #include <cassert>
0037 #include <cstring>
0038 #include <stdio.h>
0039 #include <stdlib.h>
0040 #include <string.h>
0041 
0042 #include <QDebug>
0043 #include <QHash>
0044 #include <QTextCodec>
0045 
0046 #if defined(Q_OS_WIN)
0047 #define strncasecmp _strnicmp
0048 #endif
0049 
0050 namespace KCodecs
0051 {
0052 static QList<QByteArray> charsetCache;
0053 
0054 QByteArray cachedCharset(const QByteArray &name)
0055 {
0056     auto it = std::find_if(charsetCache.cbegin(), charsetCache.cend(), [&name](const QByteArray &charset) {
0057         return qstricmp(name.data(), charset.data()) == 0;
0058     });
0059     if (it != charsetCache.cend()) {
0060         return *it;
0061     }
0062 
0063     charsetCache.append(name.toUpper());
0064     return charsetCache.last();
0065 }
0066 
0067 namespace CodecNames
0068 {
0069 QByteArray utf8()
0070 {
0071     return QByteArrayLiteral("UTF-8");
0072 }
0073 }
0074 
0075 Q_REQUIRED_RESULT
0076 QByteArray updateEncodingCharset(const QByteArray &currentCharset, const QByteArray &nextCharset)
0077 {
0078     if (!nextCharset.isEmpty()) {
0079         if (currentCharset.isEmpty()) {
0080             return nextCharset;
0081         }
0082         if (currentCharset != nextCharset) {
0083             // only one charset per string supported, so change to superset charset UTF-8,
0084             // which should  cover any possible chars
0085             return CodecNames::utf8();
0086         }
0087     }
0088     return currentCharset;
0089 }
0090 
0091 } // namespace KCodecs
0092 
0093 /******************************** KCodecs ********************************/
0094 
0095 QByteArray KCodecs::quotedPrintableEncode(const QByteArray &in, bool useCRLF)
0096 {
0097     Codec *codec = Codec::codecForName("quoted-printable");
0098     return codec->encode(in, useCRLF ? Codec::NewlineCRLF : Codec::NewlineLF);
0099 }
0100 
0101 void KCodecs::quotedPrintableEncode(const QByteArray &in, QByteArray &out, bool useCRLF)
0102 {
0103     out = quotedPrintableEncode(in, useCRLF ? Codec::NewlineCRLF : Codec::NewlineLF);
0104 }
0105 
0106 QByteArray KCodecs::quotedPrintableDecode(const QByteArray &in)
0107 {
0108     Codec *codec = Codec::codecForName("quoted-printable");
0109     return codec->decode(in);
0110 }
0111 
0112 void KCodecs::quotedPrintableDecode(const QByteArray &in, QByteArray &out)
0113 {
0114     out = quotedPrintableDecode(in);
0115 }
0116 
0117 QByteArray KCodecs::base64Encode(const QByteArray &in)
0118 {
0119     Codec *codec = Codec::codecForName("base64");
0120     return codec->encode(in);
0121 }
0122 
0123 #if KCODECS_BUILD_DEPRECATED_SINCE(5, 5)
0124 QByteArray KCodecs::base64Encode(const QByteArray &in, bool insertLFs)
0125 {
0126     Q_UNUSED(insertLFs);
0127     return base64Encode(in);
0128 }
0129 #endif
0130 
0131 void KCodecs::base64Encode(const QByteArray &in, QByteArray &out, bool insertLFs)
0132 {
0133     Q_UNUSED(insertLFs);
0134     out = base64Encode(in);
0135 }
0136 
0137 QByteArray KCodecs::base64Decode(const QByteArray &in)
0138 {
0139     Codec *codec = Codec::codecForName("base64");
0140     return codec->decode(in);
0141 }
0142 
0143 void KCodecs::base64Decode(const QByteArray &in, QByteArray &out)
0144 {
0145     out = base64Decode(in);
0146 }
0147 
0148 #if KCODECS_BUILD_DEPRECATED_SINCE(5, 56)
0149 QByteArray KCodecs::uuencode(const QByteArray &in)
0150 {
0151     Codec *codec = Codec::codecForName("x-uuencode");
0152     return codec->encode(in);
0153 }
0154 #endif
0155 
0156 #if KCODECS_BUILD_DEPRECATED_SINCE(5, 56)
0157 void KCodecs::uuencode(const QByteArray &in, QByteArray &out)
0158 {
0159     out = uuencode(in);
0160 }
0161 #endif
0162 
0163 QByteArray KCodecs::uudecode(const QByteArray &in)
0164 {
0165     Codec *codec = Codec::codecForName("x-uuencode");
0166     return codec->decode(in);
0167 }
0168 
0169 void KCodecs::uudecode(const QByteArray &in, QByteArray &out)
0170 {
0171     out = uudecode(in);
0172 }
0173 
0174 //@cond PRIVATE
0175 
0176 namespace KCodecs
0177 {
0178 // parse the encoded-word (scursor points to after the initial '=')
0179 bool parseEncodedWord(const char *&scursor,
0180                       const char *const send,
0181                       QString *result,
0182                       QByteArray *language,
0183                       QByteArray *usedCS,
0184                       const QByteArray &defaultCS,
0185                       CharsetOption charsetOption)
0186 {
0187     assert(result);
0188     assert(language);
0189 
0190     // make sure the caller already did a bit of the work.
0191     assert(*(scursor - 1) == '=');
0192 
0193     //
0194     // STEP 1:
0195     // scan for the charset/language portion of the encoded-word
0196     //
0197 
0198     char ch = *scursor++;
0199 
0200     if (ch != '?') {
0201         // qCDebug(KCODECS_LOG) << "first";
0202         // qCDebug(KCODECS_LOG) << "Premature end of encoded word";
0203         return false;
0204     }
0205 
0206     // remember start of charset (ie. just after the initial "=?") and
0207     // language (just after the first '*') fields:
0208     const char *charsetStart = scursor;
0209     const char *languageStart = nullptr;
0210 
0211     // find delimiting '?' (and the '*' separating charset and language
0212     // tags, if any):
0213     for (; scursor != send; scursor++) {
0214         if (*scursor == '?') {
0215             break;
0216         } else if (*scursor == '*' && languageStart == nullptr) {
0217             languageStart = scursor + 1;
0218         }
0219     }
0220 
0221     // not found? can't be an encoded-word!
0222     if (scursor == send || *scursor != '?') {
0223         // qCDebug(KCODECS_LOG) << "second";
0224         // qCDebug(KCODECS_LOG) << "Premature end of encoded word";
0225         return false;
0226     }
0227 
0228     // extract the language information, if any (if languageStart is 0,
0229     // language will be null, too):
0230     QByteArray maybeLanguage(languageStart, scursor - languageStart);
0231     // extract charset information (keep in mind: the size given to the
0232     // ctor is one off due to the \0 terminator):
0233     QByteArray maybeCharset(charsetStart, (languageStart ? languageStart - 1 : scursor) - charsetStart);
0234 
0235     //
0236     // STEP 2:
0237     // scan for the encoding portion of the encoded-word
0238     //
0239 
0240     // remember start of encoding (just _after_ the second '?'):
0241     scursor++;
0242     const char *encodingStart = scursor;
0243 
0244     // find next '?' (ending the encoding tag):
0245     for (; scursor != send; scursor++) {
0246         if (*scursor == '?') {
0247             break;
0248         }
0249     }
0250 
0251     // not found? Can't be an encoded-word!
0252     if (scursor == send || *scursor != '?') {
0253         // qCDebug(KCODECS_LOG) << "third";
0254         // qCDebug(KCODECS_LOG) << "Premature end of encoded word";
0255         return false;
0256     }
0257 
0258     // extract the encoding information:
0259     QByteArray maybeEncoding(encodingStart, scursor - encodingStart);
0260 
0261     // qCDebug(KCODECS_LOG) << "parseEncodedWord: found charset == \"" << maybeCharset
0262     //         << "\"; language == \"" << maybeLanguage
0263     //         << "\"; encoding == \"" << maybeEncoding << "\"";
0264 
0265     //
0266     // STEP 3:
0267     // scan for encoded-text portion of encoded-word
0268     //
0269 
0270     // remember start of encoded-text (just after the third '?'):
0271     scursor++;
0272     const char *encodedTextStart = scursor;
0273 
0274     // find the '?=' sequence (ending the encoded-text):
0275     for (; scursor != send; scursor++) {
0276         if (*scursor == '?') {
0277             if (scursor + 1 != send) {
0278                 if (*(scursor + 1) != '=') { // We expect a '=' after the '?', but we got something else; ignore
0279                     // qCDebug(KCODECS_LOG) << "Stray '?' in q-encoded word, ignoring this.";
0280                     continue;
0281                 } else { // yep, found a '?=' sequence
0282                     scursor += 2;
0283                     break;
0284                 }
0285             } else { // The '?' is the last char, but we need a '=' after it!
0286                 // qCDebug(KCODECS_LOG) << "Premature end of encoded word";
0287                 return false;
0288             }
0289         }
0290     }
0291 
0292     if (*(scursor - 2) != '?' || *(scursor - 1) != '=' || scursor < encodedTextStart + 2) {
0293         // qCDebug(KCODECS_LOG) << "Premature end of encoded word";
0294         return false;
0295     }
0296 
0297     // set end sentinel for encoded-text:
0298     const char *const encodedTextEnd = scursor - 2;
0299 
0300     //
0301     // STEP 4:
0302     // setup decoders for the transfer encoding and the charset
0303     //
0304 
0305     // try if there's a codec for the encoding found:
0306     Codec *codec = Codec::codecForName(maybeEncoding);
0307     if (!codec) {
0308         // qCDebug(KCODECS_LOG) << "Unknown encoding" << maybeEncoding;
0309         return false;
0310     }
0311 
0312     // get an instance of a corresponding decoder:
0313     Decoder *dec = codec->makeDecoder();
0314     assert(dec);
0315 
0316     // try if there's a (text)codec for the charset found:
0317     bool matchOK = false;
0318     QByteArray cs;
0319     QTextCodec *textCodec = nullptr;
0320     if (charsetOption == KCodecs::ForceDefaultCharset || maybeCharset.isEmpty()) {
0321         textCodec = KCharsets::charsets()->d->codecForName(QLatin1String(defaultCS), matchOK);
0322         cs = cachedCharset(defaultCS);
0323     } else {
0324         textCodec = KCharsets::charsets()->d->codecForName(QLatin1String(maybeCharset), matchOK);
0325         if (!matchOK) { // no suitable codec found => use default charset
0326             textCodec = KCharsets::charsets()->d->codecForName(QLatin1String(defaultCS), matchOK);
0327             cs = cachedCharset(defaultCS);
0328         } else {
0329             cs = cachedCharset(maybeCharset);
0330         }
0331     }
0332     if (usedCS) {
0333         *usedCS = updateEncodingCharset(*usedCS, cs);
0334     }
0335 
0336     if (!matchOK || !textCodec) {
0337         // qCDebug(KCODECS_LOG) << "Unknown charset" << maybeCharset;
0338         delete dec;
0339         return false;
0340     };
0341 
0342     // qCDebug(KCODECS_LOG) << "mimeName(): \"" << textCodec->name() << "\"";
0343 
0344     // allocate a temporary buffer to store the 8bit text:
0345     int encodedTextLength = encodedTextEnd - encodedTextStart;
0346     QByteArray buffer;
0347     buffer.resize(codec->maxDecodedSizeFor(encodedTextLength));
0348     char *bbegin = buffer.data();
0349     char *bend = bbegin + buffer.length();
0350 
0351     //
0352     // STEP 5:
0353     // do the actual decoding
0354     //
0355 
0356     if (!dec->decode(encodedTextStart, encodedTextEnd, bbegin, bend)) {
0357         qWarning() << codec->name() << "codec lies about its maxDecodedSizeFor(" << encodedTextLength << ")\nresult may be truncated";
0358     }
0359 
0360     *result = textCodec->toUnicode(buffer.data(), bbegin - buffer.data());
0361 
0362     // qCDebug(KCODECS_LOG) << "result now: \"" << result << "\"";
0363     // cleanup:
0364     delete dec;
0365     *language = maybeLanguage;
0366 
0367     return true;
0368 }
0369 
0370 } // namespace KCodecs
0371 
0372 //@endcond
0373 
0374 QString KCodecs::decodeRFC2047String(const QString &msg)
0375 {
0376     QByteArray usedCS;
0377     return decodeRFC2047String(msg.toUtf8(), &usedCS, CodecNames::utf8(), NoOption);
0378 }
0379 
0380 QString KCodecs::decodeRFC2047String(const QByteArray &src, QByteArray *usedCS, const QByteArray &defaultCS, CharsetOption charsetOption)
0381 {
0382     QByteArray result;
0383     QByteArray spaceBuffer;
0384     const char *scursor = src.constData();
0385     const char *send = scursor + src.length();
0386     bool onlySpacesSinceLastWord = false;
0387     if (usedCS) {
0388         usedCS->clear();
0389     }
0390 
0391     while (scursor != send) {
0392         // space
0393         if (isspace(*scursor) && onlySpacesSinceLastWord) {
0394             spaceBuffer += *scursor++;
0395             continue;
0396         }
0397 
0398         // possible start of an encoded word
0399         if (*scursor == '=') {
0400             QByteArray language;
0401             QString decoded;
0402             ++scursor;
0403             const char *start = scursor;
0404             if (parseEncodedWord(scursor, send, &decoded, &language, usedCS, defaultCS, charsetOption)) {
0405                 result += decoded.toUtf8();
0406                 onlySpacesSinceLastWord = true;
0407                 spaceBuffer.clear();
0408             } else {
0409                 if (onlySpacesSinceLastWord) {
0410                     result += spaceBuffer;
0411                     onlySpacesSinceLastWord = false;
0412                 }
0413                 result += '=';
0414                 scursor = start; // reset cursor after parsing failure
0415             }
0416             continue;
0417         } else {
0418             // unencoded data
0419             if (onlySpacesSinceLastWord) {
0420                 result += spaceBuffer;
0421                 onlySpacesSinceLastWord = false;
0422             }
0423             result += *scursor;
0424             ++scursor;
0425         }
0426     }
0427     // If there are any chars that couldn't be decoded in UTF-8,
0428     // fallback to local codec
0429     const QString tryUtf8 = QString::fromUtf8(result);
0430     if (tryUtf8.contains(QChar(0xFFFD))) {
0431         QTextCodec *codec = QTextCodec::codecForLocale();
0432         if (usedCS) {
0433             *usedCS = updateEncodingCharset(*usedCS, cachedCharset(codec->name()));
0434         }
0435         return codec->toUnicode(result);
0436     } else {
0437         return tryUtf8;
0438     }
0439 }
0440 
0441 QByteArray KCodecs::encodeRFC2047String(const QString &src, const QByteArray &charset)
0442 {
0443     QByteArray result;
0444     int start = 0;
0445     int end = 0;
0446     bool nonAscii = false;
0447     bool ok = true;
0448     bool useQEncoding = false;
0449 
0450     // fromLatin1() is safe here, codecForName() uses toLatin1() internally
0451     const QTextCodec *codec = KCharsets::charsets()->d->codecForName(QString::fromLatin1(charset), ok);
0452 
0453     QByteArray usedCS;
0454     if (!ok) {
0455         // no codec available => try local8Bit and hope the best ;-)
0456         codec = QTextCodec::codecForLocale();
0457         usedCS = codec->name();
0458     } else {
0459         Q_ASSERT(codec);
0460         if (charset.isEmpty()) {
0461             usedCS = codec->name();
0462         } else {
0463             usedCS = charset;
0464         }
0465     }
0466 
0467     QTextCodec::ConverterState converterState(QTextCodec::IgnoreHeader);
0468     QByteArray encoded8Bit = codec->fromUnicode(src.constData(), src.length(), &converterState);
0469     if (converterState.invalidChars > 0) {
0470         usedCS = CodecNames::utf8();
0471         codec = QTextCodec::codecForName(usedCS);
0472         encoded8Bit = codec->fromUnicode(src);
0473     }
0474 
0475     if (usedCS.contains("8859-")) { // use "B"-Encoding for non iso-8859-x charsets
0476         useQEncoding = true;
0477     }
0478 
0479     uint encoded8BitLength = encoded8Bit.length();
0480     for (unsigned int i = 0; i < encoded8BitLength; i++) {
0481         if (encoded8Bit[i] == ' ') { // encoding starts at word boundaries
0482             start = i + 1;
0483         }
0484 
0485         // encode escape character, for japanese encodings...
0486         if (((signed char)encoded8Bit[i] < 0) || (encoded8Bit[i] == '\033')) {
0487             end = start; // non us-ascii char found, now we determine where to stop encoding
0488             nonAscii = true;
0489             break;
0490         }
0491     }
0492 
0493     if (nonAscii) {
0494         while ((end < encoded8Bit.length()) && (encoded8Bit[end] != ' ')) {
0495             // we encode complete words
0496             end++;
0497         }
0498 
0499         for (int x = end; x < encoded8Bit.length(); x++) {
0500             if (((signed char)encoded8Bit[x] < 0) || (encoded8Bit[x] == '\033')) {
0501                 end = x; // we found another non-ascii word
0502 
0503                 while ((end < encoded8Bit.length()) && (encoded8Bit[end] != ' ')) {
0504                     // we encode complete words
0505                     end++;
0506                 }
0507             }
0508         }
0509 
0510         result = encoded8Bit.left(start) + "=?" + usedCS;
0511 
0512         if (useQEncoding) {
0513             result += "?Q?";
0514 
0515             char hexcode; // "Q"-encoding implementation described in RFC 2047
0516             for (int i = start; i < end; i++) {
0517                 const char c = encoded8Bit[i];
0518                 if (c == ' ') { // make the result readable with not MIME-capable readers
0519                     result += '_';
0520                 } else {
0521                     if (((c >= 'a') && (c <= 'z')) || // paranoid mode, encode *all* special chars to avoid problems
0522                         ((c >= 'A') && (c <= 'Z')) || // with "From" & "To" headers
0523                         ((c >= '0') && (c <= '9'))) {
0524                         result += c;
0525                     } else {
0526                         result += '='; // "stolen" from KMail ;-)
0527                         hexcode = ((c & 0xF0) >> 4) + 48;
0528                         if (hexcode >= 58) {
0529                             hexcode += 7;
0530                         }
0531                         result += hexcode;
0532                         hexcode = (c & 0x0F) + 48;
0533                         if (hexcode >= 58) {
0534                             hexcode += 7;
0535                         }
0536                         result += hexcode;
0537                     }
0538                 }
0539             }
0540         } else {
0541             result += "?B?" + encoded8Bit.mid(start, end - start).toBase64();
0542         }
0543 
0544         result += "?=";
0545         result += encoded8Bit.right(encoded8Bit.length() - end);
0546     } else {
0547         result = encoded8Bit;
0548     }
0549 
0550     return result;
0551 }
0552 
0553 /******************************************************************************/
0554 /*                           KCodecs::Codec                                   */
0555 
0556 // global list of KCodecs::Codec's
0557 //@cond PRIVATE
0558 namespace
0559 {
0560 static QHash<QByteArray, KCodecs::Codec *> *allCodecs = nullptr;
0561 Q_GLOBAL_STATIC(QMutex, dictLock)
0562 
0563 static void createCodecs()
0564 {
0565     // all->insert( "7bit", new KCodecs::SevenBitCodec() );
0566     // all->insert( "8bit", new KCodecs::EightBitCodec() );
0567     allCodecs->insert("base64", new KCodecs::Base64Codec());
0568     allCodecs->insert("quoted-printable", new KCodecs::QuotedPrintableCodec());
0569     allCodecs->insert("b", new KCodecs::Rfc2047BEncodingCodec());
0570     allCodecs->insert("q", new KCodecs::Rfc2047QEncodingCodec());
0571     allCodecs->insert("x-kmime-rfc2231", new KCodecs::Rfc2231EncodingCodec());
0572     allCodecs->insert("x-uuencode", new KCodecs::UUCodec());
0573     // all->insert( "binary", new KCodecs::BinaryCodec() );
0574 }
0575 
0576 static void cleanupCodecs()
0577 {
0578     qDeleteAll(*allCodecs);
0579     delete allCodecs;
0580     allCodecs = nullptr;
0581 }
0582 
0583 }
0584 //@endcond
0585 
0586 KCodecs::Codec *KCodecs::Codec::codecForName(const char *name)
0587 {
0588     const QByteArray ba(name);
0589     return codecForName(ba);
0590 }
0591 
0592 KCodecs::Codec *KCodecs::Codec::codecForName(const QByteArray &name)
0593 {
0594     QMutexLocker locker(dictLock()); // protect "allCodecs"
0595     if (!allCodecs) {
0596         allCodecs = new QHash<QByteArray, Codec *>();
0597         qAddPostRoutine(cleanupCodecs);
0598         createCodecs();
0599     }
0600     QByteArray lowerName = name.toLower();
0601     Codec *codec = (*allCodecs).value(lowerName);
0602 
0603     if (!codec) {
0604         qWarning() << "Unknown codec \"" << name << "\" requested!";
0605     }
0606 
0607     return codec;
0608 }
0609 
0610 bool KCodecs::Codec::encode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend, NewlineType newline) const
0611 {
0612     // get an encoder:
0613     std::unique_ptr<Encoder> enc(makeEncoder(newline));
0614     if (!enc) {
0615         qWarning() << "makeEncoder failed for" << name();
0616         return false;
0617     }
0618 
0619     // encode and check for output buffer overflow:
0620     while (!enc->encode(scursor, send, dcursor, dend)) {
0621         if (dcursor == dend) {
0622             return false; // not enough space in output buffer
0623         }
0624     }
0625 
0626     // finish and check for output buffer overflow:
0627     while (!enc->finish(dcursor, dend)) {
0628         if (dcursor == dend) {
0629             return false; // not enough space in output buffer
0630         }
0631     }
0632 
0633     return true; // successfully encoded.
0634 }
0635 
0636 QByteArray KCodecs::Codec::encode(const QByteArray &src, NewlineType newline) const
0637 {
0638     // allocate buffer for the worst case:
0639     QByteArray result;
0640     result.resize(maxEncodedSizeFor(src.size(), newline));
0641 
0642     // set up iterators:
0643     QByteArray::ConstIterator iit = src.begin();
0644     QByteArray::ConstIterator iend = src.end();
0645     QByteArray::Iterator oit = result.begin();
0646     QByteArray::ConstIterator oend = result.end();
0647 
0648     // encode
0649     if (!encode(iit, iend, oit, oend, newline)) {
0650         qCritical() << name() << "codec lies about it's mEncodedSizeFor()";
0651     }
0652 
0653     // shrink result to actual size:
0654     result.truncate(oit - result.begin());
0655 
0656     return result;
0657 }
0658 
0659 QByteArray KCodecs::Codec::decode(const QByteArray &src, NewlineType newline) const
0660 {
0661     // allocate buffer for the worst case:
0662     QByteArray result;
0663     result.resize(maxDecodedSizeFor(src.size(), newline));
0664 
0665     // set up iterators:
0666     QByteArray::ConstIterator iit = src.begin();
0667     QByteArray::ConstIterator iend = src.end();
0668     QByteArray::Iterator oit = result.begin();
0669     QByteArray::ConstIterator oend = result.end();
0670 
0671     // decode
0672     if (!decode(iit, iend, oit, oend, newline)) {
0673         qCritical() << name() << "codec lies about it's maxDecodedSizeFor()";
0674     }
0675 
0676     // shrink result to actual size:
0677     result.truncate(oit - result.begin());
0678 
0679     return result;
0680 }
0681 
0682 bool KCodecs::Codec::decode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend, NewlineType newline) const
0683 {
0684     // get a decoder:
0685     std::unique_ptr<Decoder> dec(makeDecoder(newline));
0686     assert(dec);
0687 
0688     // decode and check for output buffer overflow:
0689     while (!dec->decode(scursor, send, dcursor, dend)) {
0690         if (dcursor == dend) {
0691             return false; // not enough space in output buffer
0692         }
0693     }
0694 
0695     // finish and check for output buffer overflow:
0696     while (!dec->finish(dcursor, dend)) {
0697         if (dcursor == dend) {
0698             return false; // not enough space in output buffer
0699         }
0700     }
0701 
0702     return true; // successfully encoded.
0703 }
0704 
0705 /******************************************************************************/
0706 /*                          KCodecs::Encoder                                  */
0707 
0708 KCodecs::EncoderPrivate::EncoderPrivate(Codec::NewlineType newline)
0709     : outputBufferCursor(0)
0710     , newline(newline)
0711 {
0712 }
0713 
0714 KCodecs::Encoder::Encoder(Codec::NewlineType newline)
0715     : d(new KCodecs::EncoderPrivate(newline))
0716 {
0717 }
0718 
0719 KCodecs::Encoder::~Encoder() = default;
0720 
0721 bool KCodecs::Encoder::write(char ch, char *&dcursor, const char *const dend)
0722 {
0723     if (dcursor != dend) {
0724         // if there's space in the output stream, write there:
0725         *dcursor++ = ch;
0726         return true;
0727     } else {
0728         // else buffer the output:
0729         if (d->outputBufferCursor >= maxBufferedChars) {
0730             qCritical() << "KCodecs::Encoder: internal buffer overflow!";
0731         } else {
0732             d->outputBuffer[d->outputBufferCursor++] = ch;
0733         }
0734         return false;
0735     }
0736 }
0737 
0738 // write as much as possible off the output buffer. Return true if
0739 // flushing was complete, false if some chars could not be flushed.
0740 bool KCodecs::Encoder::flushOutputBuffer(char *&dcursor, const char *const dend)
0741 {
0742     int i;
0743     // copy output buffer to output stream:
0744     for (i = 0; dcursor != dend && i < d->outputBufferCursor; ++i) {
0745         *dcursor++ = d->outputBuffer[i];
0746     }
0747 
0748     // calculate the number of missing chars:
0749     int numCharsLeft = d->outputBufferCursor - i;
0750     // push the remaining chars to the begin of the buffer:
0751     if (numCharsLeft) {
0752         ::memmove(d->outputBuffer, d->outputBuffer + i, numCharsLeft);
0753     }
0754     // adjust cursor:
0755     d->outputBufferCursor = numCharsLeft;
0756 
0757     return !numCharsLeft;
0758 }
0759 
0760 bool KCodecs::Encoder::writeCRLF(char *&dcursor, const char *const dend)
0761 {
0762     if (d->newline == Codec::NewlineCRLF) {
0763         write('\r', dcursor, dend);
0764     }
0765     return write('\n', dcursor, dend);
0766 }
0767 
0768 /******************************************************************************/
0769 /*                           KCodecs::Decoder                                 */
0770 
0771 KCodecs::DecoderPrivate::DecoderPrivate(Codec::NewlineType newline)
0772     : newline(newline)
0773 {
0774 }
0775 
0776 KCodecs::Decoder::Decoder(Codec::NewlineType newline)
0777     : d(new KCodecs::DecoderPrivate(newline))
0778 {
0779 }
0780 
0781 KCodecs::Decoder::~Decoder() = default;