• Skip to content
  • Skip to link menu
KDE 4.7 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • KDE Home
  • Contact Us
 

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

KMIME Library

Skip menu "KMIME Library"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal