File indexing completed on 2024-10-13 03:36:28
0001 /* -*- c++ -*- 0002 SPDX-FileCopyrightText: 2002 Marc Mutz <mutz@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 /** 0007 @file 0008 This file is part of the API for handling @ref MIME data and 0009 defines the @ref QuotedPrintable, @ref RFC2047Q, and 0010 @ref RFC2231 @ref Codec classes. 0011 0012 @brief 0013 Defines the classes QuotedPrintableCodec, Rfc2047QEncodingCodec, and 0014 Rfc2231EncodingCodec. 0015 0016 @authors Marc Mutz \<mutz@kde.org\> 0017 */ 0018 0019 #include "kcodecsqp.h" 0020 #include "kcodecs_p.h" 0021 0022 #include <QDebug> 0023 0024 #include <cassert> 0025 0026 using namespace KCodecs; 0027 0028 namespace KCodecs 0029 { 0030 // none except a-zA-Z0-9!*+-/ 0031 const uchar eTextMap[16] = {0x00, 0x00, 0x00, 0x00, 0x40, 0x35, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xE0, 0x7F, 0xFF, 0xFF, 0xE0}; 0032 0033 // some helpful functions: 0034 0035 /** 0036 Converts a 4-bit @p value into its hexadecimal characater representation. 0037 So input of value [0,15] returns ['0','1',... 'F']. Input values 0038 greater than 15 will produce undesired results. 0039 @param value is an unsigned character containing the 4-bit input value. 0040 */ 0041 static inline char binToHex(uchar value) 0042 { 0043 if (value > 9) { 0044 return value + 'A' - 10; 0045 } else { 0046 return value + '0'; 0047 } 0048 } 0049 0050 /** 0051 Returns the high-order 4 bits of an 8-bit value in another 8-bit value. 0052 @param ch is an unsigned character containing the 8-bit input value. 0053 */ 0054 static inline uchar highNibble(uchar ch) 0055 { 0056 return ch >> 4; 0057 } 0058 0059 /** 0060 Returns the low-order 4 bits of an 8-bit value in another 8-bit value. 0061 @param ch is an unsigned character containing the 8-bit input value. 0062 */ 0063 static inline uchar lowNibble(uchar ch) 0064 { 0065 return ch & 0xF; 0066 } 0067 0068 /** 0069 Returns true if the specified value is a not Control character or 0070 question mark; else true. 0071 @param ch is an unsigned character containing the 8-bit input value. 0072 */ 0073 static inline bool keep(uchar ch) 0074 { 0075 // no CTLs, except HT and not '?' 0076 return !((ch < ' ' && ch != '\t') || ch == '?'); 0077 } 0078 0079 // 0080 // QuotedPrintableCodec 0081 // 0082 0083 class QuotedPrintableEncoder : public Encoder 0084 { 0085 char mInputBuffer[16]; 0086 uchar mCurrentLineLength; // 0..76 0087 uchar mAccu; 0088 uint mInputBufferReadCursor : 4; // 0..15 0089 uint mInputBufferWriteCursor : 4; // 0..15 0090 enum { 0091 Never, 0092 AtBOL, 0093 Definitely, 0094 } mAccuNeedsEncoding : 2; 0095 bool mSawLineEnd : 1; 0096 bool mSawCR : 1; 0097 bool mFinishing : 1; 0098 bool mFinished : 1; 0099 0100 protected: 0101 friend class QuotedPrintableCodec; 0102 QuotedPrintableEncoder(Codec::NewlineType newline = Codec::NewlineLF) 0103 : Encoder(newline) 0104 , mCurrentLineLength(0) 0105 , mAccu(0) 0106 , mInputBufferReadCursor(0) 0107 , mInputBufferWriteCursor(0) 0108 , mAccuNeedsEncoding(Never) 0109 , mSawLineEnd(false) 0110 , mSawCR(false) 0111 , mFinishing(false) 0112 , mFinished(false) 0113 { 0114 } 0115 0116 bool needsEncoding(uchar ch) 0117 { 0118 return ch > '~' || (ch < ' ' && ch != '\t') || ch == '='; 0119 } 0120 bool needsEncodingAtEOL(uchar ch) 0121 { 0122 return ch == ' ' || ch == '\t'; 0123 } 0124 bool needsEncodingAtBOL(uchar ch) 0125 { 0126 return ch == 'F' || ch == '.' || ch == '-'; 0127 } 0128 bool fillInputBuffer(const char *&scursor, const char *const send); 0129 bool processNextChar(); 0130 void createOutputBuffer(char *&dcursor, const char *const dend); 0131 0132 public: 0133 ~QuotedPrintableEncoder() override 0134 { 0135 } 0136 0137 bool encode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend) override; 0138 0139 bool finish(char *&dcursor, const char *const dend) override; 0140 }; 0141 0142 class QuotedPrintableDecoder : public Decoder 0143 { 0144 const char mEscapeChar; 0145 char mBadChar; 0146 /** @p accu holds the msb nibble of the hexchar or zero. */ 0147 uchar mAccu; 0148 /** @p insideHexChar is true iff we're inside an hexchar (=XY). 0149 Together with @ref mAccu, we can build this states: 0150 @li @p insideHexChar == @p false: 0151 normal text 0152 @li @p insideHexChar == @p true, @p mAccu == 0: 0153 saw the leading '=' 0154 @li @p insideHexChar == @p true, @p mAccu != 0: 0155 saw the first nibble '=X' 0156 */ 0157 const bool mQEncoding; 0158 bool mInsideHexChar; 0159 bool mFlushing; 0160 bool mExpectLF; 0161 bool mHaveAccu; 0162 /** @p mLastChar holds the first char of an encoded char, so that 0163 we are able to keep the first char if the second char is invalid. */ 0164 char mLastChar; 0165 0166 protected: 0167 friend class QuotedPrintableCodec; 0168 friend class Rfc2047QEncodingCodec; 0169 friend class Rfc2231EncodingCodec; 0170 QuotedPrintableDecoder(Codec::NewlineType newline = Codec::NewlineLF, bool aQEncoding = false, char aEscapeChar = '=') 0171 : Decoder(newline) 0172 , mEscapeChar(aEscapeChar) 0173 , mBadChar(0) 0174 , mAccu(0) 0175 , mQEncoding(aQEncoding) 0176 , mInsideHexChar(false) 0177 , mFlushing(false) 0178 , mExpectLF(false) 0179 , mHaveAccu(false) 0180 , mLastChar(0) 0181 { 0182 } 0183 0184 public: 0185 ~QuotedPrintableDecoder() override 0186 { 0187 } 0188 0189 bool decode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend) override; 0190 bool finish(char *&dcursor, const char *const dend) override; 0191 }; 0192 0193 class Rfc2047QEncodingEncoder : public Encoder 0194 { 0195 uchar mAccu; 0196 uchar mStepNo; 0197 const char mEscapeChar; 0198 bool mInsideFinishing : 1; 0199 0200 protected: 0201 friend class Rfc2047QEncodingCodec; 0202 friend class Rfc2231EncodingCodec; 0203 Rfc2047QEncodingEncoder(Codec::NewlineType newline = Codec::NewlineLF, char aEscapeChar = '=') 0204 : Encoder(newline) 0205 , mAccu(0) 0206 , mStepNo(0) 0207 , mEscapeChar(aEscapeChar) 0208 , mInsideFinishing(false) 0209 { 0210 // else an optimization in ::encode might break. 0211 assert(aEscapeChar == '=' || aEscapeChar == '%'); 0212 } 0213 0214 bool isEText(uchar ch) 0215 { 0216 return (ch < 128) && (eTextMap[ch / 8] & 0x80 >> ch % 8); 0217 } 0218 0219 // this code assumes that isEText( mEscapeChar ) == false! 0220 bool needsEncoding(uchar ch) 0221 { 0222 if (ch > 'z') { 0223 return true; // {|}~ DEL and 8bit chars need 0224 } 0225 if (!isEText(ch)) { 0226 return true; // all but a-zA-Z0-9!/*+- need, too 0227 } 0228 if (mEscapeChar == '%' && (ch == '*' || ch == '/')) { 0229 return true; // not allowed in rfc2231 encoding 0230 } 0231 return false; 0232 } 0233 0234 public: 0235 ~Rfc2047QEncodingEncoder() override 0236 { 0237 } 0238 0239 bool encode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend) override; 0240 bool finish(char *&dcursor, const char *const dend) override; 0241 }; 0242 0243 // this doesn't access any member variables, so it can be defined static 0244 // but then we can't call it from virtual functions 0245 static qsizetype QuotedPrintableDecoder_maxDecodedSizeFor(qsizetype insize, Codec::NewlineType newline) 0246 { 0247 // all chars unencoded: 0248 qsizetype result = insize; 0249 // but maybe all of them are \n and we need to make them \r\n :-o 0250 if (newline == Codec::NewlineCRLF) { 0251 result += insize; 0252 } 0253 0254 // there might be an accu plus escape 0255 result += 2; 0256 0257 return result; 0258 } 0259 0260 Encoder *QuotedPrintableCodec::makeEncoder(Codec::NewlineType newline) const 0261 { 0262 return new QuotedPrintableEncoder(newline); 0263 } 0264 0265 Decoder *QuotedPrintableCodec::makeDecoder(Codec::NewlineType newline) const 0266 { 0267 return new QuotedPrintableDecoder(newline); 0268 } 0269 0270 qsizetype QuotedPrintableCodec::maxDecodedSizeFor(qsizetype insize, Codec::NewlineType newline) const 0271 { 0272 return QuotedPrintableDecoder_maxDecodedSizeFor(insize, newline); 0273 } 0274 0275 Encoder *Rfc2047QEncodingCodec::makeEncoder(Codec::NewlineType newline) const 0276 { 0277 return new Rfc2047QEncodingEncoder(newline); 0278 } 0279 0280 Decoder *Rfc2047QEncodingCodec::makeDecoder(Codec::NewlineType newline) const 0281 { 0282 return new QuotedPrintableDecoder(newline, true); 0283 } 0284 0285 qsizetype Rfc2047QEncodingCodec::maxDecodedSizeFor(qsizetype insize, Codec::NewlineType newline) const 0286 { 0287 return QuotedPrintableDecoder_maxDecodedSizeFor(insize, newline); 0288 } 0289 0290 Encoder *Rfc2231EncodingCodec::makeEncoder(Codec::NewlineType newline) const 0291 { 0292 return new Rfc2047QEncodingEncoder(newline, '%'); 0293 } 0294 0295 Decoder *Rfc2231EncodingCodec::makeDecoder(Codec::NewlineType newline) const 0296 { 0297 return new QuotedPrintableDecoder(newline, true, '%'); 0298 } 0299 0300 qsizetype Rfc2231EncodingCodec::maxDecodedSizeFor(qsizetype insize, Codec::NewlineType newline) const 0301 { 0302 return QuotedPrintableDecoder_maxDecodedSizeFor(insize, newline); 0303 } 0304 0305 /********************************************************/ 0306 /********************************************************/ 0307 /********************************************************/ 0308 0309 bool QuotedPrintableDecoder::decode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend) 0310 { 0311 if (d->newline == Codec::NewlineCRLF) { 0312 qWarning() << "CRLF output for decoders isn't yet supported!"; 0313 } 0314 0315 while (scursor != send && dcursor != dend) { 0316 if (mFlushing) { 0317 // we have to flush chars in the aftermath of a decoding 0318 // error. The way to request a flush is to 0319 // - store the offending character in mBadChar and 0320 // - set mFlushing to true. 0321 // The supported cases are (H: hexchar, X: bad char): 0322 // =X, =HX, CR 0323 // mBadChar is only written out if it is not by itself illegal in 0324 // quoted-printable (e.g. CTLs, 8Bits). 0325 // A fast way to suppress mBadChar output is to set it to NUL. 0326 if (mInsideHexChar) { 0327 // output '=' 0328 *dcursor++ = mEscapeChar; 0329 mInsideHexChar = false; 0330 } else if (mHaveAccu) { 0331 // output the high nibble of the accumulator: 0332 *dcursor++ = mLastChar; 0333 mHaveAccu = false; 0334 mAccu = 0; 0335 } else { 0336 // output mBadChar 0337 assert(mAccu == 0); 0338 if (mBadChar) { 0339 if (mBadChar == '=') { 0340 mInsideHexChar = true; 0341 } else { 0342 *dcursor++ = mBadChar; 0343 } 0344 mBadChar = 0; 0345 } 0346 mFlushing = false; 0347 } 0348 continue; 0349 } 0350 assert(mBadChar == 0); 0351 0352 uchar ch = *scursor++; 0353 0354 if (mExpectLF && ch != '\n') { 0355 // qWarning() << "QuotedPrintableDecoder:" 0356 // "illegally formed soft linebreak or lonely CR!"; 0357 mInsideHexChar = false; 0358 mExpectLF = false; 0359 if (mAccu != 0) { 0360 return false; 0361 } 0362 } 0363 0364 if (mInsideHexChar) { 0365 uchar value = 255; 0366 // next char(s) represent nibble instead of itself: 0367 if (ch <= '9') { 0368 if (ch >= '0') { 0369 value = ch - '0'; 0370 } else { 0371 switch (ch) { 0372 case '\r': 0373 mExpectLF = true; 0374 break; 0375 case '\n': 0376 // soft line break, but only if mAccu is NUL. 0377 if (!mHaveAccu) { 0378 mExpectLF = false; 0379 mInsideHexChar = false; 0380 break; 0381 } 0382 // else fall through 0383 default: 0384 // qWarning() << "QuotedPrintableDecoder:" 0385 // "illegally formed hex char! Outputting verbatim."; 0386 mBadChar = ch; 0387 mFlushing = true; 0388 } 0389 continue; 0390 } 0391 } else { // ch > '9' 0392 if (ch <= 'F') { 0393 if (ch >= 'A') { 0394 value = 10 + ch - 'A'; 0395 } else { // [:-@] 0396 mBadChar = ch; 0397 mFlushing = true; 0398 continue; 0399 } 0400 } else { // ch > 'F' 0401 if (ch <= 'f' && ch >= 'a') { 0402 value = 10 + ch - 'a'; 0403 } else { 0404 mBadChar = ch; 0405 mFlushing = true; 0406 continue; 0407 } 0408 } 0409 } 0410 0411 assert(value < 16); 0412 assert(mBadChar == 0); 0413 assert(!mExpectLF); 0414 0415 if (mHaveAccu) { 0416 *dcursor++ = char(mAccu | value); 0417 mAccu = 0; 0418 mHaveAccu = false; 0419 mInsideHexChar = false; 0420 } else { 0421 mHaveAccu = true; 0422 mAccu = value << 4; 0423 mLastChar = ch; 0424 } 0425 } else { // not mInsideHexChar 0426 if ((ch <= '~' && ch >= ' ') || ch == '\t') { 0427 if (ch == mEscapeChar) { 0428 mInsideHexChar = true; 0429 } else if (mQEncoding && ch == '_') { 0430 *dcursor++ = char(0x20); 0431 } else { 0432 *dcursor++ = char(ch); 0433 } 0434 } else if (ch == '\n') { 0435 *dcursor++ = '\n'; 0436 mExpectLF = false; 0437 } else if (ch == '\r') { 0438 mExpectLF = true; 0439 } else { 0440 // qWarning() << "QuotedPrintableDecoder:" << ch << 0441 // "illegal character in input stream!"; 0442 *dcursor++ = char(ch); 0443 } 0444 } 0445 } 0446 0447 return scursor == send; 0448 } 0449 0450 bool QuotedPrintableDecoder::finish(char *&dcursor, const char *const dend) 0451 { 0452 while ((mInsideHexChar || mHaveAccu || mFlushing) && dcursor != dend) { 0453 // we have to flush chars 0454 if (mInsideHexChar) { 0455 // output '=' 0456 *dcursor++ = mEscapeChar; 0457 mInsideHexChar = false; 0458 } else if (mHaveAccu) { 0459 // output the high nibble of the accumulator: 0460 *dcursor++ = mLastChar; 0461 mHaveAccu = false; 0462 mAccu = 0; 0463 } else { 0464 // output mBadChar 0465 assert(mAccu == 0); 0466 if (mBadChar) { 0467 *dcursor++ = mBadChar; 0468 mBadChar = 0; 0469 } 0470 mFlushing = false; 0471 } 0472 } 0473 0474 // return false if we are not finished yet; note that mInsideHexChar is always false 0475 return !(mHaveAccu || mFlushing); 0476 } 0477 0478 bool QuotedPrintableEncoder::fillInputBuffer(const char *&scursor, const char *const send) 0479 { 0480 // Don't read more if there's still a tail of a line in the buffer: 0481 if (mSawLineEnd) { 0482 return true; 0483 } 0484 0485 // Read until the buffer is full or we have found CRLF or LF (which 0486 // don't end up in the input buffer): 0487 for (; (mInputBufferWriteCursor + 1) % 16 != mInputBufferReadCursor && scursor != send; mInputBufferWriteCursor++) { 0488 char ch = *scursor++; 0489 if (ch == '\r') { 0490 mSawCR = true; 0491 } else if (ch == '\n') { 0492 // remove the CR from the input buffer (if any) and return that 0493 // we found a line ending: 0494 if (mSawCR) { 0495 mSawCR = false; 0496 assert(mInputBufferWriteCursor != mInputBufferReadCursor); 0497 mInputBufferWriteCursor--; 0498 } 0499 mSawLineEnd = true; 0500 return true; // saw CRLF or LF 0501 } else { 0502 mSawCR = false; 0503 } 0504 mInputBuffer[mInputBufferWriteCursor] = ch; 0505 } 0506 mSawLineEnd = false; 0507 return false; // didn't see a line ending... 0508 } 0509 0510 bool QuotedPrintableEncoder::processNextChar() 0511 { 0512 // If we process a buffer which doesn't end in a line break, we 0513 // can't process all of it, since the next chars that will be read 0514 // could be a line break. So we empty the buffer only until a fixed 0515 // number of chars is left (except when mFinishing, which means that 0516 // the data doesn't end in newline): 0517 const int minBufferFillWithoutLineEnd = 4; 0518 0519 assert(d->outputBufferCursor == 0); 0520 0521 int bufferFill = int(mInputBufferWriteCursor) - int(mInputBufferReadCursor); 0522 if (bufferFill < 0) { 0523 bufferFill += 16; 0524 } 0525 0526 assert(bufferFill >= 0 && bufferFill <= 15); 0527 0528 if (!mFinishing // 0529 && !mSawLineEnd // 0530 && bufferFill < minBufferFillWithoutLineEnd) { 0531 return false; 0532 } 0533 0534 // buffer is empty, return false: 0535 if (mInputBufferReadCursor == mInputBufferWriteCursor) { 0536 return false; 0537 } 0538 0539 // Real processing goes here: 0540 mAccu = mInputBuffer[mInputBufferReadCursor++]; 0541 if (needsEncoding(mAccu)) { // always needs encoding or 0542 mAccuNeedsEncoding = Definitely; 0543 } else if ((mSawLineEnd || mFinishing) // needs encoding at end of line 0544 && bufferFill == 1 // or end of buffer 0545 && needsEncodingAtEOL(mAccu)) { 0546 mAccuNeedsEncoding = Definitely; 0547 } else if (needsEncodingAtBOL(mAccu)) { 0548 mAccuNeedsEncoding = AtBOL; 0549 } else { 0550 // never needs encoding 0551 mAccuNeedsEncoding = Never; 0552 } 0553 0554 return true; 0555 } 0556 0557 // Outputs processed (verbatim or hex-encoded) chars and inserts soft 0558 // line breaks as necessary. Depends on processNextChar's directions 0559 // on whether to encode the current char, and whether 0560 // the current char is the last one in it's input line: 0561 void QuotedPrintableEncoder::createOutputBuffer(char *&dcursor, const char *const dend) 0562 { 0563 const int maxLineLength = 76; // rfc 2045 0564 0565 assert(d->outputBufferCursor == 0); 0566 0567 /* clang-format off */ 0568 bool lastOneOnThisLine = mSawLineEnd 0569 && mInputBufferReadCursor == mInputBufferWriteCursor; 0570 /* clang-format on */ 0571 0572 int neededSpace = 1; 0573 if (mAccuNeedsEncoding == Definitely) { 0574 neededSpace = 3; 0575 } 0576 0577 // reserve space for the soft hyphen (=) 0578 if (!lastOneOnThisLine) { 0579 neededSpace++; 0580 } 0581 0582 if (mCurrentLineLength > maxLineLength - neededSpace) { 0583 // current line too short, insert soft line break: 0584 write('=', dcursor, dend); 0585 writeCRLF(dcursor, dend); 0586 mCurrentLineLength = 0; 0587 } 0588 0589 if (Never == mAccuNeedsEncoding // 0590 || (AtBOL == mAccuNeedsEncoding && mCurrentLineLength != 0)) { 0591 write(mAccu, dcursor, dend); 0592 mCurrentLineLength++; 0593 } else { 0594 write('=', dcursor, dend); 0595 write(binToHex(highNibble(mAccu)), dcursor, dend); 0596 write(binToHex(lowNibble(mAccu)), dcursor, dend); 0597 mCurrentLineLength += 3; 0598 } 0599 } 0600 0601 bool QuotedPrintableEncoder::encode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend) 0602 { 0603 // support probing by the caller: 0604 if (mFinishing) { 0605 return true; 0606 } 0607 0608 while (scursor != send && dcursor != dend) { 0609 if (d->outputBufferCursor && !flushOutputBuffer(dcursor, dend)) { 0610 return scursor == send; 0611 } 0612 0613 assert(d->outputBufferCursor == 0); 0614 0615 // fill input buffer until eol has been reached or until the 0616 // buffer is full, whatever comes first: 0617 fillInputBuffer(scursor, send); 0618 0619 if (processNextChar()) { 0620 // there was one... 0621 createOutputBuffer(dcursor, dend); 0622 } else if (mSawLineEnd && mInputBufferWriteCursor == mInputBufferReadCursor) { 0623 // load a hard line break into output buffer: 0624 writeCRLF(dcursor, dend); 0625 // signal fillInputBuffer() we are ready for the next line: 0626 mSawLineEnd = false; 0627 mCurrentLineLength = 0; 0628 } else { 0629 // we are supposedly finished with this input block: 0630 break; 0631 } 0632 } 0633 0634 // make sure we write as much as possible and don't stop _writing_ 0635 // just because we have no more _input_: 0636 if (d->outputBufferCursor) { 0637 flushOutputBuffer(dcursor, dend); 0638 } 0639 0640 return scursor == send; 0641 0642 } // encode 0643 0644 bool QuotedPrintableEncoder::finish(char *&dcursor, const char *const dend) 0645 { 0646 mFinishing = true; 0647 0648 if (mFinished) { 0649 return flushOutputBuffer(dcursor, dend); 0650 } 0651 0652 while (dcursor != dend) { 0653 if (d->outputBufferCursor && !flushOutputBuffer(dcursor, dend)) { 0654 return false; 0655 } 0656 0657 assert(d->outputBufferCursor == 0); 0658 0659 if (processNextChar()) { 0660 // there was one... 0661 createOutputBuffer(dcursor, dend); 0662 } else if (mSawLineEnd && mInputBufferWriteCursor == mInputBufferReadCursor) { 0663 // load a hard line break into output buffer: 0664 writeCRLF(dcursor, dend); 0665 mSawLineEnd = false; 0666 mCurrentLineLength = 0; 0667 } else { 0668 mFinished = true; 0669 return flushOutputBuffer(dcursor, dend); 0670 } 0671 } 0672 0673 return mFinished && !d->outputBufferCursor; 0674 0675 } // finish 0676 0677 bool Rfc2047QEncodingEncoder::encode(const char *&scursor, const char *const send, char *&dcursor, const char *const dend) 0678 { 0679 if (mInsideFinishing) { 0680 return true; 0681 } 0682 0683 while (scursor != send && dcursor != dend) { 0684 uchar value = 0; 0685 switch (mStepNo) { 0686 case 0: 0687 // read the next char and decide if and how do encode: 0688 mAccu = *scursor++; 0689 if (!needsEncoding(mAccu)) { 0690 *dcursor++ = char(mAccu); 0691 } else if (mEscapeChar == '=' && mAccu == 0x20) { 0692 // shortcut encoding for 0x20 (latin-1/us-ascii SPACE) 0693 // (not for rfc2231 encoding) 0694 *dcursor++ = '_'; 0695 } else { 0696 // needs =XY encoding - write escape char: 0697 *dcursor++ = mEscapeChar; 0698 mStepNo = 1; 0699 } 0700 continue; 0701 case 1: 0702 // extract hi-nibble: 0703 value = highNibble(mAccu); 0704 mStepNo = 2; 0705 break; 0706 case 2: 0707 // extract lo-nibble: 0708 value = lowNibble(mAccu); 0709 mStepNo = 0; 0710 break; 0711 default: 0712 assert(0); 0713 } 0714 0715 // and write: 0716 *dcursor++ = binToHex(value); 0717 } 0718 0719 return scursor == send; 0720 } // encode 0721 0722 bool Rfc2047QEncodingEncoder::finish(char *&dcursor, const char *const dend) 0723 { 0724 mInsideFinishing = true; 0725 0726 // write the last bits of mAccu, if any: 0727 while (mStepNo != 0 && dcursor != dend) { 0728 uchar value = 0; 0729 switch (mStepNo) { 0730 case 1: 0731 // extract hi-nibble: 0732 value = highNibble(mAccu); 0733 mStepNo = 2; 0734 break; 0735 case 2: 0736 // extract lo-nibble: 0737 value = lowNibble(mAccu); 0738 mStepNo = 0; 0739 break; 0740 default: 0741 assert(0); 0742 } 0743 0744 // and write: 0745 *dcursor++ = binToHex(value); 0746 } 0747 0748 return mStepNo == 0; 0749 } 0750 0751 } // namespace KCodecs