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