File indexing completed on 2024-09-08 12:13:47
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 ¤tCharset, 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;