32 #include "kmime_codec_qp.h"
34 #include "kmime_util.h"
40 using namespace KMime;
46 static inline char binToHex( uchar value ) {
48 return value +
'A' - 10;
53 static inline uchar highNibble( uchar ch ) {
57 static inline uchar lowNibble( uchar ch ) {
61 static inline bool keep( uchar ch ) {
63 return !( ((ch <
' ') && (ch !=
'\t')) || (ch ==
'?') );
70 class QuotedPrintableEncoder :
public Encoder {
71 char mInputBuffer[16];
72 uchar mCurrentLineLength;
74 uint mInputBufferReadCursor : 4;
75 uint mInputBufferWriteCursor : 4;
77 Never, AtBOL, Definitely
78 } mAccuNeedsEncoding : 2;
84 friend class QuotedPrintableCodec;
85 QuotedPrintableEncoder(
bool withCRLF=
false )
86 :
Encoder( withCRLF ), mCurrentLineLength(0), mAccu(0),
87 mInputBufferReadCursor(0), mInputBufferWriteCursor(0),
88 mAccuNeedsEncoding(Never),
89 mSawLineEnd(false), mSawCR(false), mFinishing(false),
92 bool needsEncoding( uchar ch ) {
93 return ( (ch >
'~') || ((ch <
' ') && (ch !=
'\t')) || (ch ==
'=') );
95 bool needsEncodingAtEOL( uchar ch ) {
96 return ( ch ==
' ' || ch ==
'\t' );
98 bool needsEncodingAtBOL( uchar ch ) {
99 return ( ch ==
'F' || ch ==
'.' || ch ==
'-' );
101 bool fillInputBuffer(
const char* & scursor,
const char *
const send );
102 bool processNextChar();
103 void createOutputBuffer(
char* & dcursor,
const char *
const dend );
105 virtual ~QuotedPrintableEncoder() {}
107 bool encode(
const char* & scursor,
const char *
const send,
108 char* & dcursor,
const char *
const dend );
110 bool finish(
char* & dcursor,
const char *
const dend );
114 class QuotedPrintableDecoder :
public Decoder {
115 const char mEscapeChar;
128 const bool mTQEncoding;
134 friend class QuotedPrintableCodec;
135 friend class Rfc2047TQEncodingCodec;
136 friend class Rfc2231EncodingCodec;
137 QuotedPrintableDecoder(
bool withCRLF=
false,
138 bool aTQEncoding=
false,
char aEscapeChar=
'=' )
140 mEscapeChar(aEscapeChar),
143 mTQEncoding(aTQEncoding),
144 mInsideHexChar(false),
149 virtual ~QuotedPrintableDecoder() {}
151 bool decode(
const char* & scursor,
const char *
const send,
152 char* & dcursor,
const char *
const dend );
154 bool finish(
char* &,
const char *
const ) {
return true; }
158 class Rfc2047TQEncodingEncoder :
public Encoder {
161 const char mEscapeChar;
162 bool mInsideFinishing : 1;
164 friend class Rfc2047TQEncodingCodec;
165 friend class Rfc2231EncodingCodec;
166 Rfc2047TQEncodingEncoder(
bool withCRLF=
false,
char aEscapeChar=
'=' )
168 mAccu(0), mStepNo(0), mEscapeChar( aEscapeChar ),
169 mInsideFinishing( false )
172 assert( aEscapeChar ==
'=' || aEscapeChar ==
'%' );
176 bool needsEncoding( uchar ch ) {
177 if ( ch >
'z' )
return true;
178 if ( !isEText( ch ) )
return true;
179 if ( mEscapeChar ==
'%' && ( ch ==
'*' || ch ==
'/' ) )
185 virtual ~Rfc2047TQEncodingEncoder() {}
187 bool encode(
const char* & scursor,
const char *
const send,
188 char* & dcursor,
const char *
const dend );
189 bool finish(
char* & dcursor,
const char *
const dend );
194 static int QuotedPrintableDecoder_maxDecodedSizeFor(
int insize,
bool withCRLF ) {
207 Encoder * QuotedPrintableCodec::makeEncoder(
bool withCRLF )
const {
208 return new QuotedPrintableEncoder( withCRLF );
211 Decoder * QuotedPrintableCodec::makeDecoder(
bool withCRLF )
const {
212 return new QuotedPrintableDecoder( withCRLF );
215 int QuotedPrintableCodec::maxDecodedSizeFor(
int insize,
bool withCRLF )
const {
216 return QuotedPrintableDecoder_maxDecodedSizeFor(insize, withCRLF);
219 Encoder * Rfc2047TQEncodingCodec::makeEncoder(
bool withCRLF )
const {
220 return new Rfc2047TQEncodingEncoder( withCRLF );
223 Decoder * Rfc2047TQEncodingCodec::makeDecoder(
bool withCRLF )
const {
224 return new QuotedPrintableDecoder( withCRLF,
true );
227 int Rfc2047TQEncodingCodec::maxDecodedSizeFor(
int insize,
bool withCRLF )
const {
228 return QuotedPrintableDecoder_maxDecodedSizeFor(insize, withCRLF);
231 Encoder * Rfc2231EncodingCodec::makeEncoder(
bool withCRLF )
const {
232 return new Rfc2047TQEncodingEncoder( withCRLF,
'%' );
235 Decoder * Rfc2231EncodingCodec::makeDecoder(
bool withCRLF )
const {
236 return new QuotedPrintableDecoder( withCRLF,
true,
'%' );
239 int Rfc2231EncodingCodec::maxDecodedSizeFor(
int insize,
bool withCRLF )
const {
240 return QuotedPrintableDecoder_maxDecodedSizeFor(insize, withCRLF);
247 bool QuotedPrintableDecoder::decode(
const char* & scursor,
const char *
const send,
248 char* & dcursor,
const char *
const dend ) {
250 kdWarning() <<
"CRLF output for decoders isn't yet supported!" << endl;
252 while ( scursor != send && dcursor != dend ) {
263 if ( mInsideHexChar ) {
265 *dcursor++ = mEscapeChar;
266 mInsideHexChar =
false;
267 }
else if ( mHaveAccu ) {
269 *dcursor++ = binToHex( highNibble( mAccu ) );
274 assert( mAccu == 0 );
276 if ( ((mBadChar >=
'>') && (mBadChar <=
'~')) ||
277 ((mBadChar >=
'!') && (mBadChar <=
'<')) )
278 *dcursor++ = mBadChar;
285 assert( mBadChar == 0 );
287 uchar ch = *scursor++;
290 if ( mExpectLF && ch !=
'\n' ) {
291 kdWarning() <<
"QuotedPrintableDecoder: "
292 "illegally formed soft linebreak or lonely CR!" << endl;
293 mInsideHexChar =
false;
295 assert( mAccu == 0 );
298 if ( mInsideHexChar ) {
312 mInsideHexChar =
false;
317 kdWarning() <<
"QuotedPrintableDecoder: "
318 "illegally formed hex char! Outputting verbatim." << endl;
327 value = 10 + ch -
'A';
334 if ( ch <= 'f' && ch >=
'a' ) {
335 value = 10 + ch -
'a';
344 assert( value < 16 );
345 assert( mBadChar == 0 );
346 assert( !mExpectLF );
349 *dcursor++ = char( mAccu | value );
352 mInsideHexChar =
false;
358 if ( ((ch <=
'~') && (ch >=
' ')) || (ch ==
'\t') ) {
359 if ( ch == mEscapeChar ) {
360 mInsideHexChar =
true;
361 }
else if ( mTQEncoding && ch ==
'_' ) {
362 *dcursor++ = char(0x20);
364 *dcursor++ = char(ch);
366 }
else if ( ch ==
'\n' ) {
369 }
else if ( ch ==
'\r' ) {
372 kdWarning() <<
"QuotedPrintableDecoder: " << ch <<
373 " illegal character in input stream! Ignoring." << endl;
378 return (scursor == send);
381 bool QuotedPrintableEncoder::fillInputBuffer(
const char* & scursor,
382 const char *
const send ) {
389 for ( ; ( mInputBufferWriteCursor + 1 ) % 16 != mInputBufferReadCursor
390 && scursor != send ; mInputBufferWriteCursor++ ) {
391 char ch = *scursor++;
394 }
else if ( ch ==
'\n' ) {
399 assert( mInputBufferWriteCursor != mInputBufferReadCursor );
400 mInputBufferWriteCursor--;
407 mInputBuffer[ mInputBufferWriteCursor ] = ch;
413 bool QuotedPrintableEncoder::processNextChar() {
420 const int minBufferFillWithoutLineEnd = 4;
422 assert( mOutputBufferCursor == 0 );
424 int bufferFill = int(mInputBufferWriteCursor) - int(mInputBufferReadCursor) ;
425 if ( bufferFill < 0 )
428 assert( bufferFill >=0 && bufferFill <= 15 );
430 if ( !mFinishing && !mSawLineEnd &&
431 bufferFill < minBufferFillWithoutLineEnd )
435 if ( mInputBufferReadCursor == mInputBufferWriteCursor )
439 mAccu = mInputBuffer[ mInputBufferReadCursor++ ];
440 if ( needsEncoding( mAccu ) )
441 mAccuNeedsEncoding = Definitely;
442 else if ( ( mSawLineEnd || mFinishing )
444 && needsEncodingAtEOL( mAccu ) )
445 mAccuNeedsEncoding = Definitely;
446 else if ( needsEncodingAtBOL( mAccu ) )
447 mAccuNeedsEncoding = AtBOL;
450 mAccuNeedsEncoding = Never;
459 void QuotedPrintableEncoder::createOutputBuffer(
char* & dcursor,
460 const char *
const dend )
462 const int maxLineLength = 76;
464 assert( mOutputBufferCursor == 0 );
466 bool lastOneOnThisLine = mSawLineEnd
467 && mInputBufferReadCursor == mInputBufferWriteCursor;
470 if ( mAccuNeedsEncoding == Definitely)
474 if ( !lastOneOnThisLine )
477 if ( mCurrentLineLength > maxLineLength - neededSpace ) {
479 write(
'=', dcursor, dend );
480 writeCRLF( dcursor, dend );
481 mCurrentLineLength = 0;
484 if ( (Never == mAccuNeedsEncoding) ||
485 ((AtBOL == mAccuNeedsEncoding) && (mCurrentLineLength != 0)) ) {
486 write( mAccu, dcursor, dend );
487 mCurrentLineLength++;
489 write(
'=', dcursor, dend );
490 write( binToHex( highNibble( mAccu ) ), dcursor, dend );
491 write( binToHex( lowNibble( mAccu ) ), dcursor, dend );
492 mCurrentLineLength += 3;
497 bool QuotedPrintableEncoder::encode(
const char* & scursor,
const char *
const send,
498 char* & dcursor,
const char *
const dend )
501 if ( mFinishing )
return true;
503 while ( scursor != send && dcursor != dend ) {
504 if ( mOutputBufferCursor && !flushOutputBuffer( dcursor, dend ) )
505 return (scursor == send);
507 assert( mOutputBufferCursor == 0 );
511 fillInputBuffer( scursor, send );
513 if ( processNextChar() )
515 createOutputBuffer( dcursor, dend );
516 else if ( mSawLineEnd &&
517 mInputBufferWriteCursor == mInputBufferReadCursor ) {
519 writeCRLF( dcursor, dend );
522 mCurrentLineLength = 0;
530 if ( mOutputBufferCursor ) flushOutputBuffer( dcursor, dend );
532 return (scursor == send);
536 bool QuotedPrintableEncoder::finish(
char* & dcursor,
537 const char *
const dend ) {
541 return flushOutputBuffer( dcursor, dend );
543 while ( dcursor != dend ) {
544 if ( mOutputBufferCursor && !flushOutputBuffer( dcursor, dend ) )
547 assert( mOutputBufferCursor == 0 );
549 if ( processNextChar() )
551 createOutputBuffer( dcursor, dend );
552 else if ( mSawLineEnd &&
553 mInputBufferWriteCursor == mInputBufferReadCursor ) {
555 writeCRLF( dcursor, dend );
557 mCurrentLineLength = 0;
560 return flushOutputBuffer( dcursor, dend );
564 return mFinished && !mOutputBufferCursor;
569 bool Rfc2047TQEncodingEncoder::encode(
const char* & scursor,
const char *
const send,
570 char* & dcursor,
const char *
const dend )
572 if ( mInsideFinishing )
return true;
574 while ( scursor != send && dcursor != dend ) {
580 if ( !needsEncoding( mAccu ) ) {
581 *dcursor++ = char(mAccu);
582 }
else if ( mEscapeChar ==
'=' && mAccu == 0x20 ) {
588 *dcursor++ = mEscapeChar;
594 value = highNibble(mAccu);
599 value = lowNibble(mAccu);
602 default: assert( 0 );
606 *dcursor++ = binToHex( value );
609 return (scursor == send);
612 #include <tqstring.h>
614 bool Rfc2047TQEncodingEncoder::finish(
char* & dcursor,
const char *
const dend ) {
615 mInsideFinishing =
true;
618 while ( mStepNo != 0 && dcursor != dend ) {
623 value = highNibble(mAccu);
628 value = lowNibble(mAccu);
631 default: assert( 0 );
635 *dcursor++ = binToHex( value );
Stateful decoder class, modelled after TQTextDecoder.
Stateful encoder class, modelled after TQTextEncoder.