libkmime

kmime_codec_base64.cpp
1/*
2 kmime_codec_base64.cpp
3
4 This file is part of KMime, the KDE internet mail/usenet news message library.
5 Copyright (c) 2001 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_base64.h"
33
34#include <kdebug.h>
35
36#include <cassert>
37
38using namespace KMime;
39
40namespace KMime {
41
42// codec for base64 as specified in RFC 2045
43 //class Base64Codec;
44 //class Base64Decoder;
45 //class Base64Encoder;
46
47// codec for the B encoding as specified in RFC 2047
48 //class Rfc2047BEncodingCodec;
49 //class Rfc2047BEncodingEncoder;
50 //class Rfc2047BEncodingDecoder;
51
52
53static const uchar base64DecodeMap[128] = {
54 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
55 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
56
57 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
58 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
59
60 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
61 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
62
63 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
64 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64
65};
66
67static const char base64EncodeMap[64] = {
68 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
69 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
70 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
71 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
72 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
73 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
74 'w', 'x', 'y', 'z', '0', '1', '2', '3',
75 '4', '5', '6', '7', '8', '9', '+', '/'
76};
77
78
79class Base64Decoder : public Decoder {
80 uint mStepNo;
81 uchar mOutbits;
82 bool mSawPadding : 1;
83
84protected:
85 friend class Base64Codec;
86 Base64Decoder( bool withCRLF=false )
87 : Decoder( withCRLF ), mStepNo(0), mOutbits(0),
88 mSawPadding(false) {}
89
90public:
91 virtual ~Base64Decoder() {}
92
93 bool decode( const char* & scursor, const char * const send,
94 char* & dcursor, const char * const dend );
95 // ### really needs no finishing???
96 bool finish( char* & /*dcursor*/, const char * const /*dend*/ ) { return true; }
97};
98
99
100
101class Base64Encoder : public Encoder {
102 uint mStepNo;
104 uint mWrittenPacketsOnThisLine;
105 uchar mNextbits;
106 bool mInsideFinishing : 1;
107
108protected:
109 friend class Rfc2047BEncodingCodec;
110 friend class Rfc2047BEncodingEncoder;
111 friend class Base64Codec;
112 Base64Encoder( bool withCRLF=false )
113 : Encoder( withCRLF ), mStepNo(0), mWrittenPacketsOnThisLine(0),
114 mNextbits(0), mInsideFinishing(false) {}
115
116 bool generic_finish( char* & dcursor, const char * const dend,
117 bool withLFatEnd );
118
119public:
120 virtual ~Base64Encoder() {}
121
122 bool encode( const char* & scursor, const char * const send,
123 char* & dcursor, const char * const dend );
124
125 bool finish( char* & dcursor, const char * const dend );
126
127protected:
128 bool writeBase64( uchar ch, char* & dcursor, const char * const dend ) {
129 return write( base64EncodeMap[ ch ], dcursor, dend );
130 }
131};
132
133
134
135class Rfc2047BEncodingEncoder : public Base64Encoder {
136protected:
137 friend class Rfc2047BEncodingCodec;
138 Rfc2047BEncodingEncoder( bool withCRLF=false )
139 : Base64Encoder( withCRLF ) {};
140public:
141 bool encode( const char* & scursor, const char * const send,
142 char* & dcursor, const char * const dend );
143 bool finish( char* & dcursor, const char * const dend );
144};
145
146
147Encoder * Base64Codec::makeEncoder( bool withCRLF ) const {
148 return new Base64Encoder( withCRLF );
149}
150
151Decoder * Base64Codec::makeDecoder( bool withCRLF ) const {
152 return new Base64Decoder( withCRLF );
153}
154
155Encoder * Rfc2047BEncodingCodec::makeEncoder( bool withCRLF ) const {
156 return new Rfc2047BEncodingEncoder( withCRLF );
157}
158
159 /********************************************************/
160 /********************************************************/
161 /********************************************************/
162
163
164bool Base64Decoder::decode( const char* & scursor, const char * const send,
165 char* & dcursor, const char * const dend )
166{
167 while ( dcursor != dend && scursor != send ) {
168 uchar ch = *scursor++;
169 uchar value;
170
171 // try converting ch to a 6-bit value:
172 if ( ch < 128 )
173 value = base64DecodeMap[ ch ];
174 else
175 value = 64;
176
177 // ch isn't of the base64 alphabet, check for other significant chars:
178 if ( value >= 64 ) {
179 if ( ch == '=' ) {
180 // padding:
181 if ( mStepNo == 0 || mStepNo == 1) {
182 if (!mSawPadding) {
183 // malformed
184 kdWarning() << "Base64Decoder: unexpected padding "
185 "character in input stream" << endl;
186 }
187 mSawPadding = true;
188 break;
189 } else if ( mStepNo == 2 ) {
190 // ok, there should be another one
191 } else if ( mStepNo == 3 ) {
192 // ok, end of encoded stream
193 mSawPadding = true;
194 break;
195 }
196 mSawPadding = true;
197 mStepNo = (mStepNo + 1) % 4;
198 continue;
199 } else {
200 // non-base64 alphabet
201 continue;
202 }
203 }
204
205 if ( mSawPadding ) {
206 kdWarning() << "Base64Decoder: Embedded padding character "
207 "encountered!" << endl;
208 return true;
209 }
210
211 // add the new bits to the output stream and flush full octets:
212 switch ( mStepNo ) {
213 case 0:
214 mOutbits = value << 2;
215 break;
216 case 1:
217 *dcursor++ = (char)(mOutbits | value >> 4);
218 mOutbits = value << 4;
219 break;
220 case 2:
221 *dcursor++ = (char)(mOutbits | value >> 2);
222 mOutbits = value << 6;
223 break;
224 case 3:
225 *dcursor++ = (char)(mOutbits | value);
226 mOutbits = 0;
227 break;
228 default:
229 assert( 0 );
230 }
231 mStepNo = (mStepNo + 1) % 4;
232 }
233
234 // return false when caller should call us again:
235 return (scursor == send);
236} // Base64Decoder::decode()
237
238
239
240bool Base64Encoder::encode( const char* & scursor, const char * const send,
241 char* & dcursor, const char * const dend ) {
242 const uint maxPacketsPerLine = 76 / 4;
243
244 // detect when the caller doesn't adhere to our rules:
245 if ( mInsideFinishing ) return true;
246
247 while ( scursor != send && dcursor != dend ) {
248 // properly empty the output buffer before starting something new:
249 // ### fixme: we can optimize this away, since the buffer isn't
250 // written to anyway (most of the time)
251 if ( mOutputBufferCursor && !flushOutputBuffer( dcursor, dend ) )
252 return (scursor == send);
253
254 uchar ch = *scursor++;
255 // mNextbits // (part of) value of next sextet
256
257 // check for line length;
258 if ( mStepNo == 0 && mWrittenPacketsOnThisLine >= maxPacketsPerLine ) {
259 writeCRLF( dcursor, dend );
260 mWrittenPacketsOnThisLine = 0;
261 }
262
263 // depending on mStepNo, extract value and mNextbits from the
264 // octet stream:
265 switch ( mStepNo ) {
266 case 0:
267 assert( mNextbits == 0 );
268 writeBase64( ch >> 2, dcursor, dend ); // top-most 6 bits -> output
269 mNextbits = (ch & 0x3) << 4; // 0..1 bits -> 4..5 in mNextbits
270 break;
271 case 1:
272 assert( (mNextbits & ~0x30) == 0 );
273 writeBase64( mNextbits | ch >> 4, dcursor, dend ); // 4..7 bits -> 0..3 in value
274 mNextbits = (ch & 0xf) << 2; // 0..3 bits -> 2..5 in mNextbits
275 break;
276 case 2:
277 assert( (mNextbits & ~0x3C) == 0 );
278 writeBase64( mNextbits | ch >> 6, dcursor, dend ); // 6..7 bits -> 0..1 in value
279 writeBase64( ch & 0x3F, dcursor, dend ); // 0..5 bits -> output
280 mNextbits = 0;
281 mWrittenPacketsOnThisLine++;
282 break;
283 default:
284 assert( 0 );
285 }
286 mStepNo = ( mStepNo + 1 ) % 3;
287 }
288
289 if ( mOutputBufferCursor ) flushOutputBuffer( dcursor, dend );
290
291 return (scursor == send);
292}
293
294
295bool Rfc2047BEncodingEncoder::encode( const char* & scursor,
296 const char * const send,
297 char* & dcursor,
298 const char * const dend )
299{
300 // detect when the caller doesn't adhere to our rules:
301 if ( mInsideFinishing ) return true;
302
303 while ( scursor != send && dcursor != dend ) {
304 // properly empty the output buffer before starting something new:
305 // ### fixme: we can optimize this away, since the buffer isn't
306 // written to anyway (most of the time)
307 if ( mOutputBufferCursor && !flushOutputBuffer( dcursor, dend ) )
308 return (scursor == send);
309
310 uchar ch = *scursor++;
311 // mNextbits // (part of) value of next sextet
312
313 // depending on mStepNo, extract value and mNextbits from the
314 // octet stream:
315 switch ( mStepNo ) {
316 case 0:
317 assert( mNextbits == 0 );
318 writeBase64( ch >> 2, dcursor, dend ); // top-most 6 bits -> output
319 mNextbits = (ch & 0x3) << 4; // 0..1 bits -> 4..5 in mNextbits
320 break;
321 case 1:
322 assert( (mNextbits & ~0x30) == 0 );
323 writeBase64( mNextbits | ch >> 4, dcursor, dend ); // 4..7 bits -> 0..3 in value
324 mNextbits = (ch & 0xf) << 2; // 0..3 bits -> 2..5 in mNextbits
325 break;
326 case 2:
327 assert( (mNextbits & ~0x3C) == 0 );
328 writeBase64( mNextbits | ch >> 6, dcursor, dend ); // 6..7 bits -> 0..1 in value
329 writeBase64( ch & 0x3F, dcursor, dend ); // 0..5 bits -> output
330 mNextbits = 0;
331 break;
332 default:
333 assert( 0 );
334 }
335 mStepNo = ( mStepNo + 1 ) % 3;
336 }
337
338 if ( mOutputBufferCursor ) flushOutputBuffer( dcursor, dend );
339
340 return (scursor == send);
341}
342
343
344bool Base64Encoder::finish( char* & dcursor, const char * const dend ) {
345 return generic_finish( dcursor, dend, true );
346}
347
348bool Rfc2047BEncodingEncoder::finish( char* & dcursor,
349 const char * const dend ) {
350 return generic_finish( dcursor, dend, false );
351}
352
353bool Base64Encoder::generic_finish( char* & dcursor, const char * const dend,
354 bool withLFatEnd )
355{
356 if ( mInsideFinishing )
357 return flushOutputBuffer( dcursor, dend );
358
359 if ( mOutputBufferCursor && !flushOutputBuffer( dcursor, dend ) )
360 return false;
361
362 mInsideFinishing = true;
363
364 //
365 // writing out the last mNextbits...
366 //
367 switch ( mStepNo ) {
368 case 1: // 2 mNextbits waiting to be written. Needs two padding chars:
369 case 2: // 4 or 6 mNextbits waiting to be written. Completes a block
370 writeBase64( mNextbits, dcursor, dend );
371 mNextbits = 0;
372 break;
373 case 0: // no padding, nothing to be written, except possibly the CRLF
374 assert( mNextbits == 0 );
375 break;
376 default:
377 assert( 0 );
378 }
379
380 //
381 // adding padding...
382 //
383 switch( mStepNo ) {
384 case 1:
385 write( '=', dcursor, dend );
386 // fall through:
387 case 2:
388 write( '=', dcursor, dend );
389 // fall through:
390 case 0: // completed an quartet - add CRLF
391 if ( withLFatEnd )
392 writeCRLF( dcursor, dend );
393 return flushOutputBuffer( dcursor, dend );
394 default:
395 assert( 0 );
396 }
397return true; // asserts get compiled out
398}
399
400
401
402
403
404
405} // namespace KMime
Stateful decoder class, modelled after TQTextDecoder.
Definition: kmime_codecs.h:268
Stateful encoder class, modelled after TQTextEncoder.
Definition: kmime_codecs.h:300
bool write(char ch, char *&dcursor, const char *const dend)
Writes ch to the output stream or the output buffer, depending on whether or not the output stream ha...
Definition: kmime_codecs.h:328