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