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 
38 using namespace KMime;
39 
40 namespace 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 
53 static 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 
67 static 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 
79 class Base64Decoder : public Decoder {
80  uint mStepNo;
81  uchar mOutbits;
82  bool mSawPadding : 1;
83 
84 protected:
85  friend class Base64Codec;
86  Base64Decoder( bool withCRLF=false )
87  : Decoder( withCRLF ), mStepNo(0), mOutbits(0),
88  mSawPadding(false) {}
89 
90 public:
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 
101 class Base64Encoder : public Encoder {
102  uint mStepNo;
104  uint mWrittenPacketsOnThisLine;
105  uchar mNextbits;
106  bool mInsideFinishing : 1;
107 
108 protected:
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 
119 public:
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 
127 protected:
128  bool writeBase64( uchar ch, char* & dcursor, const char * const dend ) {
129  return write( base64EncodeMap[ ch ], dcursor, dend );
130  }
131 };
132 
133 
134 
135 class Rfc2047BEncodingEncoder : public Base64Encoder {
136 protected:
137  friend class Rfc2047BEncodingCodec;
138  Rfc2047BEncodingEncoder( bool withCRLF=false )
139  : Base64Encoder( withCRLF ) {};
140 public:
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 
147 Encoder * Base64Codec::makeEncoder( bool withCRLF ) const {
148  return new Base64Encoder( withCRLF );
149 }
150 
151 Decoder * Base64Codec::makeDecoder( bool withCRLF ) const {
152  return new Base64Decoder( withCRLF );
153 }
154 
155 Encoder * Rfc2047BEncodingCodec::makeEncoder( bool withCRLF ) const {
156  return new Rfc2047BEncodingEncoder( withCRLF );
157 }
158 
159  /********************************************************/
160  /********************************************************/
161  /********************************************************/
162 
163 
164 bool 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 
240 bool 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 
295 bool 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 
344 bool Base64Encoder::finish( char* & dcursor, const char * const dend ) {
345  return generic_finish( dcursor, dend, true );
346 }
347 
348 bool Rfc2047BEncodingEncoder::finish( char* & dcursor,
349  const char * const dend ) {
350  return generic_finish( dcursor, dend, false );
351 }
352 
353 bool 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  }
397 return 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