New upstream release:
[debian/iodine.git] / src / base64.c
1 /*
2  * Copyright (c) 2006-2007 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include "encoding.h"
22 #include "common.h"
23 #include "base64.h"
24
25 static const char cb64[] = 
26         "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789.";
27 static unsigned char rev64[128];
28 static int reverse_init = 0;
29
30 #define REV64(x) rev64[(int) (x)]
31 #define MODE    (cb64[62])
32 #define P62     (cb64[62])
33 #define P63     (cb64[63])
34
35 static struct encoder base64_encoder =
36 {
37         "BASE64",
38         base64_encode,
39         base64_decode,
40         base64_handles_dots,
41         base64_handles_dots
42 };
43
44 struct encoder
45 *get_base64_encoder()
46 {
47         return &base64_encoder;
48 }
49
50 int 
51 base64_handles_dots()
52 {
53         return 0;
54 }
55
56 static void
57 findesc(int *count, unsigned char *esc, char c1, char c2, char c3, char c4)
58 {
59         int min1 = 0;
60         int min2 = 0;
61
62         int num1 = 0xFF; /* a very big number */
63         int num2 = 0xFE; /* a nearly as big number */
64
65         int i;
66
67         /* check if no more escapes needed */
68         if (count[62] == 0 && count[63] == 0) {
69                 esc[0] = MODE;
70                 esc[1] = MODE;
71                 return;
72         }
73
74         for (i = 0; i < 62; i++) {
75                 if (i == c1 || i == c2 || i == c3 || i == c4) {
76                         continue;
77                 }
78
79                 if (count[i] < num1) {
80                         min2 = min1;
81                         num2 = num1;
82                         min1 = i;
83                         num1 = count[i];
84                 } else if (count[i] < num2) {
85                         min2 = i;
86                         num2 = count[i];
87                 }
88         }
89
90         esc[0] = cb64[min1];
91         esc[1] = cb64[min2];
92 }
93         
94 static void
95 escape_chars(char *buf, size_t buflen)
96 {
97         int counter[64];
98         int escapes;
99         int reset;
100         int i;
101         unsigned char temp[4096];
102         unsigned char *r;
103         unsigned char *w;
104         unsigned char *e;
105         unsigned char esc[2];
106
107         memset(counter, 0, sizeof(counter));
108         esc[0] = P62;
109         esc[1] = P63;
110
111         /* first, find the number of times each token is used */
112         r = (unsigned char *) buf;
113         w = temp;
114         while (*r) {
115                 counter[REV64(*r)]++;
116                 *w++ = *r++;
117         }
118
119         /* check if work needed */
120         if (counter[62] == 0 && counter[63] == 0)
121                 return;
122         
123         r = temp;
124         w = (unsigned char *) buf;
125         reset = 1;
126         escapes = 0;
127         /* check a block for esc chars */
128         while (*r) {
129                 if (reset == 0 && escapes == 0 && (
130                     r[0] == esc[0] || r[1] == esc[0] ||r[2] == esc[0] ||r[2] == esc[0] ||
131                     r[0] == esc[1] || r[1] == esc[1] ||r[2] == esc[1] ||r[2] == esc[1])) {
132                         /* last set of escape chars were unused.
133                          * if we reset last escape switch then maybe we dont have to switch now */
134
135                         /* change the latest escape switch to 999 (RESET) */
136                         e[1] = MODE;
137                         e[2] = MODE;
138                          
139                         /* store default esc chars */
140                         esc[0] = P62;
141                         esc[1] = P63;
142
143                         reset = 1;
144                 }
145                 /* these two if blocks can not be combined because a block can contain both
146                  * char 9 and/or . and the current escape chars. */
147                 if (r[0] == esc[0] || r[1] == esc[0] ||r[2] == esc[0] ||r[2] == esc[0] ||
148                     r[0] == esc[1] || r[1] == esc[1] ||r[2] == esc[1] ||r[2] == esc[1]) {
149                         /* switch escape chars */
150                         escapes = 0;
151                         reset = 0;
152
153                         /* find 2 suitable escape chars */
154                         findesc(counter, esc, REV64(r[0]), REV64(r[1]), REV64(r[2]), REV64(r[3]));
155
156                         /* store escape switch position */
157                         e = w;
158
159                         /* write new escape chars */
160                         *w++ = MODE;
161                         *w++ = esc[0];
162                         *w++ = esc[1];
163                 }
164                 
165                 /* update counter on remaining chars */
166                 for (i = 0; i < 4; i++) {
167                         if (r[i])
168                                 counter[REV64(r[i])]--;
169                 }
170
171                 /* do the escaping */
172                 for (i = 0; i < 4; i++) {
173                         if (r[i] == P62) {
174                                 r[i] = esc[0];
175                                 escapes++;
176                         } else if (r[i] == P63) {
177                                 r[i] = esc[1];
178                                 escapes++;
179                         }
180                 }       
181                 
182                 /* copy back to buf */
183                 *w++ = *r++;
184                 *w++ = *r++;
185                 *w++ = *r++;
186                 *w++ = *r++;
187         }
188 }
189
190 int 
191 base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
192 {
193         size_t newsize;
194         size_t maxsize;
195         unsigned char c;
196         unsigned char *s;
197         unsigned char *p;
198         unsigned char *q;
199         int i;
200
201         memset(buf, 0, *buflen);
202         
203         if (!reverse_init) {
204                 for (i = 0; i < 64; i++) {
205                         c = cb64[i];
206                         rev64[(int) c] = i;
207                 }
208                 reverse_init = 1;
209         }
210
211         /* how many chars can we encode within the buf */
212         maxsize = 3 * (*buflen / 4 - 1) - 1;
213         /* how big will the encoded data be */
214         newsize = 4 * (size / 3 + 1) + 1;
215         /* if the buffer is too small, eat some of the data */
216         if (*buflen < newsize) {
217                 size = maxsize;
218         }
219
220         p = s = (unsigned char *) buf;
221         q = (unsigned char *)data;
222
223         for(i=0;i<size;i+=3) {
224                 p[0] = cb64[((q[0] & 0xfc) >> 2)];
225                 p[1] = cb64[(((q[0] & 0x03) << 4) | ((q[1] & 0xf0) >> 4))];
226                 p[2] = (i+1 < size) ? cb64[((q[1] & 0x0f) << 2 ) | ((q[2] & 0xc0) >> 6)] : '\0';
227                 p[3] = (i+2 < size) ? cb64[(q[2] & 0x3f)] : '\0';
228                 
229                 q += 3;
230                 p += 4;
231         }       
232         *p = 0;
233
234         escape_chars(buf, *buflen);
235
236         /* store number of bytes from data that was used */
237         *buflen = size;
238
239         return strlen(buf) - 1;
240 }
241
242 #define DECODE_ERROR 0xffffffff
243
244 static int
245 decode_token(const unsigned char *t, unsigned char *data, size_t len) 
246 {
247         if (len < 2)
248                 return 0;
249
250         data[0] = ((REV64(t[0]) & 0x3f) << 2) | 
251                           ((REV64(t[1]) & 0x30) >> 4);
252
253         if (len < 3)
254                 return 1;
255
256         data[1] = ((REV64(t[1]) & 0x0f) << 4) | 
257                           ((REV64(t[2]) & 0x3c) >> 2);
258
259         if (len < 4)
260                 return 2;
261
262         data[2] = ((REV64(t[2]) & 0x03) << 6) |
263                           (REV64(t[3]) & 0x3f);
264
265         return 3;
266 }
267
268 int
269 base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
270 {
271         unsigned char *q;
272         size_t newsize;
273         size_t maxsize;
274         const char *p;
275         unsigned char c;
276         unsigned char block[4];
277         unsigned char prot62;
278         unsigned char prot63;
279         int len;
280         int i;
281
282         if (!reverse_init) {
283                 for (i = 0; i < 64; i++) {
284                         c = cb64[i];
285                         rev64[(int) c] = i;
286                 }
287                 reverse_init = 1;
288         }
289         
290         /* chars needed to decode slen */
291         newsize = 3 * (slen / 4 + 1) + 1;
292         /* encoded chars that fit in buf */
293         maxsize = 4 * (*buflen / 3 + 1) + 1;
294         /* if the buffer is too small, eat some of the data */
295         if (*buflen < newsize) {
296                 slen = maxsize;
297         }
298         
299         prot62 = P62;
300         prot63 = P63;
301
302         q = buf;
303         for (p = str; *p; p += 4) {
304                 /* handle escape instructions */
305                 if (*p == MODE) {
306                         p++;
307                         if (p[0] == MODE && p[1] == MODE) {
308                                 /* reset escape chars */
309                                 prot62 = P62;
310                                 prot63 = P63;
311                                 
312                                 p += 2;
313                         } else {
314                                 prot62 = *p++;
315                                 prot63 = *p++;
316                         }
317                 }
318                 /* since the str is const, we unescape in another buf */
319                 for (i = 0; i < 4; i++) {
320                         block[i] = p[i];
321                         if (prot62 == block[i]) {
322                                 block[i] = P62;
323                         } else if (prot63 == block[i]) {
324                                 block[i] = P63;
325                         }
326                 }
327                 len = decode_token(block, (unsigned char *) q, slen);   
328                 q += len;
329                 slen -= 4;
330                 
331                 if (len < 3)
332                         break;
333         }
334         *q = '\0';
335         
336         return q - (unsigned char *) buf;
337 }
338