[svn-upgrade] new version iodine (0.6.0~rc1)
[debian/iodine.git] / src / base64.c
1 /*
2  * Copyright (c) 2006-2009 Bjorn Andersson <flex@kryo.se>, Erik Ekman <yarrick@kryo.se>
3  * Mostly rewritten 2009 J.A.Bezemer@opensourcepartners.nl
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "encoding.h"
23 #include "base64.h"
24
25 #define BLKSIZE_RAW 3
26 #define BLKSIZE_ENC 4
27
28 /* Note: the "unofficial" char is last here, which means that the \377 pattern
29    in DOWNCODECCHECK1 ('Y' request) will properly test it. */
30 static const char cb64[] = 
31         "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789+";
32 static unsigned char rev64[256];
33 static int reverse_init = 0;
34
35 static int base64_encode(char *, size_t *, const void *, size_t);
36 static int base64_decode(void *, size_t *, const char *, size_t);
37 static int base64_handles_dots();
38 static int base64_blksize_raw();
39 static int base64_blksize_enc();
40
41 static struct encoder base64_encoder =
42 {
43         "Base64",
44         base64_encode,
45         base64_decode,
46         base64_handles_dots,
47         base64_handles_dots,
48         base64_blksize_raw,
49         base64_blksize_enc
50 };
51
52 struct encoder
53 *get_base64_encoder()
54 {
55         return &base64_encoder;
56 }
57
58 static int 
59 base64_handles_dots()
60 {
61         return 0;
62 }
63
64 static int 
65 base64_blksize_raw()
66 {
67         return BLKSIZE_RAW;
68 }
69
70 static int 
71 base64_blksize_enc()
72 {
73         return BLKSIZE_ENC;
74 }
75
76 inline static void
77 base64_reverse_init()
78 {
79         int i;
80         unsigned char c;
81
82         if (!reverse_init) {
83                 memset (rev64, 0, 256);
84                 for (i = 0; i < 64; i++) {
85                         c = cb64[i];
86                         rev64[(int) c] = i;
87                 }
88                 reverse_init = 1;
89         }
90 }
91
92 static int 
93 base64_encode(char *buf, size_t *buflen, const void *data, size_t size)
94 /*
95  * Fills *buf with max. *buflen characters, encoding size bytes of *data.
96  *
97  * NOTE: *buf space should be at least 1 byte _more_ than *buflen
98  * to hold the trailing '\0'.
99  *
100  * return value    : #bytes filled in buf   (excluding \0)
101  * sets *buflen to : #bytes encoded from data
102  */
103 {
104         unsigned char *udata = (unsigned char *) data;
105         int iout = 0;   /* to-be-filled output char */
106         int iin = 0;    /* one more than last input byte that can be
107                            successfully decoded */
108
109         /* Note: Don't bother to optimize manually. GCC optimizes
110            better(!) when using simplistic array indexing. */
111
112         while (1) {
113                 if (iout >= *buflen || iin >= size)
114                         break;
115                 buf[iout] = cb64[((udata[iin] & 0xfc) >> 2)];
116                 iout++;
117
118                 if (iout >= *buflen || iin >= size) {
119                         iout--;         /* previous char is useless */
120                         break;
121                 }
122                 buf[iout] = cb64[((udata[iin] & 0x03) << 4) |
123                                   ((iin + 1 < size) ?
124                                    ((udata[iin + 1] & 0xf0) >> 4) : 0)];
125                 iin++;                  /* 0 complete, iin=1 */
126                 iout++;
127
128                 if (iout >= *buflen || iin >= size)
129                         break;
130                 buf[iout] = cb64[((udata[iin] & 0x0f) << 2 ) |
131                                   ((iin + 1 < size) ?
132                                    ((udata[iin + 1] & 0xc0) >> 6) : 0)];
133                 iin++;                  /* 1 complete, iin=2 */
134                 iout++;
135
136                 if (iout >= *buflen || iin >= size)
137                         break;
138                 buf[iout] = cb64[(udata[iin] & 0x3f)];
139                 iin++;                  /* 2 complete, iin=3 */
140                 iout++;
141         }
142
143         buf[iout] = '\0';
144
145         /* store number of bytes from data that was used */
146         *buflen = iin;
147
148         return iout;
149 }
150
151 #define REV64(x) rev64[(int) (x)]
152
153 static int
154 base64_decode(void *buf, size_t *buflen, const char *str, size_t slen)
155 /*
156  * Fills *buf with max. *buflen bytes, decoded from slen chars in *str.
157  * Decoding stops early when *str contains \0.
158  * Illegal encoded chars are assumed to decode to zero.
159  *
160  * NOTE: *buf space should be at least 1 byte _more_ than *buflen
161  * to hold a trailing '\0' that is added (though *buf will usually
162  * contain full-binary data).
163  *
164  * return value    : #bytes filled in buf   (excluding \0)
165  */
166 {
167         unsigned char *ubuf = (unsigned char *) buf;
168         int iout = 0;   /* to-be-filled output byte */
169         int iin = 0;    /* next input char to use in decoding */
170
171         base64_reverse_init ();
172
173         /* Note: Don't bother to optimize manually. GCC optimizes
174            better(!) when using simplistic array indexing. */
175
176         while (1) {
177                 if (iout >= *buflen || iin + 1 >= slen ||
178                     str[iin] == '\0' || str[iin + 1] == '\0')
179                         break;
180                 ubuf[iout] = ((REV64(str[iin]) & 0x3f) << 2) | 
181                              ((REV64(str[iin + 1]) & 0x30) >> 4);
182                 iin++;                  /* 0 used up, iin=1 */
183                 iout++;
184
185                 if (iout >= *buflen || iin + 1 >= slen ||
186                     str[iin] == '\0' || str[iin + 1] == '\0')
187                         break;
188                 ubuf[iout] = ((REV64(str[iin]) & 0x0f) << 4) | 
189                              ((REV64(str[iin + 1]) & 0x3c) >> 2);
190                 iin++;                  /* 1 used up, iin=2 */
191                 iout++;
192
193                 if (iout >= *buflen || iin + 1 >= slen ||
194                     str[iin] == '\0' || str[iin + 1] == '\0')
195                         break;
196                 ubuf[iout] = ((REV64(str[iin]) & 0x03) << 6) |
197                              (REV64(str[iin + 1]) & 0x3f);
198                 iin += 2;               /* 2,3 used up, iin=4 */
199                 iout++;
200         }
201
202         ubuf[iout] = '\0';
203
204         return iout;
205 }