1 | /* -*- c++ -*- |
2 | kmime_codec_uuencode.cpp |
3 | |
4 | KMime, the KDE Internet mail/usenet news message library. |
5 | Copyright (c) 2002 Marc Mutz <mutz@kde.org> |
6 | |
7 | This library is free software; you can redistribute it and/or |
8 | modify it under the terms of the GNU Library General Public |
9 | License as published by the Free Software Foundation; either |
10 | version 2 of the License, or (at your option) any later version. |
11 | |
12 | This library is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | Library General Public License for more details. |
16 | |
17 | You should have received a copy of the GNU Library General Public License |
18 | along with this library; see the file COPYING.LIB. If not, write to |
19 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
20 | Boston, MA 02110-1301, USA. |
21 | */ |
22 | /** |
23 | @file |
24 | This file is part of the API for handling @ref MIME data and |
25 | defines a @ref uuencode @ref Codec class. |
26 | |
27 | @brief |
28 | Defines the UUCodec class. |
29 | |
30 | @authors Marc Mutz \<mutz@kde.org\> |
31 | */ |
32 | |
33 | #include "kmime_codec_uuencode.h" |
34 | |
35 | #include <kdebug.h> |
36 | |
37 | #include <cassert> |
38 | |
39 | using namespace KMime; |
40 | |
41 | namespace KMime { |
42 | |
43 | class UUDecoder : public Decoder |
44 | { |
45 | uint mStepNo; |
46 | uchar mAnnouncedOctetCount; // (on current line) |
47 | uchar mCurrentOctetCount; // (on current line) |
48 | uchar mOutbits; |
49 | bool mLastWasCRLF : 1; |
50 | bool mSawBegin : 1; // whether we already saw ^begin... |
51 | uint mIntoBeginLine : 3; // count #chars we compared against "begin" 0..5 |
52 | bool mSawEnd : 1; // whether we already saw ^end... |
53 | uint mIntoEndLine : 2; // count #chars we compared against "end" 0..3 |
54 | |
55 | void searchForBegin( const char* &scursor, const char * const send ); |
56 | |
57 | protected: |
58 | friend class UUCodec; |
59 | UUDecoder( bool withCRLF=false ) |
60 | : Decoder( withCRLF ), mStepNo( 0 ), |
61 | mAnnouncedOctetCount( 0 ), mCurrentOctetCount( 0 ), |
62 | mOutbits( 0 ), mLastWasCRLF( true ), |
63 | mSawBegin( false ), mIntoBeginLine( 0 ), |
64 | mSawEnd( false ), mIntoEndLine( 0 ) {} |
65 | |
66 | public: |
67 | virtual ~UUDecoder() {} |
68 | |
69 | bool decode( const char* &scursor, const char * const send, |
70 | char* &dcursor, const char * const dend ); |
71 | // ### really needs no finishing??? |
72 | bool finish( char* &dcursor, const char * const dend ) |
73 | { Q_UNUSED( dcursor ); Q_UNUSED( dend ); return true; } |
74 | }; |
75 | |
76 | Encoder * UUCodec::makeEncoder( bool ) const |
77 | { |
78 | return 0; // encoding not supported |
79 | } |
80 | |
81 | Decoder * UUCodec::makeDecoder( bool withCRLF ) const |
82 | { |
83 | return new UUDecoder( withCRLF ); |
84 | } |
85 | |
86 | /********************************************************/ |
87 | /********************************************************/ |
88 | /********************************************************/ |
89 | |
90 | void UUDecoder::searchForBegin( const char* &scursor, const char * const send ) |
91 | { |
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 | } |
106 | } else { // mIntoBeginLine == beginLength |
107 | // found '\n': begin line complete |
108 | mLastWasCRLF = true; |
109 | mIntoBeginLine = 0; |
110 | return; |
111 | } |
112 | } else if ( mSawBegin ) { |
113 | // OK, skip stuff until the next \n |
114 | } else { |
115 | kWarning() << "UUDecoder: garbage before \"begin\", resetting parser" ; |
116 | mIntoBeginLine = 0; |
117 | } |
118 | } |
119 | |
120 | } |
121 | |
122 | // uuencoding just shifts all 6-bit octets by 32 (SP/' '), except NUL, |
123 | // which gets mapped to 0x60 |
124 | static inline uchar uuDecode( uchar c ) |
125 | { |
126 | return ( c - ' ' ) // undo shift and |
127 | & 0x3F; // map 0x40 (0x60-' ') to 0... |
128 | } |
129 | |
130 | bool UUDecoder::decode( const char* &scursor, const char * const send, |
131 | char* &dcursor, const char * const dend ) |
132 | { |
133 | // First, check whether we still need to find the "begin" line: |
134 | if ( !mSawBegin || mIntoBeginLine != 0 ) { |
135 | searchForBegin( scursor, send ); |
136 | } else if ( mSawEnd ) { |
137 | // or if we are past the end line: |
138 | scursor = send; // do nothing anymore... |
139 | return true; |
140 | } |
141 | |
142 | while ( dcursor != dend && scursor != send ) { |
143 | uchar ch = *scursor++; |
144 | uchar value; |
145 | |
146 | // Check whether we need to look for the "end" line: |
147 | if ( mIntoEndLine > 0 ) { |
148 | static const char end[] = "end" ; |
149 | static const uint endLength = 3; |
150 | |
151 | if ( ch == end[mIntoEndLine] ) { |
152 | ++mIntoEndLine; |
153 | if ( mIntoEndLine == endLength ) { |
154 | mSawEnd = true; |
155 | scursor = send; // shortcut to the end |
156 | return true; |
157 | } |
158 | continue; |
159 | } else { |
160 | kWarning() << "UUDecoder: invalid line octet count looks like \"end\" (mIntoEndLine =" |
161 | << mIntoEndLine << ")!" ; |
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 | |
187 | continue; |
188 | } |
189 | |
190 | // try converting ch to a 6-bit value: |
191 | if ( ch > 0x60 ) { |
192 | continue; // invalid char |
193 | } else if ( ch > ' ' ) { |
194 | value = uuDecode( ch ); |
195 | } else if ( ch == '\n' ) { // line end |
196 | mLastWasCRLF = true; |
197 | continue; |
198 | } else { |
199 | continue; |
200 | } |
201 | |
202 | // add the new bits to the output stream and flush full octets: |
203 | switch ( mStepNo ) { |
204 | case 0: |
205 | mOutbits = value << 2; |
206 | break; |
207 | case 1: |
208 | if ( mCurrentOctetCount < mAnnouncedOctetCount ) { |
209 | *dcursor++ = (char)( mOutbits | value >> 4 ); |
210 | } |
211 | ++mCurrentOctetCount; |
212 | mOutbits = value << 4; |
213 | break; |
214 | case 2: |
215 | if ( mCurrentOctetCount < mAnnouncedOctetCount ) { |
216 | *dcursor++ = (char)( mOutbits | value >> 2 ); |
217 | } |
218 | ++mCurrentOctetCount; |
219 | mOutbits = value << 6; |
220 | break; |
221 | case 3: |
222 | if ( mCurrentOctetCount < mAnnouncedOctetCount ) { |
223 | *dcursor++ = (char)( mOutbits | value ); |
224 | } |
225 | ++mCurrentOctetCount; |
226 | mOutbits = 0; |
227 | break; |
228 | default: |
229 | assert( 0 ); |
230 | } |
231 | mStepNo = ( mStepNo + 1 ) % 4; |
232 | |
233 | // check whether we ran over the announced octet count for this line: |
234 | kWarning( mCurrentOctetCount == mAnnouncedOctetCount + 1 ) |
235 | << "UUDecoder: mismatch between announced (" |
236 | << mAnnouncedOctetCount << ") and actual line octet count!" ; |
237 | |
238 | } |
239 | |
240 | // return false when caller should call us again: |
241 | return scursor == send; |
242 | } // UUDecoder::decode() |
243 | |
244 | } // namespace KMime |
245 | |