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
38using namespace KMime;
39
40namespace KMime {
41
42
43class 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
56protected:
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
65public:
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
76Encoder * UUCodec::makeEncoder( bool ) const {
77 return 0; // encoding not supported
78}
79
80Decoder * UUCodec::makeDecoder( bool withCRLF ) const {
81 return new UUDecoder( withCRLF );
82}
83
84
85 /********************************************************/
86 /********************************************************/
87 /********************************************************/
88
89
90
91void 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
125static inline uchar uuDecode( uchar c ) {
126 return ( c - ' ' ) // undo shift and
127 & 0x3F; // map 0x40 (0x60-' ') to 0...
128}
129
130
131bool 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