2 Copyright (C) 2000-2005 SKYRIX Software AG
4 This file is part of SOPE.
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
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.
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
22 #include "NGBase64Coding.h"
24 #import <Foundation/NSString.h>
25 #import <Foundation/NSException.h>
27 static inline BOOL isbase64(char a) {
28 if (('A' <= a) && (a <= 'Z'))
30 if (('a' <= a) && (a <= 'z'))
32 if (('0' <= a) && (a <= '9'))
34 if ((a == '+') || (a == '/'))
39 static inline int encode_base64(const char *_src, size_t _srcLen, char *_dest,
40 size_t _destSize, size_t *_destLen,
42 static inline int decode_base64(const char *_src, size_t _srcLen, char *_dest,
43 size_t _destSize, size_t *_destLen);
45 @implementation NSString(Base64Coding)
47 static Class StringClass = Nil;
48 static int NSStringMaxLineWidth = 1024;
50 - (NSString *)stringByEncodingBase64 {
53 size_t destLength = -1;
56 if ((len = [self cStringLength]) == 0)
59 destSize = (len + 2) / 3 * 4; // 3:4 conversion ratio
60 destSize += destSize / NSStringMaxLineWidth + 2; // space for '\n' and '\0'
62 dest = malloc(destSize + 4);
63 if (dest == NULL) return nil;
65 NSAssert(destSize > 0, @"invalid buffer size ..");
66 NSAssert(dest, @"invalid buffer ..");
68 src = malloc(len + 4);
69 [self getCString:src];
72 if (encode_base64(src, len,
73 dest, destSize, &destLength, NSStringMaxLineWidth) == 0) {
75 return [[[NSString alloc]
76 initWithCStringNoCopy:dest
78 freeWhenDone:YES] autorelease];
82 if (dest) free((void *)dest); dest = NULL;
86 - (NSString *)stringByDecodingBase64 {
89 size_t destLength = -1;
92 if (StringClass == Nil) StringClass = [NSString class];
94 if ((len = [self cStringLength]) == 0)
97 destSize = (len / 4 + 1) * 3 + 1;
98 dest = malloc(destSize + 1);
100 NSAssert(destSize > 0, @"invalid buffer size ..");
101 NSAssert(dest, @"invalid buffer ..");
103 src = malloc(len + 4);
104 [self getCString:src];
107 if (decode_base64(src, len, dest, destSize, &destLength) == 0) {
110 if (*dest == '\0' && destLength > 0) {
111 NSLog(@"ERROR(%s): could not decode %@ as string (contains \\0 bytes)!",
112 __PRETTY_FUNCTION__, self);
114 if (dest) free(dest);
118 return [[[StringClass alloc]
119 initWithCStringNoCopy:dest
121 freeWhenDone:YES] autorelease];
125 if (dest) free(dest);
130 - (NSData *)dataByDecodingBase64 {
133 size_t destLength = -1;
136 if (StringClass == Nil) StringClass = [NSString class];
138 if ((len = [self cStringLength]) == 0)
139 return [NSData data];
141 destSize = (len / 4 + 1) * 3 + 1;
142 dest = malloc(destSize + 1);
144 NSAssert(destSize > 0, @"invalid buffer size ..");
145 NSAssert(dest, @"invalid buffer ..");
147 src = malloc(len + 4);
148 [self getCString:src];
151 if (decode_base64(src, len, dest, destSize, &destLength) == 0) {
153 return [NSData dataWithBytesNoCopy:dest length:destLength];
157 if (dest) free(dest);
161 @end /* NSString(Base64Coding) */
163 @implementation NSData(Base64Coding)
165 // TODO: explain that size (which RFC specifies that?)
166 static int NSDataMaxLineWidth = 72;
168 - (NSData *)dataByEncodingBase64WithLineLength:(unsigned)_lineLength {
171 size_t destLength = -1;
174 if ((len = [self length]) == 0)
175 return [NSData data];
177 destSize = (len + 2) / 3 * 4; // 3:4 conversion ratio
178 destSize += destSize / _lineLength + 2; // space for newlines and '\0'
181 dest = malloc(destSize + 4);
183 NSAssert(destSize > 0, @"invalid buffer size ..");
184 NSAssert(dest, @"invalid buffer ..");
186 if (encode_base64([self bytes], len,
187 dest, destSize, &destLength, _lineLength) == 0) {
188 return [NSData dataWithBytesNoCopy:dest length:destLength];
191 if (dest != NULL) free((void *)dest);
194 - (NSData *)dataByEncodingBase64 {
195 return [self dataByEncodingBase64WithLineLength:NSDataMaxLineWidth];
198 - (NSData *)dataByDecodingBase64 {
201 size_t destLength = -1;
204 if ((len = [self length]) == 0)
205 return [NSData data];
207 destSize = (len / 4 + 1) * 3 + 1;
208 dest = malloc(destSize + 4);
210 NSAssert(destSize > 0, @"invalid buffer size ..");
211 NSAssert(dest, @"invalid buffer ..");
213 if (decode_base64([self bytes], len, dest, destSize, &destLength) == 0)
214 return [NSData dataWithBytesNoCopy:dest length:destLength];
216 if (dest) free(dest);
220 - (NSString *)stringByEncodingBase64 {
224 if ((data = [self dataByEncodingBase64]) == nil)
226 s = [[NSString alloc] initWithData:data
227 encoding:[NSString defaultCStringEncoding]];
228 return [s autorelease];
230 - (NSString *)stringByDecodingBase64 {
234 if ((data = [self dataByDecodingBase64]) == nil)
236 s = [[NSString alloc] initWithData:data
237 encoding:[NSString defaultCStringEncoding]];
238 return [s autorelease];
241 @end /* NSData(Base64Coding) */
245 int NGEncodeBase64(const void *_source, unsigned _len,
246 void *_buffer, unsigned _bufferCapacity,
250 if ((_source == NULL) || (_buffer == NULL) || (_bufferCapacity == 0))
253 { // check whether buffer is big enough
255 outSize = (_len + 2) / 3 * 4; // 3:4 conversion ratio
256 outSize += (outSize / _maxLineWidth) + 2; // Space for newlines and NUL
258 if (_bufferCapacity < outSize)
262 if (encode_base64(_source, _len,
263 _buffer, _bufferCapacity, &len, _maxLineWidth) == 0) {
270 int NGDecodeBase64(const void *_source, unsigned _len,
271 void *_buffer, unsigned _bufferCapacity) {
274 if ((_source == NULL) || (_buffer == NULL) || (_bufferCapacity == 0))
277 if (((_len / 4 + 1) * 3 + 1) > _bufferCapacity)
280 if (decode_base64(_source, _len, _buffer, _bufferCapacity, &len) == 0)
286 // private implementation
288 static char base64tab[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
289 "abcdefghijklmnopqrstuvwxyz0123456789+/";
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'
310 static inline int encode_base64(const char *_src, size_t _srcLen, char *_dest,
311 size_t _destSize, size_t *_destLen,
313 size_t inLen = _srcLen;
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];
330 if ((outPos + 1) % (_maxLineWidth + 1) == 0)
331 out[outPos++] = '\n';
334 // Encode the remaining one or two characters.
337 //out[outPos++] = '\n';
340 c1 = _src[inPos] & 0xFF;
341 out[outPos++] = base64tab[(c1 & 0xFC) >> 2];
342 out[outPos++] = base64tab[((c1 & 0x03) << 4)];
345 //out[outPos++] = '\n';
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)];
354 //out[outPos++] = '\n';
362 static inline int decode_base64(const char *_src, size_t inLen, char *out,
363 size_t _destSize, size_t *_destLen)
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;
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.
377 while (inPos < inLen) {
378 a1 = a2 = a3 = a4 = 0;
381 while (inPos < inLen) {
382 a1 = _src[inPos++] & 0xFF;
386 else if (a1 == '=') {
390 else if (a1 != '\r' && a1 != '\n' && a1 != ' ' && a1 != '\t') {
396 while (inPos < inLen) {
397 a2 = _src[inPos++] & 0xFF;
401 else if (a2 == '=') {
405 else if (a2 != '\r' && a2 != '\n' && a2 != ' ' && a2 != '\t') {
411 while (inPos < inLen) {
412 a3 = _src[inPos++] & 0xFF;
416 else if (a3 == '=') {
420 else if (a3 != '\r' && a3 != '\n' && a3 != ' ' && a3 != '\t') {
426 while (inPos < inLen) {
427 a4 = _src[inPos++] & 0xFF;
431 else if (a4 == '=') {
435 else if (a4 != '\r' && a4 != '\n' && a4 != ' ' && a4 != '\t') {
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;
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;
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;
481 return (isErr) ? -1 : 0;
484 // for static linking
486 void __link_NGBase64Coding(void) {
487 __link_NGBase64Coding();