]> err.no Git - sope/blob - sope-core/NGExtensions/NGBase64Coding.m
Drop apache 1 build-dependency
[sope] / sope-core / NGExtensions / NGBase64Coding.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "NGBase64Coding.h"
23 #include "common.h"
24 #import <Foundation/NSString.h>
25 #import <Foundation/NSException.h>
26
27 static inline BOOL isbase64(char a) {
28   if (('A' <= a) && (a <= 'Z'))
29     return YES;
30   if (('a' <= a) && (a <= 'z'))
31     return YES;
32   if (('0' <= a) && (a <= '9'))
33     return YES;
34   if ((a == '+') || (a == '/'))
35     return YES;
36   return NO;
37 }
38
39 static inline int encode_base64(const char *_src, size_t _srcLen, char *_dest,
40                                 size_t _destSize, size_t *_destLen,
41                                 int _maxLineWidth);
42 static inline int decode_base64(const char *_src, size_t _srcLen, char *_dest,
43                                 size_t _destSize, size_t *_destLen);
44
45 @implementation NSString(Base64Coding)
46
47 static Class StringClass = Nil;
48 static int NSStringMaxLineWidth = 1024;
49   
50 - (NSString *)stringByEncodingBase64 {
51   unsigned len;
52   size_t destSize;
53   size_t destLength = -1;
54   char   *dest, *src;
55   
56   if ((len = [self cStringLength]) == 0)
57     return @"";
58   
59   destSize = (len + 2) / 3 * 4; // 3:4 conversion ratio
60   destSize += destSize / NSStringMaxLineWidth + 2; // space for '\n' and '\0'
61   destSize += 64;
62   dest = malloc(destSize + 4);
63   if (dest == NULL) return nil;
64
65   NSAssert(destSize > 0, @"invalid buffer size ..");
66   NSAssert(dest,         @"invalid buffer ..");
67
68   src = malloc(len + 4);
69   [self getCString:src];
70   src[len] = '\0';
71   
72   if (encode_base64(src, len,
73                     dest, destSize, &destLength, NSStringMaxLineWidth) == 0) {
74     if (src) free(src);
75     return [[[NSString alloc]
76                        initWithCStringNoCopy:dest
77                        length:destLength
78                        freeWhenDone:YES] autorelease];
79   }
80
81   if (src) free(src);
82   if (dest) free((void *)dest); dest = NULL;
83   return nil;
84 }
85
86 - (NSString *)stringByDecodingBase64 {
87   unsigned len;
88   size_t destSize;
89   size_t destLength = -1;
90   char   *dest, *src;
91
92   if (StringClass == Nil) StringClass = [NSString class];
93   
94   if ((len = [self cStringLength]) == 0)
95     return @"";
96   
97   destSize = (len / 4 + 1) * 3 + 1;
98   dest = malloc(destSize + 1);
99
100   NSAssert(destSize > 0, @"invalid buffer size ..");
101   NSAssert(dest,         @"invalid buffer ..");
102
103   src = malloc(len + 4);
104   [self getCString:src];
105   src[len] = '\0';
106   
107   if (decode_base64(src, len, dest, destSize, &destLength) == 0) {
108     if (src) free(src);
109     
110     if (*dest == '\0' && destLength > 0) {
111       NSLog(@"ERROR(%s): could not decode %@ as string (contains \\0 bytes)!", 
112             __PRETTY_FUNCTION__, self);
113       abort();
114       if (dest) free(dest);
115       return nil;
116     }
117     
118     return [[[StringClass alloc]
119               initWithCStringNoCopy:dest
120               length:destLength
121               freeWhenDone:YES] autorelease];
122   }
123   else {
124     if (src) free(src);
125     if (dest) free(dest);
126     return nil;
127   }
128 }
129
130 - (NSData *)dataByDecodingBase64 {
131   unsigned len;
132   size_t destSize;
133   size_t destLength = -1;
134   char   *dest, *src;
135
136   if (StringClass == Nil) StringClass = [NSString class];
137   
138   if ((len = [self cStringLength]) == 0)
139     return [NSData data];
140   
141   destSize = (len / 4 + 1) * 3 + 1;
142   dest = malloc(destSize + 1);
143
144   NSAssert(destSize > 0, @"invalid buffer size ..");
145   NSAssert(dest,         @"invalid buffer ..");
146
147   src = malloc(len + 4);
148   [self getCString:src];
149   src[len] = '\0';
150   
151   if (decode_base64(src, len, dest, destSize, &destLength) == 0) {
152     if (src) free(src);
153     return [NSData dataWithBytesNoCopy:dest length:destLength];
154   }
155   
156   if (src)  free(src);
157   if (dest) free(dest);
158   return nil;
159 }
160
161 @end /* NSString(Base64Coding) */
162
163 @implementation NSData(Base64Coding)
164
165 // TODO: explain that size (which RFC specifies that?)
166 static int NSDataMaxLineWidth = 72;
167
168 - (NSData *)dataByEncodingBase64WithLineLength:(unsigned)_lineLength {
169   unsigned len;
170   size_t destSize;
171   size_t destLength = -1;
172   char   *dest;
173   
174   if ((len = [self length]) == 0)
175     return [NSData data];
176   
177   destSize   = (len + 2) / 3 * 4; // 3:4 conversion ratio
178   destSize += destSize / _lineLength + 2; // space for newlines and '\0'
179   destSize += 64;
180
181   dest = malloc(destSize + 4);
182
183   NSAssert(destSize > 0, @"invalid buffer size ..");
184   NSAssert(dest,         @"invalid buffer ..");
185   
186   if (encode_base64([self bytes], len,
187                     dest, destSize, &destLength, _lineLength) == 0) {
188     return [NSData dataWithBytesNoCopy:dest length:destLength];
189   }
190
191   if (dest != NULL) free((void *)dest);
192   return nil;
193 }
194 - (NSData *)dataByEncodingBase64 {
195   return [self dataByEncodingBase64WithLineLength:NSDataMaxLineWidth];
196 }
197
198 - (NSData *)dataByDecodingBase64 {
199   unsigned len;
200   size_t destSize;
201   size_t destLength = -1;
202   char   *dest;
203
204   if ((len = [self length]) == 0)
205     return [NSData data];
206   
207   destSize = (len / 4 + 1) * 3 + 1;
208   dest = malloc(destSize + 4);
209
210   NSAssert(destSize > 0, @"invalid buffer size ..");
211   NSAssert(dest,         @"invalid buffer ..");
212   
213   if (decode_base64([self bytes], len, dest, destSize, &destLength) == 0)
214     return [NSData dataWithBytesNoCopy:dest length:destLength];
215
216   if (dest) free(dest);
217   return nil;
218 }
219
220 - (NSString *)stringByEncodingBase64 {
221   NSData   *data;
222   NSString *s;
223   
224   if ((data = [self dataByEncodingBase64]) == nil)
225     return nil;
226   s = [[NSString alloc] initWithData:data 
227                         encoding:[NSString defaultCStringEncoding]];
228   return [s autorelease];
229 }
230 - (NSString *)stringByDecodingBase64 {
231   NSData   *data;
232   NSString *s;
233   
234   if ((data = [self dataByDecodingBase64]) == nil)
235     return nil;
236   s = [[NSString alloc] initWithData:data
237                         encoding:[NSString defaultCStringEncoding]];
238   return [s autorelease];
239 }
240
241 @end /* NSData(Base64Coding) */
242
243 // functions
244
245 int NGEncodeBase64(const void *_source, unsigned _len,
246                    void *_buffer, unsigned _bufferCapacity,
247                    int _maxLineWidth) {
248   size_t len;
249
250   if ((_source == NULL) || (_buffer == NULL) || (_bufferCapacity == 0))
251     return -1;
252   
253   { // check whether buffer is big enough
254     size_t outSize;
255     outSize =  (_len + 2) / 3 * 4;            // 3:4 conversion ratio
256     outSize += (outSize / _maxLineWidth) + 2; // Space for newlines and NUL
257
258     if (_bufferCapacity < outSize)
259       return -1;
260   }
261   
262   if (encode_base64(_source, _len,
263                     _buffer, _bufferCapacity, &len, _maxLineWidth) == 0) {
264     return len;
265   }
266   else
267     return -1;
268 }
269
270 int NGDecodeBase64(const void *_source, unsigned _len,
271                    void *_buffer, unsigned _bufferCapacity) {
272   size_t len;
273   
274   if ((_source == NULL) || (_buffer == NULL) || (_bufferCapacity == 0))
275     return -1;
276   
277   if (((_len / 4 + 1) * 3 + 1) > _bufferCapacity)
278     return -1;
279   
280   if (decode_base64(_source, _len, _buffer, _bufferCapacity, &len) == 0)
281     return len;
282   else
283     return -1;
284 }
285
286 // private implementation
287
288 static char base64tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
289     "abcdefghijklmnopqrstuvwxyz0123456789+/";
290
291 static char base64idx[128] = {
292     '\377','\377','\377','\377','\377','\377','\377','\377',
293     '\377','\377','\377','\377','\377','\377','\377','\377',
294     '\377','\377','\377','\377','\377','\377','\377','\377',
295     '\377','\377','\377','\377','\377','\377','\377','\377',
296     '\377','\377','\377','\377','\377','\377','\377','\377',
297     '\377','\377','\377',    62,'\377','\377','\377',    63,
298         52,    53,    54,    55,    56,    57,    58,    59,
299         60,    61,'\377','\377','\377','\377','\377','\377',
300     '\377',     0,     1,     2,     3,     4,     5,     6,
301          7,     8,     9,    10,    11,    12,    13,    14,
302         15,    16,    17,    18,    19,    20,    21,    22,
303         23,    24,    25,'\377','\377','\377','\377','\377',
304     '\377',    26,    27,    28,    29,    30,    31,    32,
305         33,    34,    35,    36,    37,    38,    39,    40,
306         41,    42,    43,    44,    45,    46,    47,    48,
307         49,    50,    51,'\377','\377','\377','\377','\377'
308 };
309
310 static inline int encode_base64(const char *_src, size_t _srcLen, char *_dest,
311                                 size_t _destSize, size_t *_destLen,
312                                 int _maxLineWidth) {
313   size_t inLen  = _srcLen;
314   char   *out   = _dest;
315   size_t inPos  = 0;
316   size_t outPos = 0;
317   int    c1, c2, c3;
318   unsigned i;
319   
320   // Get three characters at a time and encode them.
321   for (i = 0; i < inLen / 3; ++i) {
322     c1 = _src[inPos++] & 0xFF;
323     c2 = _src[inPos++] & 0xFF;
324     c3 = _src[inPos++] & 0xFF;
325     out[outPos++] = base64tab[(c1 & 0xFC) >> 2];
326     out[outPos++] = base64tab[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)];
327     out[outPos++] = base64tab[((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6)];
328     out[outPos++] = base64tab[c3 & 0x3F];
329
330     if ((outPos + 1) % (_maxLineWidth + 1) == 0)
331       out[outPos++] = '\n';
332   }
333   
334   // Encode the remaining one or two characters.
335   switch (inLen % 3) {
336     case 0:
337       //out[outPos++] = '\n';
338       break;
339     case 1:
340       c1 = _src[inPos] & 0xFF;
341       out[outPos++] = base64tab[(c1 & 0xFC) >> 2];
342       out[outPos++] = base64tab[((c1 & 0x03) << 4)];
343       out[outPos++] = '=';
344       out[outPos++] = '=';
345       //out[outPos++] = '\n';
346       break;
347     case 2:
348       c1 = _src[inPos++] & 0xFF;
349       c2 = _src[inPos] & 0xFF;
350       out[outPos++] = base64tab[(c1 & 0xFC) >> 2];
351       out[outPos++] = base64tab[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)];
352       out[outPos++] = base64tab[((c2 & 0x0F) << 2)];
353       out[outPos++] = '=';
354       //out[outPos++] = '\n';
355       break;
356   }
357   out[outPos] = 0;
358   *_destLen = outPos;
359   return 0;
360 }
361
362 static inline int decode_base64(const char *_src, size_t inLen, char *out,
363                                 size_t _destSize, size_t *_destLen) 
364 {
365   BOOL   isErr     = NO;
366   BOOL   isEndSeen = NO;
367   register int    b1, b2, b3;
368   register int    a1, a2, a3, a4;
369   register size_t inPos  = 0;
370   register size_t outPos = 0;
371   
372   /* Get four input chars at a time and decode them. Ignore white space
373    * chars (CR, LF, SP, HT). If '=' is encountered, terminate input. If
374    * a char other than white space, base64 char, or '=' is encountered,
375    * flag an input error, but otherwise ignore the char.
376    */
377   while (inPos < inLen) {
378     a1 = a2 = a3 = a4 = 0;
379
380     // get byte 1
381     while (inPos < inLen) {
382       a1 = _src[inPos++] & 0xFF;
383       
384       if (isbase64(a1))
385         break;
386       else if (a1 == '=') {
387         isEndSeen = YES;
388         break;
389       }
390       else if (a1 != '\r' && a1 != '\n' && a1 != ' ' && a1 != '\t') {
391         isErr = YES;
392       }
393     }
394     
395     // get byte 2
396     while (inPos < inLen) {
397       a2 = _src[inPos++] & 0xFF;
398
399       if (isbase64(a2))
400         break;
401       else if (a2 == '=') {
402         isEndSeen = YES;
403         break;
404       }
405       else if (a2 != '\r' && a2 != '\n' && a2 != ' ' && a2 != '\t') {
406         isErr = YES;
407       }
408     }
409     
410     // get byte 3
411     while (inPos < inLen) {
412       a3 = _src[inPos++] & 0xFF;
413
414       if (isbase64(a3))
415         break;
416       else if (a3 == '=') {
417         isEndSeen = YES;
418         break;
419       }
420       else if (a3 != '\r' && a3 != '\n' && a3 != ' ' && a3 != '\t') {
421         isErr = YES;
422       }
423     }
424
425     // get byte 4
426     while (inPos < inLen) {
427       a4 = _src[inPos++] & 0xFF;
428
429       if (isbase64(a4))
430         break;
431       else if (a4 == '=') {
432         isEndSeen = YES;
433         break;
434       }
435       else if (a4 != '\r' && a4 != '\n' && a4 != ' ' && a4 != '\t') {
436         isErr = YES;
437       }
438     }
439
440     // complete chunk
441     if (isbase64(a1) && isbase64(a2) && isbase64(a3) && isbase64(a4)) {
442       a1 = base64idx[a1] & 0xFF;
443       a2 = base64idx[a2] & 0xFF;
444       a3 = base64idx[a3] & 0xFF;
445       a4 = base64idx[a4] & 0xFF;
446       b1 = ((a1 << 2) & 0xFC) | ((a2 >> 4) & 0x03);
447       b2 = ((a2 << 4) & 0xF0) | ((a3 >> 2) & 0x0F);
448       b3 = ((a3 << 6) & 0xC0) | ( a4       & 0x3F);
449       out[outPos++] = (char)b1;
450       out[outPos++] = (char)b2;
451       out[outPos++] = (char)b3;
452     }
453     // 3-chunk
454     else if (isbase64(a1) && isbase64(a2) && isbase64(a3) && a4 == '=') {
455       a1 = base64idx[a1] & 0xFF;
456       a2 = base64idx[a2] & 0xFF;
457       a3 = base64idx[a3] & 0xFF;
458       b1 = ((a1 << 2) & 0xFC) | ((a2 >> 4) & 0x03);
459       b2 = ((a2 << 4) & 0xF0) | ((a3 >> 2) & 0x0F);
460       out[outPos++] = (char)b1;
461       out[outPos++] = (char)b2;
462       break;
463     }
464     // 2-chunk
465     else if (isbase64(a1) && isbase64(a2) && a3 == '=' && a4 == '=') {
466       a1 = base64idx[a1] & 0xFF;
467       a2 = base64idx[a2] & 0xFF;
468       b1 = ((a1 << 2) & 0xFC) | ((a2 >> 4) & 0x03);
469       out[outPos++] = (char)b1;
470       break;
471     }
472     // invalid state
473     else {
474       break;
475     }
476     
477     if (isEndSeen)
478       break;
479   }
480   *_destLen = outPos;
481   return (isErr) ? -1 : 0;
482 }
483
484 // for static linking
485
486 void __link_NGBase64Coding(void) {
487   __link_NGBase64Coding();
488 }