]> err.no Git - sope/blob - sope-core/NGStreams/NGBase64Stream.m
added OSX framework support
[sope] / sope-core / NGStreams / NGBase64Stream.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 <NGStreams/NGBase64Stream.h>
23 #include <NGStreams/NGCTextStream.h>
24 #include "common.h"
25
26 static inline BOOL isbase64(char a) {
27   if (('A' <= a) && (a <= 'Z')) return YES;
28   if (('a' <= a) && (a <= 'z')) return YES;
29   if (('0' <= a) && (a <= '9')) return YES;
30   if ((a == '+') || (a == '/')) return YES;
31   return NO;
32 }
33
34 @implementation NGBase64Stream
35
36 // ******************** decoding ********************
37
38 static char dmap[256] = {
39   127, 127, 127, 127, 127, 127, 127, 127,      //   000-007
40   127, 127, 127, 127, 127, 127, 127, 127,      //   010-017
41   127, 127, 127, 127, 127, 127, 127, 127,      //   020-027
42   127, 127, 127, 127, 127, 127, 127, 127,      //   030-037
43   127, 127, 127, 127, 127, 127, 127, 127,      //   040-047   !"#$%&'
44   127, 127, 127,  62, 127, 127, 127,  63,      //   050-057  ()*+,-./
45    52,  53,  54,  55,  56,  57,  58,  59,      //   060-067  01234567
46    60,  61, 127, 127, 127, 126, 127, 127,      //   070-077  89:;<=>?
47
48   127,   0,   1,   2,   3,   4,   5,   6,      //   100-107  @ABCDEFG
49     7,   8,   9,  10,  11,  12,  13,  14,      //   110-117  HIJKLMNO
50    15,  16,  17,  18,  19,  20,  21,  22,      //   120-127  PQRSTUVW
51    23,  24,  25, 127, 127, 127, 127, 127,      //   130-137  XYZ[\]^_
52   127,  26,  27,  28,  29,  30,  31,  32,      //   140-147  `abcdefg
53    33,  34,  35,  36,  37,  38,  39,  40,      //   150-157  hijklmno
54    41,  42,  43,  44,  45,  46,  47,  48,      //   160-167  pqrstuvw
55    49,  50,  51, 127, 127, 127, 127, 127,      //   170-177  xyz{|}~
56
57   127, 127, 127, 127, 127, 127, 127, 127,      //   200-207
58   127, 127, 127, 127, 127, 127, 127, 127,      //   210-217
59   127, 127, 127, 127, 127, 127, 127, 127,      //   220-227
60   127, 127, 127, 127, 127, 127, 127, 127,      //   230-237
61   127, 127, 127, 127, 127, 127, 127, 127,      //   240-247
62   127, 127, 127, 127, 127, 127, 127, 127,      //   250-257
63   127, 127, 127, 127, 127, 127, 127, 127,      //   260-267
64   127, 127, 127, 127, 127, 127, 127, 127,      //   270-277
65
66   127, 127, 127, 127, 127, 127, 127, 127,      //   300-307
67   127, 127, 127, 127, 127, 127, 127, 127,      //   310-317
68   127, 127, 127, 127, 127, 127, 127, 127,      //   320-327
69   127, 127, 127, 127, 127, 127, 127, 127,      //   330-337
70   127, 127, 127, 127, 127, 127, 127, 127,      //   340-347
71   127, 127, 127, 127, 127, 127, 127, 127,      //   350-357
72   127, 127, 127, 127, 127, 127, 127, 127,      //   360-367
73   127, 127, 127, 127, 127, 127, 127, 127,      //   370-377
74 };
75
76 - (unsigned)readBytes:(void *)_buf count:(unsigned)_len {
77   unsigned char chunk[4]; // input buffer
78   unsigned char chunkLen; // input buffer length
79   unsigned      readLen = 0;
80
81   if (self->decBufferLen == 0) { // no bytes in buffer, read next token
82     register unsigned value;
83     {
84       volatile unsigned pos = 0, toGo = 4;
85       char     tmp[4];
86
87       memset(chunk, 126, sizeof(chunk)); // set all EOF
88
89       NS_DURING {
90         do {
91           unsigned      readCount = 0;
92           unsigned char i;
93
94           readCount = [super readBytes:tmp count:toGo];
95           NSAssert(readCount != 0, @"invalid result from readBytes:count:");
96
97           for (i = 0; i < readCount; i++) {
98             if (isbase64(tmp[(int)i])) {
99               chunk[pos] = tmp[(int)i];
100               pos++;
101               toGo--;
102             }
103           }
104         }
105         while (toGo > 0);
106       }
107       NS_HANDLER {
108         if ([localException isKindOfClass:[NGEndOfStreamException class]]) {
109           if (pos == 0)
110             [localException raise];
111         }
112         else
113           [localException raise];
114       }
115       NS_ENDHANDLER;
116
117       chunkLen = pos;
118     }
119     NSAssert(chunkLen > 0, @"invalid chunk len (should have thrown EOF) ..");
120
121     if (chunkLen == 4) { // complete token
122       NSCAssert(self->decBufferLen  == 0, @"data pending in buffer ..");
123       NSCAssert(chunkLen == 4, @"invalid chunk size ..");
124   
125       value = ((dmap[chunk[0]] << 18) |
126                (dmap[chunk[1]] << 12) |
127                (dmap[chunk[2]] << 6) |
128                (dmap[chunk[3]]));
129   
130       self->decBuffer[0] = (unsigned char)(0xFF & (value >> 16));
131       self->decBuffer[1] = (unsigned char)(0xFF & (value >> 8));
132       self->decBuffer[2] = (unsigned char)(0xFF & (value));
133       self->decBufferLen = 3;
134     }
135     else {
136       unsigned char b0 = dmap[chunk[0]];
137       unsigned char b1 = dmap[chunk[1]];
138       unsigned char b2 = dmap[chunk[2]];
139       unsigned char b3 = dmap[chunk[3]];
140       char          eqCount = 0; // number of equal signs
141
142       NSCAssert(self->decBufferLen == 0, @"data pending in buffer ..");
143
144       if (b0 == 126) { b0 = 0; eqCount++; }
145       if (b1 == 126) { b1 = 0; eqCount++; }
146       if (b2 == 126) { b2 = 0; eqCount++; }
147       if (b3 == 126) { b3 = 0; eqCount++; }
148
149       value = ((b0 << 18) | (b1 << 12) | (b2 << 6) | (b3));
150
151       self->decBuffer[0] = (unsigned char)(value >> 16);
152       self->decBufferLen = 1;
153       if (eqCount <= 1) {
154         self->decBuffer[1] = (unsigned char)((value >> 8) & 0xFF);
155         self->decBufferLen = 2;
156         if (eqCount == 0) {
157           self->decBuffer[2] = (unsigned char)((value & 0xFF));
158           self->decBufferLen = 3;
159         }
160       }
161     }
162
163     NSAssert((self->decBufferLen > 0) && (self->decBufferLen < 4),
164              @"invalid result length ..");
165   }
166
167   // copy decoded bytes to output buffer
168   if (_len >= self->decBufferLen) {
169     readLen = self->decBufferLen;
170     memcpy(_buf, self->decBuffer, readLen);
171     self->decBufferLen = 0;
172   }
173   else {
174     readLen = _len;
175     NSAssert((readLen > 0) && (readLen < 3), @"invalid length ..");
176
177     if (readLen == 1) {
178       *(char *)_buf = self->decBuffer[0];
179       self->decBuffer[0] = self->decBuffer[1];
180       self->decBuffer[1] = self->decBuffer[2];
181       self->decBufferLen--;
182     }
183     else { // readLen == 2;
184       ((char *)_buf)[0] = self->decBuffer[0];
185       ((char *)_buf)[1] = self->decBuffer[1];
186       self->decBuffer[0] = self->decBuffer[2];
187       self->decBufferLen -= 2;
188     }
189   }
190   return readLen;
191 }
192
193 // ******************** encoding ********************
194
195 static char emap[] = {
196   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',     // 0-7
197   'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',     // 8-15
198   'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',     // 16-23
199   'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',     // 24-31
200   'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',     // 32-39
201   'o', 'p', 'q', 'r', 's', 't', 'u', 'v',     // 40-47
202   'w', 'x', 'y', 'z', '0', '1', '2', '3',     // 48-55
203   '4', '5', '6', '7', '8', '9', '+', '/'      // 56-63
204 };
205   
206 static inline void _encodeToken(NGBase64Stream *self) {
207   int i = self->lineLength;
208
209   // ratio 3:4
210   self->line[i]     = emap[0x3F & (self->buf >> 18)]; // sextet 1 (octet 1)
211   self->line[i + 1] = emap[0x3F & (self->buf >> 12)]; // sextet 2 (octet 1 and 2)
212   self->line[i + 2] = emap[0x3F & (self->buf >> 6)];  // sextet 3 (octet 2 and 3)
213   self->line[i + 3] = emap[0x3F & (self->buf)];       // sextet 4 (octet 3)
214   self->lineLength += 4;
215   self->buf        =  0;
216   self->bufBytes   =  0;
217 }
218
219 static inline void _encodePartialToken(NGBase64Stream *self) {
220   int i = self->lineLength;
221
222   self->line[i]     = emap[0x3F & (self->buf >> 18)]; // sextet 1 (octet 1)
223   self->line[i + 1] = emap[0x3F & (self->buf >> 12)]; // sextet 2 (octet 1 and 2)
224   self->line[i + 2] = (self->bufBytes == 1) ? '=' : emap[0x3F & (self->buf >> 6)];
225   self->line[i + 3] = (self->bufBytes <= 2) ? '=' : emap[0x3F & (self->buf)];
226
227   self->lineLength += 4;
228   self->buf        =  0;
229   self->bufBytes   =  0;
230 }
231
232 static inline void _flushLine(NGBase64Stream *self) {
233   [self->source safeWriteBytes:self->line count:self->lineLength];
234   self->lineLength = 0;
235 }
236
237 static inline void 
238 _encode(NGBase64Stream *self, const char *_in, unsigned _inLen) 
239 {
240   // Given a sequence of input bytes, produces a sequence of output bytes
241   // using the base64 encoding.
242   register unsigned int i;
243
244   for (i = 0; i < _inLen; i++) {
245     if (self->bufBytes == 0)
246       self->buf = ((self->buf & 0xFFFF) | (_in[i] << 16));
247     else if (self->bufBytes == 1)
248       self->buf = ((self->buf & 0xFF00FF) | ((_in[i] << 8) & 0xFFFF));
249     else
250       self->buf = ((self->buf & 0xFFFF00) | (_in[i] & 0xFF));
251
252     if ((++(self->bufBytes)) == 3) {
253       _encodeToken(self);
254       if (self->lineLength >= 72)
255         _flushLine(self);
256     }
257
258     if (i == (_inLen - 1)) {
259       if ((self->bufBytes > 0) && (self->bufBytes < 3))
260         _encodePartialToken(self);
261       if (self->lineLength > 0)
262         _flushLine(self);
263     }
264   }
265
266   // reset line buffer
267   memset(self->line, 0, sizeof(self->line));
268 }
269
270 - (BOOL)close {
271   if (![self flush])  return NO;
272   if (![super close]) return NO;
273   return YES;
274 }
275 - (BOOL)flush {
276   // output buffer
277   if (self->bufBytes)
278     _encodePartialToken(self);
279   _flushLine(self);
280
281   // reset line buffer
282   memset(self->line, 0, sizeof(self->line));
283   
284   return [super flush];
285 }
286
287 - (unsigned)writeBytes:(const void *)_buf count:(unsigned)_len {
288   _encode(self, _buf, _len);
289   return _len;
290 }
291
292 @end /* NGBase64Stream */