]> err.no Git - sope/blob - sope-core/NGExtensions/NGBase64Coding.m
added NGResourceLocator
[sope] / sope-core / NGExtensions / NGBase64Coding.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo 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   OGo 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 OGo; 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 newlines 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 static int NSDataMaxLineWidth = 72;
166
167 - (NSData *)dataByEncodingBase64 {
168   unsigned len;
169   size_t destSize;
170   size_t destLength = -1;
171   char   *dest;
172   
173   if ((len = [self length]) == 0)
174     return [NSData data];
175   
176   destSize   = (len + 2) / 3 * 4; // 3:4 conversion ratio
177   destSize += destSize / NSDataMaxLineWidth + 2; // space for newlines and '\0'
178   destSize += 64;
179
180   dest = malloc(destSize + 4);
181
182   NSAssert(destSize > 0, @"invalid buffer size ..");
183   NSAssert(dest,         @"invalid buffer ..");
184   
185   if (encode_base64([self bytes], len,
186                     dest, destSize, &destLength, NSDataMaxLineWidth) == 0) {
187     return [NSData dataWithBytesNoCopy:dest length:destLength];
188   }
189   else {
190     if (dest) free((void *)dest);
191     return nil;
192   }
193 }
194
195 - (NSData *)dataByDecodingBase64 {
196   unsigned len;
197   size_t destSize;
198   size_t destLength = -1;
199   char   *dest;
200
201   if ((len = [self length]) == 0)
202     return [NSData data];
203   
204   destSize = (len / 4 + 1) * 3 + 1;
205   dest = malloc(destSize + 4);
206
207   NSAssert(destSize > 0, @"invalid buffer size ..");
208   NSAssert(dest,         @"invalid buffer ..");
209   
210   if (decode_base64([self bytes], len, dest, destSize, &destLength) == 0)
211     return [NSData dataWithBytesNoCopy:dest length:destLength];
212
213   if (dest) free(dest);
214   return nil;
215 }
216
217 - (NSString *)stringByEncodingBase64 {
218   NSData   *data;
219   NSString *s;
220   
221   if ((data = [self dataByEncodingBase64]) == nil)
222     return nil;
223   s = [[NSString alloc] initWithData:data 
224                         encoding:[NSString defaultCStringEncoding]];
225   return [s autorelease];
226 }
227 - (NSString *)stringByDecodingBase64 {
228   NSData   *data;
229   NSString *s;
230   
231   if ((data = [self dataByDecodingBase64]) == nil)
232     return nil;
233   s = [[NSString alloc] initWithData:data
234                         encoding:[NSString defaultCStringEncoding]];
235   return [s autorelease];
236 }
237
238 @end /* NSData(Base64Coding) */
239
240 // functions
241
242 int NGEncodeBase64(const void *_source, unsigned _len,
243                    void *_buffer, unsigned _bufferCapacity,
244                    int _maxLineWidth) {
245   size_t len;
246
247   if ((_source == NULL) || (_buffer == NULL) || (_bufferCapacity == 0))
248     return -1;
249   
250   { // check whether buffer is big enough
251     size_t outSize;
252     outSize =  (_len + 2) / 3 * 4;            // 3:4 conversion ratio
253     outSize += (outSize / _maxLineWidth) + 2; // Space for newlines and NUL
254
255     if (_bufferCapacity < outSize)
256       return -1;
257   }
258   
259   if (encode_base64(_source, _len,
260                     _buffer, _bufferCapacity, &len, _maxLineWidth) == 0) {
261     return len;
262   }
263   else
264     return -1;
265 }
266
267 int NGDecodeBase64(const void *_source, unsigned _len,
268                    void *_buffer, unsigned _bufferCapacity) {
269   size_t len;
270   
271   if ((_source == NULL) || (_buffer == NULL) || (_bufferCapacity == 0))
272     return -1;
273   
274   if (((_len / 4 + 1) * 3 + 1) > _bufferCapacity)
275     return -1;
276   
277   if (decode_base64(_source, _len, _buffer, _bufferCapacity, &len) == 0)
278     return len;
279   else
280     return -1;
281 }
282
283 // private implementation
284
285 static char base64tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
286     "abcdefghijklmnopqrstuvwxyz0123456789+/";
287
288 static char base64idx[128] = {
289     '\377','\377','\377','\377','\377','\377','\377','\377',
290     '\377','\377','\377','\377','\377','\377','\377','\377',
291     '\377','\377','\377','\377','\377','\377','\377','\377',
292     '\377','\377','\377','\377','\377','\377','\377','\377',
293     '\377','\377','\377','\377','\377','\377','\377','\377',
294     '\377','\377','\377',    62,'\377','\377','\377',    63,
295         52,    53,    54,    55,    56,    57,    58,    59,
296         60,    61,'\377','\377','\377','\377','\377','\377',
297     '\377',     0,     1,     2,     3,     4,     5,     6,
298          7,     8,     9,    10,    11,    12,    13,    14,
299         15,    16,    17,    18,    19,    20,    21,    22,
300         23,    24,    25,'\377','\377','\377','\377','\377',
301     '\377',    26,    27,    28,    29,    30,    31,    32,
302         33,    34,    35,    36,    37,    38,    39,    40,
303         41,    42,    43,    44,    45,    46,    47,    48,
304         49,    50,    51,'\377','\377','\377','\377','\377'
305 };
306
307 static inline int encode_base64(const char *_src, size_t _srcLen, char *_dest,
308                                 size_t _destSize, size_t *_destLen,
309                                 int _maxLineWidth) {
310   size_t inLen  = _srcLen;
311   char   *out   = _dest;
312   size_t inPos  = 0;
313   size_t outPos = 0;
314   int    c1, c2, c3;
315   unsigned i;
316   
317   // Get three characters at a time and encode them.
318   for (i = 0; i < inLen / 3; ++i) {
319     c1 = _src[inPos++] & 0xFF;
320     c2 = _src[inPos++] & 0xFF;
321     c3 = _src[inPos++] & 0xFF;
322     out[outPos++] = base64tab[(c1 & 0xFC) >> 2];
323     out[outPos++] = base64tab[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)];
324     out[outPos++] = base64tab[((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6)];
325     out[outPos++] = base64tab[c3 & 0x3F];
326
327     if ((outPos + 1) % (_maxLineWidth + 1) == 0)
328       out[outPos++] = '\n';
329   }
330   
331   // Encode the remaining one or two characters.
332   switch (inLen % 3) {
333     case 0:
334       //out[outPos++] = '\n';
335       break;
336     case 1:
337       c1 = _src[inPos] & 0xFF;
338       out[outPos++] = base64tab[(c1 & 0xFC) >> 2];
339       out[outPos++] = base64tab[((c1 & 0x03) << 4)];
340       out[outPos++] = '=';
341       out[outPos++] = '=';
342       //out[outPos++] = '\n';
343       break;
344     case 2:
345       c1 = _src[inPos++] & 0xFF;
346       c2 = _src[inPos] & 0xFF;
347       out[outPos++] = base64tab[(c1 & 0xFC) >> 2];
348       out[outPos++] = base64tab[((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4)];
349       out[outPos++] = base64tab[((c2 & 0x0F) << 2)];
350       out[outPos++] = '=';
351       //out[outPos++] = '\n';
352       break;
353   }
354   out[outPos] = 0;
355   *_destLen = outPos;
356   return 0;
357 }
358
359 static inline int decode_base64(const char *_src, size_t inLen, char *out,
360                                 size_t _destSize, size_t *_destLen) 
361 {
362   BOOL   isErr     = NO;
363   BOOL   isEndSeen = NO;
364   register int    b1, b2, b3;
365   register int    a1, a2, a3, a4;
366   register size_t inPos  = 0;
367   register size_t outPos = 0;
368   
369   /* Get four input chars at a time and decode them. Ignore white space
370    * chars (CR, LF, SP, HT). If '=' is encountered, terminate input. If
371    * a char other than white space, base64 char, or '=' is encountered,
372    * flag an input error, but otherwise ignore the char.
373    */
374   while (inPos < inLen) {
375     a1 = a2 = a3 = a4 = 0;
376
377     // get byte 1
378     while (inPos < inLen) {
379       a1 = _src[inPos++] & 0xFF;
380       
381       if (isbase64(a1))
382         break;
383       else if (a1 == '=') {
384         isEndSeen = YES;
385         break;
386       }
387       else if (a1 != '\r' && a1 != '\n' && a1 != ' ' && a1 != '\t') {
388         isErr = YES;
389       }
390     }
391     
392     // get byte 2
393     while (inPos < inLen) {
394       a2 = _src[inPos++] & 0xFF;
395
396       if (isbase64(a2))
397         break;
398       else if (a2 == '=') {
399         isEndSeen = YES;
400         break;
401       }
402       else if (a2 != '\r' && a2 != '\n' && a2 != ' ' && a2 != '\t') {
403         isErr = YES;
404       }
405     }
406     
407     // get byte 3
408     while (inPos < inLen) {
409       a3 = _src[inPos++] & 0xFF;
410
411       if (isbase64(a3))
412         break;
413       else if (a3 == '=') {
414         isEndSeen = YES;
415         break;
416       }
417       else if (a3 != '\r' && a3 != '\n' && a3 != ' ' && a3 != '\t') {
418         isErr = YES;
419       }
420     }
421
422     // get byte 4
423     while (inPos < inLen) {
424       a4 = _src[inPos++] & 0xFF;
425
426       if (isbase64(a4))
427         break;
428       else if (a4 == '=') {
429         isEndSeen = YES;
430         break;
431       }
432       else if (a4 != '\r' && a4 != '\n' && a4 != ' ' && a4 != '\t') {
433         isErr = YES;
434       }
435     }
436
437     // complete chunk
438     if (isbase64(a1) && isbase64(a2) && isbase64(a3) && isbase64(a4)) {
439       a1 = base64idx[a1] & 0xFF;
440       a2 = base64idx[a2] & 0xFF;
441       a3 = base64idx[a3] & 0xFF;
442       a4 = base64idx[a4] & 0xFF;
443       b1 = ((a1 << 2) & 0xFC) | ((a2 >> 4) & 0x03);
444       b2 = ((a2 << 4) & 0xF0) | ((a3 >> 2) & 0x0F);
445       b3 = ((a3 << 6) & 0xC0) | ( a4       & 0x3F);
446       out[outPos++] = (char)b1;
447       out[outPos++] = (char)b2;
448       out[outPos++] = (char)b3;
449     }
450     // 3-chunk
451     else if (isbase64(a1) && isbase64(a2) && isbase64(a3) && a4 == '=') {
452       a1 = base64idx[a1] & 0xFF;
453       a2 = base64idx[a2] & 0xFF;
454       a3 = base64idx[a3] & 0xFF;
455       b1 = ((a1 << 2) & 0xFC) | ((a2 >> 4) & 0x03);
456       b2 = ((a2 << 4) & 0xF0) | ((a3 >> 2) & 0x0F);
457       out[outPos++] = (char)b1;
458       out[outPos++] = (char)b2;
459       break;
460     }
461     // 2-chunk
462     else if (isbase64(a1) && isbase64(a2) && a3 == '=' && a4 == '=') {
463       a1 = base64idx[a1] & 0xFF;
464       a2 = base64idx[a2] & 0xFF;
465       b1 = ((a1 << 2) & 0xFC) | ((a2 >> 4) & 0x03);
466       out[outPos++] = (char)b1;
467       break;
468     }
469     // invalid state
470     else {
471       break;
472     }
473     
474     if (isEndSeen)
475       break;
476   }
477   *_destLen = outPos;
478   return (isErr) ? -1 : 0;
479 }
480
481 // for static linking
482
483 void __link_NGBase64Coding(void) {
484   __link_NGBase64Coding();
485 }