18#include "kcharsets_p.h"
19#include "kcodecs_debug.h"
33#include <QStringDecoder>
34#include <QStringEncoder>
37#define strncasecmp _strnicmp
46 auto it = std::find_if(charsetCache.
cbegin(), charsetCache.
cend(), [&name](
const QByteArray &charset) {
47 return qstricmp(name.data(), charset.data()) == 0;
49 if (it != charsetCache.
cend()) {
54 return charsetCache.
last();
61 return QByteArrayLiteral(
"UTF-8");
72 if (currentCharset != nextCharset) {
75 return CodecNames::utf8();
78 return currentCharset;
87 Codec *codec = Codec::codecForName(
"quoted-printable");
88 return codec->
encode(in, useCRLF ? Codec::NewlineCRLF : Codec::NewlineLF);
98 Codec *codec = Codec::codecForName(
"quoted-printable");
109 Codec *codec = Codec::codecForName(
"base64");
121 Codec *codec = Codec::codecForName(
"base64");
132 Codec *codec = Codec::codecForName(
"x-uuencode");
146bool parseEncodedWord(
const char *&scursor,
147 const char *
const send,
158 assert(*(scursor - 1) ==
'=');
165 char ch = *scursor++;
175 const char *charsetStart = scursor;
176 const char *languageStart =
nullptr;
180 for (; scursor != send; scursor++) {
181 if (*scursor ==
'?') {
183 }
else if (*scursor ==
'*' && languageStart ==
nullptr) {
184 languageStart = scursor + 1;
189 if (scursor == send || *scursor !=
'?') {
197 QByteArray maybeLanguage(languageStart, scursor - languageStart);
200 QByteArray maybeCharset(charsetStart, (languageStart ? languageStart - 1 : scursor) - charsetStart);
209 const char *encodingStart = scursor;
212 for (; scursor != send; scursor++) {
213 if (*scursor ==
'?') {
219 if (scursor == send || *scursor !=
'?') {
226 QByteArray maybeEncoding(encodingStart, scursor - encodingStart);
239 const char *encodedTextStart = scursor;
242 for (; scursor != send; scursor++) {
243 if (*scursor ==
'?') {
244 if (scursor + 1 != send) {
245 if (*(scursor + 1) !=
'=') {
259 if (*(scursor - 2) !=
'?' || *(scursor - 1) !=
'=' || scursor < encodedTextStart + 2) {
265 const char *
const encodedTextEnd = scursor - 2;
280 Decoder *
dec = codec->makeDecoder();
285 QStringDecoder textCodec;
287 textCodec = QStringDecoder(defaultCS.
constData());
288 cs = cachedCharset(defaultCS);
290 textCodec = QStringDecoder(maybeCharset.
constData());
291 if (!textCodec.isValid()) {
292 textCodec = QStringDecoder(defaultCS.
constData());
293 cs = cachedCharset(defaultCS);
295 cs = cachedCharset(maybeCharset);
299 *usedCS = updateEncodingCharset(*usedCS, cs);
302 if (!textCodec.isValid()) {
311 int encodedTextLength = encodedTextEnd - encodedTextStart;
313 buffer.
resize(codec->maxDecodedSizeFor(encodedTextLength));
314 char *bbegin = buffer.
data();
315 char *bend = bbegin + buffer.
length();
322 if (!
dec->decode(encodedTextStart, encodedTextEnd, bbegin, bend)) {
323 qWarning() << codec->name() <<
"codec lies about its maxDecodedSizeFor(" << encodedTextLength <<
")\nresult may be truncated";
326 *result = textCodec.decode(QByteArrayView(buffer.
data(), bbegin - buffer.
data()));
331 *language = maybeLanguage;
350 const char *scursor = src.constData();
351 const char *send = scursor + src.length();
352 bool onlySpacesSinceLastWord =
false;
357 while (scursor != send) {
359 if (isspace(*scursor) && onlySpacesSinceLastWord) {
360 spaceBuffer += *scursor++;
365 if (*scursor ==
'=') {
369 const char *
start = scursor;
370 if (parseEncodedWord(scursor, send, &decoded, &language, usedCS, defaultCS, charsetOption)) {
371 result += decoded.
toUtf8();
372 onlySpacesSinceLastWord =
true;
375 if (onlySpacesSinceLastWord) {
376 result += spaceBuffer;
377 onlySpacesSinceLastWord =
false;
385 if (onlySpacesSinceLastWord) {
386 result += spaceBuffer;
387 onlySpacesSinceLastWord =
false;
397 QStringDecoder codec(QStringDecoder::System);
399 *usedCS = updateEncodingCharset(*usedCS, cachedCharset(codec.name()));
401 return codec.decode(result);
412 bool nonAscii =
false;
413 bool useQEncoding =
false;
415 QStringEncoder codec(charset.
constData());
418 if (!codec.isValid()) {
420 codec = QStringEncoder(QStringEncoder::System);
421 usedCS = codec.name();
423 Q_ASSERT(codec.isValid());
425 usedCS = codec.name();
432 if (codec.hasError()) {
433 usedCS = CodecNames::utf8();
434 codec = QStringEncoder(QStringEncoder::Utf8);
435 encoded8Bit = codec.encode(src);
442 uint encoded8BitLength = encoded8Bit.
length();
443 for (
unsigned int i = 0; i < encoded8BitLength; i++) {
444 if (encoded8Bit[i] ==
' ') {
449 if (((
signed char)encoded8Bit[i] < 0) || (encoded8Bit[i] ==
'\033')) {
457 while ((end < encoded8Bit.
length()) && (encoded8Bit[end] !=
' ')) {
462 for (
int x = end; x < encoded8Bit.
length(); x++) {
463 if (((
signed char)encoded8Bit[x] < 0) || (encoded8Bit[x] ==
'\033')) {
466 while ((end < encoded8Bit.
length()) && (encoded8Bit[end] !=
' ')) {
473 result = encoded8Bit.
left(
start) +
"=?" + usedCS;
479 for (
int i =
start; i < end; i++) {
480 const char c = encoded8Bit[i];
484 if (((c >=
'a') && (c <=
'z')) ||
485 ((c >=
'A') && (c <=
'Z')) ||
486 ((c >=
'0') && (c <=
'9'))) {
490 hexcode = ((c & 0xF0) >> 4) + 48;
495 hexcode = (c & 0x0F) + 48;
508 result += encoded8Bit.
right(encoded8Bit.
length() - end);
510 result = encoded8Bit;
523 std::unique_ptr<KCodecs::Codec> codec;
526 static const std::array<CodecEntry, 6> s_codecs{{
527 {
"b", std::make_unique<KCodecs::Rfc2047BEncodingCodec>()},
528 {
"base64", std::make_unique<KCodecs::Base64Codec>()},
529 {
"q", std::make_unique<KCodecs::Rfc2047QEncodingCodec>()},
530 {
"quoted-printable", std::make_unique<KCodecs::QuotedPrintableCodec>()},
531 {
"x-kmime-rfc2231", std::make_unique<KCodecs::Rfc2231EncodingCodec>()},
532 {
"x-uuencode", std::make_unique<KCodecs::UUCodec>()},
535 const auto it = std::lower_bound(s_codecs.begin(), s_codecs.end(),
name, [](
const auto &lhs,
auto rhs) {
536 return rhs.compare(lhs.name, Qt::CaseInsensitive) > 0;
539 qWarning() <<
"Unknown codec \"" <<
name <<
"\" requested!";
541 return (*it).codec.get();
544bool KCodecs::Codec::encode(
const char *&scursor,
const char *
const send,
char *&dcursor,
const char *
const dend, NewlineType newline)
const
547 std::unique_ptr<Encoder> enc(makeEncoder(newline));
549 qWarning() <<
"makeEncoder failed for" << name();
554 while (!enc->encode(scursor, send, dcursor, dend)) {
555 if (dcursor == dend) {
561 while (!enc->finish(dcursor, dend)) {
562 if (dcursor == dend) {
574 result.
resize(maxEncodedSizeFor(src.size(), newline));
577 QByteArray::ConstIterator iit = src.begin();
578 QByteArray::ConstIterator iend = src.end();
579 QByteArray::Iterator oit = result.
begin();
580 QByteArray::ConstIterator oend = result.
end();
583 if (!encode(iit, iend, oit, oend, newline)) {
584 qCritical() << name() <<
"codec lies about it's mEncodedSizeFor()";
597 result.
resize(maxDecodedSizeFor(src.size(), newline));
600 QByteArray::ConstIterator iit = src.begin();
601 QByteArray::ConstIterator iend = src.end();
602 QByteArray::Iterator oit = result.
begin();
603 QByteArray::ConstIterator oend = result.
end();
606 if (!decode(iit, iend, oit, oend, newline)) {
607 qCritical() << name() <<
"codec lies about it's maxDecodedSizeFor()";
616bool KCodecs::Codec::decode(
const char *&scursor,
const char *
const send,
char *&dcursor,
const char *
const dend, NewlineType newline)
const
619 std::unique_ptr<Decoder> dec(makeDecoder(newline));
623 while (!dec->decode(scursor, send, dcursor, dend)) {
624 if (dcursor == dend) {
630 while (!dec->finish(dcursor, dend)) {
631 if (dcursor == dend) {
642KCodecs::EncoderPrivate::EncoderPrivate(Codec::NewlineType newline)
643 : outputBufferCursor(0)
649 : d(new
KCodecs::EncoderPrivate(newline))
657 if (dcursor != dend) {
663 if (d->outputBufferCursor >= maxBufferedChars) {
664 qCritical() <<
"KCodecs::Encoder: internal buffer overflow!";
666 d->outputBuffer[d->outputBufferCursor++] = ch;
678 for (i = 0; dcursor != dend && i < d->outputBufferCursor; ++i) {
679 *dcursor++ = d->outputBuffer[i];
683 int numCharsLeft = d->outputBufferCursor - i;
686 ::memmove(d->outputBuffer, d->outputBuffer + i, numCharsLeft);
689 d->outputBufferCursor = numCharsLeft;
691 return !numCharsLeft;
696 if (d->newline == Codec::NewlineCRLF) {
697 write(
'\r', dcursor, dend);
699 return write(
'\n', dcursor, dend);
705KCodecs::DecoderPrivate::DecoderPrivate(Codec::NewlineType newline)
711 : d(new
KCodecs::DecoderPrivate(newline))
An abstract base class of codecs for common mail transfer encodings.
static Codec * codecForName(QByteArrayView name)
Returns a codec associated with the specified name.
virtual const char * name() const =0
Returns the name of the encoding.
virtual bool decode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend, NewlineType newline=NewlineLF) const
Convenience wrapper that can be used for small chunks of data when you can provide a large enough buf...
virtual bool encode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend, NewlineType newline=NewlineLF) const
Convenience wrapper that can be used for small chunks of data when you can provide a large enough buf...
virtual ~Decoder()
Destroys the decoder.
Decoder(Codec::NewlineType newline=Codec::NewlineLF)
Protected constructor.
bool write(char ch, char *&dcursor, const char *const dend)
Writes character ch to the output stream or the output buffer, depending on whether or not the output...
bool flushOutputBuffer(char *&dcursor, const char *const dend)
Writes characters from the output buffer to the output stream.
virtual ~Encoder()
Destroys the encoder.
Encoder(Codec::NewlineType newline=Codec::NewlineLF)
Protected constructor.
bool writeCRLF(char *&dcursor, const char *const dend)
Convenience function.
Q_SCRIPTABLE Q_NOREPLY void start()
This file is part of the API for handling MIME data and defines the Base64 and RFC2047B Codec classes...
This file is part of the API for handling MIME data and defines the QuotedPrintable,...
This file is part of the API for handling MIME data and defines a uuencode Codec class.
A wrapper class for the most commonly used encoding and decoding algorithms.
KCODECS_EXPORT QByteArray encodeRFC2047String(QStringView src, const QByteArray &charset)
Encodes string src according to RFC2047 using charset charset.
KCODECS_EXPORT QByteArray uudecode(QByteArrayView in)
Decodes the given data using the uudecode algorithm.
KCODECS_EXPORT QByteArray base64Encode(QByteArrayView in)
Encodes the given data using the base64 algorithm.
CharsetOption
Charset options for RFC2047 encoder.
@ ForceDefaultCharset
No special option.
KCODECS_EXPORT QByteArray quotedPrintableDecode(QByteArrayView in)
Decodes a quoted-printable encoded data.
KCODECS_EXPORT QByteArray base64Decode(QByteArrayView in)
Decodes the given data that was encoded using the base64 algorithm.
KCODECS_EXPORT QByteArray quotedPrintableEncode(QByteArrayView in, bool useCRLF=true)
Encodes the given data using the quoted-printable algorithm.
KCODECS_EXPORT QString decodeRFC2047String(QStringView text)
Decodes string text according to RFC2047, i.e., the construct =?charset?[qb]?encoded?...
QByteArray::iterator begin()
const char * constData() const const
bool contains(char ch) const const
QByteArray::iterator end()
bool isEmpty() const const
QByteArray left(int len) const const
QByteArray mid(int pos, int len) const const
QByteArray right(int len) const const
QByteArray toBase64(QByteArray::Base64Options options) const const
void append(const T &value)
QList::const_iterator cbegin() const const
QList::const_iterator cend() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromUtf8(const char *str, int size)
QString toUpper() const const
QByteArray toUtf8() const const
QByteArray toUtf8() const const
QTextStream & dec(QTextStream &stream)