File indexing completed on 2024-05-12 05:46:38

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