libkmime

kmime_codec_uuencode.cpp
1 /*
2  kmime_codec_uuencode.cpp
3 
4  This file is part of KMime, the KDE internet mail/usenet news message library.
5  Copyright (c) 2002 Marc Mutz <mutz@kde.org>
6 
7  KMime is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License, version 2, as
9  published by the Free Software Foundation.
10 
11  KMime is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this library; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 
20  In addition, as a special exception, the copyright holders give
21  permission to link the code of this library with any edition of
22  the TQt library by Trolltech AS, Norway (or with modified versions
23  of TQt that use the same license as TQt), and distribute linked
24  combinations including the two. You must obey the GNU General
25  Public License in all respects for all of the code used other than
26  TQt. If you modify this file, you may extend this exception to
27  your version of the file, but you are not obligated to do so. If
28  you do not wish to do so, delete this exception statement from
29  your version.
30 */
31 
32 #include "kmime_codec_uuencode.h"
33 
34 #include <kdebug.h>
35 
36 #include <cassert>
37 
38 using namespace KMime;
39 
40 namespace KMime {
41 
42 
43 class UUDecoder : public Decoder {
44  uint mStepNo;
45  uchar mAnnouncedOctetCount; // (on current line)
46  uchar mCurrentOctetCount; // (on current line)
47  uchar mOutbits;
48  bool mLastWasCRLF : 1;
49  bool mSawBegin : 1; // whether we already saw ^begin...
50  uint mIntoBeginLine : 3; // count #chars we compared against "begin" 0..5
51  bool mSawEnd : 1; // whether we already saw ^end...
52  uint mIntoEndLine : 2; // count #chars we compared against "end" 0..3
53 
54  void searchForBegin( const char* & scursor, const char * const send );
55 
56 protected:
57  friend class UUCodec;
58  UUDecoder( bool withCRLF=false )
59  : Decoder( withCRLF ), mStepNo(0),
60  mAnnouncedOctetCount(0), mCurrentOctetCount(0),
61  mOutbits(0), mLastWasCRLF(true),
62  mSawBegin(false), mIntoBeginLine(0),
63  mSawEnd(false), mIntoEndLine(0) {}
64 
65 public:
66  virtual ~UUDecoder() {}
67 
68  bool decode( const char* & scursor, const char * const send,
69  char* & dcursor, const char * const dend );
70  // ### really needs no finishing???
71  bool finish( char* & /*dcursor*/, const char * const /*dend*/ ) { return true; }
72 };
73 
74 
75 
76 Encoder * UUCodec::makeEncoder( bool ) const {
77  return 0; // encoding not supported
78 }
79 
80 Decoder * UUCodec::makeDecoder( bool withCRLF ) const {
81  return new UUDecoder( withCRLF );
82 }
83 
84 
85  /********************************************************/
86  /********************************************************/
87  /********************************************************/
88 
89 
90 
91 void UUDecoder::searchForBegin( const char* & scursor, const char * const send ) {
92  static const char begin[] = "begin\n";
93  static const uint beginLength = 5; // sic!
94 
95  assert( !mSawBegin || mIntoBeginLine > 0 );
96 
97  while ( scursor != send ) {
98  uchar ch = *scursor++;
99  if ( ch == begin[mIntoBeginLine] ) {
100  if ( mIntoBeginLine < beginLength ) {
101  // found another char
102  ++mIntoBeginLine;
103  if ( mIntoBeginLine == beginLength )
104  mSawBegin = true; // "begin" complete, now search the next \n...
105  } else /* mIntoBeginLine == beginLength */ {
106  // found '\n': begin line complete
107  mLastWasCRLF = true;
108  mIntoBeginLine = 0;
109  return;
110  }
111  } else if ( mSawBegin ) {
112  // OK, skip stuff until the next \n
113  } else {
114  kdWarning() << "UUDecoder: garbage before \"begin\", resetting parser"
115  << endl;
116  mIntoBeginLine = 0;
117  }
118  }
119 
120 }
121 
122 
123 // uuencoding just shifts all 6-bit octets by 32 (SP/' '), except NUL,
124 // which gets mapped to 0x60
125 static inline uchar uuDecode( uchar c ) {
126  return ( c - ' ' ) // undo shift and
127  & 0x3F; // map 0x40 (0x60-' ') to 0...
128 }
129 
130 
131 bool UUDecoder::decode( const char* & scursor, const char * const send,
132  char* & dcursor, const char * const dend )
133 {
134  // First, check whether we still need to find the "begin" line:
135  if ( !mSawBegin || mIntoBeginLine != 0 )
136  searchForBegin( scursor, send );
137  // or if we are past the end line:
138  else if ( mSawEnd ) {
139  scursor = send; // do nothing anymore...
140  return true;
141  }
142 
143  while ( dcursor != dend && scursor != send ) {
144  uchar ch = *scursor++;
145  uchar value;
146 
147  // Check whether we need to look for the "end" line:
148  if ( mIntoEndLine > 0 ) {
149  static const char end[] = "end";
150  static const uint endLength = 3;
151 
152  if ( ch == end[mIntoEndLine] ) {
153  ++mIntoEndLine;
154  if ( mIntoEndLine == endLength ) {
155  mSawEnd = true;
156  scursor = send; // shortcut to the end
157  return true;
158  }
159  continue;
160  } else {
161  kdWarning() << "UUDecoder: invalid line octet count looks like \"end\" (mIntoEndLine = " << mIntoEndLine << " )!" << endl;
162  mIntoEndLine = 0;
163  // fall through...
164  }
165  }
166 
167  // Normal parsing:
168 
169  // The first char of a line is an encoding of the length of the
170  // current line. We simply ignore it:
171  if ( mLastWasCRLF ) {
172  // reset char-per-line counter:
173  mLastWasCRLF = false;
174  mCurrentOctetCount = 0;
175 
176  // try to decode the chars-on-this-line announcement:
177  if ( ch == 'e' ) // maybe the beginning of the "end"? ;-)
178  mIntoEndLine = 1;
179  else if ( ch > 0x60 )
180  {} // ### invalid line length char: what shall we do??
181  else if ( ch > ' ' )
182  mAnnouncedOctetCount = uuDecode( ch );
183  else if ( ch == '\n' )
184  mLastWasCRLF = true; // oops, empty line
185 
186  continue;
187  }
188 
189  // try converting ch to a 6-bit value:
190  if ( ch > 0x60 )
191  continue; // invalid char
192  else if ( ch > ' ' )
193  value = uuDecode( ch );
194  else if ( ch == '\n' ) { // line end
195  mLastWasCRLF = true;
196  continue;
197  } else
198  continue;
199 
200  // add the new bits to the output stream and flush full octets:
201  switch ( mStepNo ) {
202  case 0:
203  mOutbits = value << 2;
204  break;
205  case 1:
206  if ( mCurrentOctetCount < mAnnouncedOctetCount )
207  *dcursor++ = (char)(mOutbits | value >> 4);
208  ++mCurrentOctetCount;
209  mOutbits = value << 4;
210  break;
211  case 2:
212  if ( mCurrentOctetCount < mAnnouncedOctetCount )
213  *dcursor++ = (char)(mOutbits | value >> 2);
214  ++mCurrentOctetCount;
215  mOutbits = value << 6;
216  break;
217  case 3:
218  if ( mCurrentOctetCount < mAnnouncedOctetCount )
219  *dcursor++ = (char)(mOutbits | value);
220  ++mCurrentOctetCount;
221  mOutbits = 0;
222  break;
223  default:
224  assert( 0 );
225  }
226  mStepNo = (mStepNo + 1) % 4;
227 
228  // check whether we ran over the announced octet count for this line:
229  kdWarning( mCurrentOctetCount == mAnnouncedOctetCount + 1 )
230  << "UUDecoder: mismatch between announced ("
231  << mAnnouncedOctetCount << ") and actual line octet count!" << endl;
232 
233  }
234 
235  // return false when caller should call us again:
236  return (scursor == send);
237 } // UUDecoder::decode()
238 
239 
240 } // namespace KMime
Stateful decoder class, modelled after TQTextDecoder.
Definition: kmime_codecs.h:268
Stateful encoder class, modelled after TQTextEncoder.
Definition: kmime_codecs.h:300