KMIME Library
kmime_codec_base64.cpp
Go to the documentation of this file.
00001 /* -*- c++ -*- 00002 kmime_codec_base64.cpp 00003 00004 KMime, the KDE Internet mail/usenet news message library. 00005 Copyright (c) 2001 Marc Mutz <mutz@kde.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00020 Boston, MA 02110-1301, USA. 00021 */ 00033 #include "kmime_codec_base64.h" 00034 00035 #include <kdebug.h> 00036 00037 #include <cassert> 00038 00039 using namespace KMime; 00040 00041 namespace KMime { 00042 00043 // codec for base64 as specified in RFC 2045 00044 //class Base64Codec; 00045 //class Base64Decoder; 00046 //class Base64Encoder; 00047 00048 // codec for the B encoding as specified in RFC 2047 00049 //class Rfc2047BEncodingCodec; 00050 //class Rfc2047BEncodingEncoder; 00051 //class Rfc2047BEncodingDecoder; 00052 00053 //@cond PRIVATE 00054 static const uchar base64DecodeMap[128] = { 00055 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 00056 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 00057 00058 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 00059 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 00060 00061 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 00062 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 00063 00064 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 00065 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64 00066 }; 00067 00068 static const char base64EncodeMap[64] = { 00069 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 00070 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 00071 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 00072 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 00073 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 00074 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 00075 'w', 'x', 'y', 'z', '0', '1', '2', '3', 00076 '4', '5', '6', '7', '8', '9', '+', '/' 00077 }; 00078 //@endcond 00079 00080 class Base64Decoder : public Decoder 00081 { 00082 uint mStepNo; 00083 uchar mOutbits; 00084 bool mSawPadding : 1; 00085 00086 protected: 00087 friend class Base64Codec; 00088 Base64Decoder( bool withCRLF=false ) 00089 : Decoder( withCRLF ), mStepNo( 0 ), mOutbits( 0 ), 00090 mSawPadding( false ) {} 00091 00092 public: 00093 virtual ~Base64Decoder() {} 00094 00095 bool decode( const char* &scursor, const char * const send, 00096 char* &dcursor, const char * const dend ); 00097 // ### really needs no finishing??? 00098 bool finish( char* &dcursor, const char * const dend ) 00099 { 00100 Q_UNUSED( dcursor ); Q_UNUSED( dend ); 00101 return true; 00102 } 00103 }; 00104 00105 class Base64Encoder : public Encoder 00106 { 00107 uint mStepNo; 00109 uint mWrittenPacketsOnThisLine; 00110 uchar mNextbits; 00111 bool mInsideFinishing : 1; 00112 00113 protected: 00114 friend class Rfc2047BEncodingCodec; 00115 friend class Rfc2047BEncodingEncoder; 00116 friend class Base64Codec; 00117 Base64Encoder( bool withCRLF=false ) 00118 : Encoder( withCRLF ), mStepNo( 0 ), mWrittenPacketsOnThisLine( 0 ), 00119 mNextbits( 0 ), mInsideFinishing( false ) {} 00120 00121 bool generic_finish( char* &dcursor, const char * const dend, 00122 bool withLFatEnd ); 00123 00124 public: 00125 virtual ~Base64Encoder() {} 00126 00127 bool encode( const char* &scursor, const char * const send, 00128 char* &dcursor, const char * const dend ); 00129 00130 bool finish( char* &dcursor, const char * const dend ); 00131 00132 protected: 00133 bool writeBase64( uchar ch, char* &dcursor, const char * const dend ) 00134 { return write( base64EncodeMap[ ch ], dcursor, dend ); } 00135 }; 00136 00137 class Rfc2047BEncodingEncoder : public Base64Encoder 00138 { 00139 protected: 00140 friend class Rfc2047BEncodingCodec; 00141 Rfc2047BEncodingEncoder( bool withCRLF=false ) 00142 : Base64Encoder( withCRLF ) {} 00143 00144 public: 00145 bool encode( const char* &scursor, const char * const send, 00146 char* &dcursor, const char * const dend ); 00147 bool finish( char* &dcursor, const char * const dend ); 00148 }; 00149 00150 Encoder *Base64Codec::makeEncoder( bool withCRLF ) const 00151 { 00152 return new Base64Encoder( withCRLF ); 00153 } 00154 00155 Decoder *Base64Codec::makeDecoder( bool withCRLF ) const 00156 { 00157 return new Base64Decoder( withCRLF ); 00158 } 00159 00160 Encoder *Rfc2047BEncodingCodec::makeEncoder( bool withCRLF ) const 00161 { 00162 return new Rfc2047BEncodingEncoder( withCRLF ); 00163 } 00164 00165 /********************************************************/ 00166 /********************************************************/ 00167 /********************************************************/ 00168 00169 bool Base64Decoder::decode( const char* &scursor, const char * const send, 00170 char* &dcursor, const char * const dend ) 00171 { 00172 while ( dcursor != dend && scursor != send ) { 00173 uchar ch = *scursor++; 00174 uchar value; 00175 00176 // try converting ch to a 6-bit value: 00177 if ( ch < 128 ) { 00178 value = base64DecodeMap[ ch ]; 00179 } else { 00180 value = 64; 00181 } 00182 00183 // ch isn't of the base64 alphabet, check for other significant chars: 00184 if ( value >= 64 ) { 00185 if ( ch == '=' ) { 00186 // padding: 00187 if ( mStepNo == 0 || mStepNo == 1 ) { 00188 if ( !mSawPadding ) { 00189 // malformed 00190 kWarning() << "Base64Decoder: unexpected padding" 00191 "character in input stream"; 00192 } 00193 mSawPadding = true; 00194 break; 00195 } else if ( mStepNo == 2 ) { 00196 // ok, there should be another one 00197 } else if ( mStepNo == 3 ) { 00198 // ok, end of encoded stream 00199 mSawPadding = true; 00200 break; 00201 } 00202 mSawPadding = true; 00203 mStepNo = (mStepNo + 1) % 4; 00204 continue; 00205 } else { 00206 // non-base64 alphabet 00207 continue; 00208 } 00209 } 00210 00211 if ( mSawPadding ) { 00212 kWarning() << "Base64Decoder: Embedded padding character" 00213 "encountered!"; 00214 return true; 00215 } 00216 00217 // add the new bits to the output stream and flush full octets: 00218 switch ( mStepNo ) { 00219 case 0: 00220 mOutbits = value << 2; 00221 break; 00222 case 1: 00223 *dcursor++ = (char)(mOutbits | value >> 4); 00224 mOutbits = value << 4; 00225 break; 00226 case 2: 00227 *dcursor++ = (char)(mOutbits | value >> 2); 00228 mOutbits = value << 6; 00229 break; 00230 case 3: 00231 *dcursor++ = (char)(mOutbits | value); 00232 mOutbits = 0; 00233 break; 00234 default: 00235 assert( 0 ); 00236 } 00237 mStepNo = (mStepNo + 1) % 4; 00238 } 00239 00240 // return false when caller should call us again: 00241 return scursor == send; 00242 } // Base64Decoder::decode() 00243 00244 bool Base64Encoder::encode( const char* &scursor, const char * const send, 00245 char* &dcursor, const char * const dend ) 00246 { 00247 const uint maxPacketsPerLine = 76 / 4; 00248 00249 // detect when the caller doesn't adhere to our rules: 00250 if ( mInsideFinishing ) { 00251 return true; 00252 } 00253 00254 while ( scursor != send && dcursor != dend ) { 00255 // properly empty the output buffer before starting something new: 00256 // ### fixme: we can optimize this away, since the buffer isn't 00257 // written to anyway (most of the time) 00258 if ( mOutputBufferCursor && !flushOutputBuffer( dcursor, dend ) ) { 00259 return scursor == send; 00260 } 00261 00262 uchar ch = *scursor++; 00263 // mNextbits // (part of) value of next sextet 00264 00265 // check for line length; 00266 if ( mStepNo == 0 && mWrittenPacketsOnThisLine >= maxPacketsPerLine ) { 00267 writeCRLF( dcursor, dend ); 00268 mWrittenPacketsOnThisLine = 0; 00269 } 00270 00271 // depending on mStepNo, extract value and mNextbits from the 00272 // octet stream: 00273 switch ( mStepNo ) { 00274 case 0: 00275 assert( mNextbits == 0 ); 00276 writeBase64( ch >> 2, dcursor, dend ); // top-most 6 bits -> output 00277 mNextbits = (ch & 0x3) << 4; // 0..1 bits -> 4..5 in mNextbits 00278 break; 00279 case 1: 00280 assert( (mNextbits & ~0x30) == 0 ); 00281 writeBase64( mNextbits | ch >> 4, dcursor, dend ); // 4..7 bits -> 0..3 in value 00282 mNextbits = (ch & 0xf) << 2; // 0..3 bits -> 2..5 in mNextbits 00283 break; 00284 case 2: 00285 assert( (mNextbits & ~0x3C) == 0 ); 00286 writeBase64( mNextbits | ch >> 6, dcursor, dend ); // 6..7 bits -> 0..1 in value 00287 writeBase64( ch & 0x3F, dcursor, dend ); // 0..5 bits -> output 00288 mNextbits = 0; 00289 mWrittenPacketsOnThisLine++; 00290 break; 00291 default: 00292 assert( 0 ); 00293 } 00294 mStepNo = ( mStepNo + 1 ) % 3; 00295 } 00296 00297 if ( mOutputBufferCursor ) { 00298 flushOutputBuffer( dcursor, dend ); 00299 } 00300 00301 return scursor == send; 00302 } 00303 00304 bool Rfc2047BEncodingEncoder::encode( const char* &scursor, 00305 const char * const send, 00306 char* &dcursor, 00307 const char * const dend ) 00308 { 00309 // detect when the caller doesn't adhere to our rules: 00310 if ( mInsideFinishing ) { 00311 return true; 00312 } 00313 00314 while ( scursor != send && dcursor != dend ) { 00315 // properly empty the output buffer before starting something new: 00316 // ### fixme: we can optimize this away, since the buffer isn't 00317 // written to anyway (most of the time) 00318 if ( mOutputBufferCursor && !flushOutputBuffer( dcursor, dend ) ) { 00319 return scursor == send; 00320 } 00321 00322 uchar ch = *scursor++; 00323 // mNextbits // (part of) value of next sextet 00324 00325 // depending on mStepNo, extract value and mNextbits from the 00326 // octet stream: 00327 switch ( mStepNo ) { 00328 case 0: 00329 assert( mNextbits == 0 ); 00330 writeBase64( ch >> 2, dcursor, dend ); // top-most 6 bits -> output 00331 mNextbits = (ch & 0x3) << 4; // 0..1 bits -> 4..5 in mNextbits 00332 break; 00333 case 1: 00334 assert( (mNextbits & ~0x30) == 0 ); 00335 writeBase64( mNextbits | ch >> 4, dcursor, dend ); // 4..7 bits -> 0..3 in value 00336 mNextbits = (ch & 0xf) << 2; // 0..3 bits -> 2..5 in mNextbits 00337 break; 00338 case 2: 00339 assert( (mNextbits & ~0x3C) == 0 ); 00340 writeBase64( mNextbits | ch >> 6, dcursor, dend ); // 6..7 bits -> 0..1 in value 00341 writeBase64( ch & 0x3F, dcursor, dend ); // 0..5 bits -> output 00342 mNextbits = 0; 00343 break; 00344 default: 00345 assert( 0 ); 00346 } 00347 mStepNo = ( mStepNo + 1 ) % 3; 00348 } 00349 00350 if ( mOutputBufferCursor ) { 00351 flushOutputBuffer( dcursor, dend ); 00352 } 00353 00354 return scursor == send; 00355 } 00356 00357 bool Base64Encoder::finish( char* &dcursor, const char * const dend ) 00358 { 00359 return generic_finish( dcursor, dend, true ); 00360 } 00361 00362 bool Rfc2047BEncodingEncoder::finish( char* & dcursor, 00363 const char * const dend ) 00364 { 00365 return generic_finish( dcursor, dend, false ); 00366 } 00367 00368 bool Base64Encoder::generic_finish( char* &dcursor, const char * const dend, 00369 bool withLFatEnd ) 00370 { 00371 if ( mInsideFinishing ) { 00372 return flushOutputBuffer( dcursor, dend ); 00373 } 00374 00375 if ( mOutputBufferCursor && !flushOutputBuffer( dcursor, dend ) ) { 00376 return false; 00377 } 00378 00379 mInsideFinishing = true; 00380 00381 // 00382 // writing out the last mNextbits... 00383 // 00384 switch ( mStepNo ) { 00385 case 1: // 2 mNextbits waiting to be written. Needs two padding chars: 00386 case 2: // 4 or 6 mNextbits waiting to be written. Completes a block 00387 writeBase64( mNextbits, dcursor, dend ); 00388 mNextbits = 0; 00389 break; 00390 case 0: // no padding, nothing to be written, except possibly the CRLF 00391 assert( mNextbits == 0 ); 00392 break; 00393 default: 00394 assert( 0 ); 00395 } 00396 00397 // 00398 // adding padding... 00399 // 00400 switch( mStepNo ) { 00401 case 1: 00402 write( '=', dcursor, dend ); 00403 // fall through: 00404 case 2: 00405 write( '=', dcursor, dend ); 00406 // fall through: 00407 case 0: // completed an quartet - add CRLF 00408 if ( withLFatEnd ) { 00409 writeCRLF( dcursor, dend ); 00410 } 00411 return flushOutputBuffer( dcursor, dend ); 00412 default: 00413 assert( 0 ); 00414 } 00415 return true; // asserts get compiled out 00416 } 00417 00418 } // namespace KMime
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon May 14 2012 04:41:32 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Mon May 14 2012 04:41:32 by doxygen 1.7.5 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.