libkmime

kmime_codec_uuencode.cpp

00001 /*  -*- c++ -*-
00002     kmime_codec_uuencode.cpp
00003 
00004     This file is part of KMime, the KDE internet mail/usenet news message library.
00005     Copyright (c) 2002 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_uuencode.h"
00033 
00034 #include <kdebug.h>
00035 
00036 #include <cassert>
00037 
00038 using namespace KMime;
00039 
00040 namespace KMime {
00041 
00042 
00043 class UUDecoder : public Decoder {
00044   uint mStepNo;
00045   uchar mAnnouncedOctetCount; // (on current line)
00046   uchar mCurrentOctetCount; // (on current line)
00047   uchar mOutbits;
00048   bool mLastWasCRLF : 1;
00049   bool mSawBegin : 1;      // whether we already saw ^begin...
00050   uint mIntoBeginLine : 3; // count #chars we compared against "begin" 0..5
00051   bool mSawEnd : 1;        // whether we already saw ^end...
00052   uint mIntoEndLine : 2;   // count #chars we compared against "end" 0..3
00053 
00054   void searchForBegin( const char* & scursor, const char * const send );
00055 
00056 protected:
00057   friend class UUCodec;
00058   UUDecoder( bool withCRLF=false )
00059     : Decoder( withCRLF ), mStepNo(0),
00060       mAnnouncedOctetCount(0), mCurrentOctetCount(0),
00061       mOutbits(0), mLastWasCRLF(true),
00062       mSawBegin(false), mIntoBeginLine(0),
00063       mSawEnd(false), mIntoEndLine(0) {}
00064 
00065 public:
00066   virtual ~UUDecoder() {}
00067 
00068   bool decode( const char* & scursor, const char * const send,
00069            char* & dcursor, const char * const dend );
00070   // ### really needs no finishing???
00071   bool finish( char* & /*dcursor*/, const char * const /*dend*/ ) { return true; }
00072 };
00073 
00074 
00075 
00076 Encoder * UUCodec::makeEncoder( bool ) const {
00077   return 0; // encoding not supported
00078 }
00079 
00080 Decoder * UUCodec::makeDecoder( bool withCRLF ) const {
00081   return new UUDecoder( withCRLF );
00082 }
00083 
00084 
00085   /********************************************************/
00086   /********************************************************/
00087   /********************************************************/
00088 
00089 
00090 
00091 void UUDecoder::searchForBegin( const char* & scursor, const char * const send ) {
00092   static const char begin[] = "begin\n";
00093   static const uint beginLength = 5; // sic!
00094 
00095   assert( !mSawBegin || mIntoBeginLine > 0 );
00096 
00097   while ( scursor != send ) {
00098     uchar ch = *scursor++;
00099     if ( ch == begin[mIntoBeginLine] ) {
00100       if ( mIntoBeginLine < beginLength ) {
00101     // found another char
00102     ++mIntoBeginLine;
00103     if ( mIntoBeginLine == beginLength )
00104       mSawBegin = true; // "begin" complete, now search the next \n...
00105       } else /* mIntoBeginLine == beginLength */ {
00106     // found '\n': begin line complete
00107     mLastWasCRLF = true;
00108     mIntoBeginLine = 0;
00109     return;
00110       }
00111     } else if ( mSawBegin ) {
00112       // OK, skip stuff until the next \n
00113     } else {
00114       kdWarning() << "UUDecoder: garbage before \"begin\", resetting parser"
00115           << endl;
00116       mIntoBeginLine = 0;
00117     }
00118   }
00119 
00120 }
00121 
00122 
00123 // uuencoding just shifts all 6-bit octets by 32 (SP/' '), except NUL,
00124 // which gets mapped to 0x60
00125 static inline uchar uuDecode( uchar c ) {
00126   return ( c - ' ' ) // undo shift and
00127          & 0x3F;     // map 0x40 (0x60-' ') to 0...
00128 }
00129 
00130 
00131 bool UUDecoder::decode( const char* & scursor, const char * const send,
00132             char* & dcursor, const char * const dend )
00133 {
00134   // First, check whether we still need to find the "begin" line:
00135   if ( !mSawBegin || mIntoBeginLine != 0 )
00136     searchForBegin( scursor, send );
00137   // or if we are past the end line:
00138   else if ( mSawEnd ) {
00139     scursor = send; // do nothing anymore...
00140     return true;
00141   }
00142 
00143   while ( dcursor != dend && scursor != send ) {
00144     uchar ch = *scursor++;
00145     uchar value;
00146 
00147     // Check whether we need to look for the "end" line:
00148     if ( mIntoEndLine > 0 ) {
00149       static const char end[] = "end";
00150       static const uint endLength = 3;
00151 
00152       if ( ch == end[mIntoEndLine] ) {
00153     ++mIntoEndLine;
00154     if ( mIntoEndLine == endLength ) {
00155       mSawEnd = true;
00156       scursor = send; // shortcut to the end
00157       return true;
00158     }
00159     continue;
00160       } else {
00161     kdWarning() << "UUDecoder: invalid line octet count looks like \"end\" (mIntoEndLine = " << mIntoEndLine << " )!" << endl;
00162     mIntoEndLine = 0;
00163     // fall through...
00164       }
00165     }
00166 
00167     // Normal parsing:
00168 
00169     // The first char of a line is an encoding of the length of the
00170     // current line. We simply ignore it:
00171     if ( mLastWasCRLF ) {
00172       // reset char-per-line counter:
00173       mLastWasCRLF = false;
00174       mCurrentOctetCount = 0;
00175 
00176       // try to decode the chars-on-this-line announcement:
00177       if ( ch == 'e' ) // maybe the beginning of the "end"? ;-)
00178     mIntoEndLine = 1;
00179       else if ( ch > 0x60 )
00180     {} // ### invalid line length char: what shall we do??
00181       else if ( ch > ' ' )
00182     mAnnouncedOctetCount = uuDecode( ch );
00183       else if ( ch == '\n' )
00184     mLastWasCRLF = true; // oops, empty line
00185 
00186       continue;
00187     }
00188 
00189     // try converting ch to a 6-bit value:
00190     if ( ch > 0x60 )
00191       continue; // invalid char
00192     else if ( ch > ' ' )
00193       value = uuDecode( ch );
00194     else if ( ch == '\n' ) { // line end
00195       mLastWasCRLF = true;
00196       continue;
00197     } else
00198       continue;
00199 
00200     // add the new bits to the output stream and flush full octets:    
00201     switch ( mStepNo ) {
00202     case 0:
00203       mOutbits = value << 2;
00204       break;
00205     case 1:
00206       if ( mCurrentOctetCount < mAnnouncedOctetCount )
00207     *dcursor++ = (char)(mOutbits | value >> 4);
00208       ++mCurrentOctetCount;
00209       mOutbits = value << 4;
00210       break;
00211     case 2:
00212       if ( mCurrentOctetCount < mAnnouncedOctetCount )
00213     *dcursor++ = (char)(mOutbits | value >> 2);
00214       ++mCurrentOctetCount;
00215       mOutbits = value << 6;
00216       break;
00217     case 3:
00218       if ( mCurrentOctetCount < mAnnouncedOctetCount )
00219     *dcursor++ = (char)(mOutbits | value);
00220       ++mCurrentOctetCount;
00221       mOutbits = 0;
00222       break;
00223     default:
00224       assert( 0 );
00225     }
00226     mStepNo = (mStepNo + 1) % 4;
00227 
00228     // check whether we ran over the announced octet count for this line:
00229     kdWarning( mCurrentOctetCount == mAnnouncedOctetCount + 1 ) 
00230       << "UUDecoder: mismatch between announced ("
00231       << mAnnouncedOctetCount << ") and actual line octet count!" << endl;
00232 
00233   }
00234 
00235   // return false when caller should call us again:
00236   return (scursor == send);
00237 } // UUDecoder::decode()
00238 
00239 
00240 } // namespace KMime
KDE Home | KDE Accessibility Home | Description of Access Keys