libkmime

kmime_codec_base64.cpp

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