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