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 TQt library by Trolltech AS, Norway (or with modified versions 00023 of TQt that use the same license as TQt), 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 TQt. 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