Commit | Line | Data |
---|---|---|
2340bcd3 JVH |
1 | #include <stdlib.h> |
2 | #include <string.h> | |
3 | #include <ctype.h> | |
4 | ||
5 | #include "base64.h" | |
6 | ||
7 | #define DEFAULT_CHARLIST "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | |
8 | ||
9 | #define BASE64_PADDING 0x40 | |
10 | #define BASE64_INVALID 0x80 | |
11 | ||
12 | struct base64_s { | |
13 | char charlist[65]; | |
14 | char charmap[256]; | |
15 | int charmap_inited; | |
16 | ||
17 | int use_padding; | |
18 | int skip_spaces; | |
19 | }; | |
20 | ||
21 | static base64_t default_base64 = | |
22 | { .charlist = DEFAULT_CHARLIST, | |
23 | .use_padding = 1 }; | |
24 | ||
25 | static void | |
26 | initialize_charmap(base64_t *base64) | |
27 | { | |
28 | int i; | |
29 | ||
30 | memset(base64->charmap, BASE64_INVALID, sizeof(base64->charmap)); | |
31 | for (i=0; i<64; i++) { | |
32 | base64->charmap[(int)base64->charlist[i]] = i; | |
33 | } | |
34 | base64->charmap['='] = BASE64_PADDING; | |
35 | base64->charmap_inited = 1; | |
36 | } | |
37 | ||
38 | base64_t * | |
39 | base64_init(const char *charlist, int use_padding, int skip_spaces) | |
40 | { | |
41 | base64_t *base64; | |
42 | int i; | |
43 | ||
44 | if (!charlist) { | |
45 | charlist = DEFAULT_CHARLIST; | |
46 | } | |
47 | if (strlen(charlist) != 64) { | |
48 | return NULL; | |
49 | } | |
50 | for (i=0; i<64; i++) { | |
51 | switch (charlist[i]) { | |
52 | case '\r': | |
53 | case '\n': | |
54 | case '=': | |
55 | return NULL; | |
56 | } | |
57 | } | |
58 | ||
59 | base64 = calloc(1, sizeof(base64_t)); | |
60 | if (!base64) { | |
61 | return NULL; | |
62 | } | |
63 | strncpy(base64->charlist, charlist, sizeof(base64->charlist)-1); | |
64 | base64->use_padding = use_padding; | |
65 | base64->skip_spaces = skip_spaces; | |
66 | ||
67 | return base64; | |
68 | } | |
69 | ||
70 | void | |
71 | base64_destroy(base64_t *base64) | |
72 | { | |
73 | free(base64); | |
74 | } | |
75 | ||
76 | int | |
77 | base64_encoded_length(base64_t *base64, int srclen) | |
78 | { | |
79 | if (!base64) { | |
80 | base64 = &default_base64; | |
81 | } | |
82 | ||
83 | if (base64->use_padding) { | |
84 | return ((srclen+2)/3*4)+1; | |
85 | } else { | |
86 | int strlen = 0; | |
87 | switch (srclen % 3) { | |
88 | case 2: | |
89 | strlen += 1; | |
90 | case 1: | |
91 | strlen += 2; | |
92 | default: | |
93 | strlen += srclen/3*4; | |
94 | break; | |
95 | } | |
96 | return strlen+1; | |
97 | } | |
98 | } | |
99 | ||
100 | int | |
101 | base64_encode(base64_t *base64, char *dst, const unsigned char *src, int srclen) | |
102 | { | |
103 | int src_idx, dst_idx; | |
104 | int residue; | |
105 | ||
106 | if (!base64) { | |
107 | base64 = &default_base64; | |
108 | } | |
109 | ||
110 | residue = 0; | |
111 | for (src_idx=dst_idx=0; src_idx<srclen; src_idx++) { | |
112 | residue |= src[src_idx]; | |
113 | ||
114 | switch (src_idx%3) { | |
115 | case 0: | |
116 | dst[dst_idx++] = base64->charlist[(residue>>2)%64]; | |
117 | residue &= 0x03; | |
118 | break; | |
119 | case 1: | |
120 | dst[dst_idx++] = base64->charlist[residue>>4]; | |
121 | residue &= 0x0f; | |
122 | break; | |
123 | case 2: | |
124 | dst[dst_idx++] = base64->charlist[residue>>6]; | |
125 | dst[dst_idx++] = base64->charlist[residue&0x3f]; | |
126 | residue = 0; | |
127 | break; | |
128 | } | |
129 | residue <<= 8; | |
130 | } | |
131 | ||
132 | /* Add padding */ | |
133 | if (src_idx%3 == 1) { | |
134 | dst[dst_idx++] = base64->charlist[residue>>4]; | |
135 | if (base64->use_padding) { | |
136 | dst[dst_idx++] = '='; | |
137 | dst[dst_idx++] = '='; | |
138 | } | |
139 | } else if (src_idx%3 == 2) { | |
140 | dst[dst_idx++] = base64->charlist[residue>>6]; | |
141 | if (base64->use_padding) { | |
142 | dst[dst_idx++] = '='; | |
143 | } | |
144 | } | |
145 | dst[dst_idx] = '\0'; | |
146 | return dst_idx; | |
147 | } | |
148 | ||
149 | int | |
150 | base64_decode(base64_t *base64, unsigned char **dst, const char *src, int srclen) | |
151 | { | |
152 | char *inbuf; | |
153 | int inbuflen; | |
154 | unsigned char *outbuf; | |
155 | int outbuflen; | |
156 | char *srcptr; | |
157 | int index; | |
158 | ||
159 | if (!base64) { | |
160 | base64 = &default_base64; | |
161 | } | |
162 | if (!base64->charmap_inited) { | |
163 | initialize_charmap(base64); | |
164 | } | |
165 | ||
166 | inbuf = malloc(srclen+4); | |
167 | if (!inbuf) { | |
168 | return -1; | |
169 | } | |
170 | memcpy(inbuf, src, srclen); | |
171 | inbuf[srclen] = '\0'; | |
172 | ||
173 | /* Remove all whitespaces from inbuf */ | |
174 | if (base64->skip_spaces) { | |
175 | int i, inbuflen = strlen(inbuf); | |
176 | for (i=0; i<inbuflen; i++) { | |
177 | if (inbuf[i] == '\0') { | |
178 | break; | |
179 | } else if (isspace(inbuf[i])) { | |
180 | memmove(inbuf+i, inbuf+i+1, inbuflen-i); | |
181 | inbuflen -= 1; | |
182 | i -= 1; | |
183 | } | |
184 | } | |
185 | } | |
186 | ||
187 | /* Add padding to inbuf if required */ | |
188 | inbuflen = strlen(inbuf); | |
189 | if (!base64->use_padding) { | |
190 | if (inbuflen%4 == 1) { | |
191 | free(inbuf); | |
192 | return -2; | |
193 | } | |
194 | if (inbuflen%4 == 2) { | |
195 | inbuf[inbuflen] = '='; | |
196 | inbuf[inbuflen+1] = '='; | |
197 | inbuf[inbuflen+2] = '\0'; | |
198 | inbuflen += 2; | |
199 | } else if (inbuflen%4 == 3) { | |
200 | inbuf[inbuflen] = '='; | |
201 | inbuf[inbuflen+1] = '\0'; | |
202 | inbuflen += 1; | |
203 | } | |
204 | } | |
205 | ||
206 | /* Make sure data is divisible by 4 */ | |
207 | if (inbuflen%4 != 0) { | |
208 | free(inbuf); | |
209 | return -3; | |
210 | } | |
211 | ||
212 | /* Calculate the output length without padding */ | |
213 | outbuflen = inbuflen/4*3; | |
214 | if (inbuflen >= 4 && inbuf[inbuflen-1] == '=') { | |
215 | outbuflen -= 1; | |
216 | if (inbuf[inbuflen-2] == '=') { | |
217 | outbuflen -= 1; | |
218 | } | |
219 | } | |
220 | ||
221 | /* Allocate buffer for outputting data */ | |
222 | outbuf = malloc(outbuflen); | |
223 | if (!outbuf) { | |
224 | free(inbuf); | |
225 | return -4; | |
226 | } | |
227 | ||
228 | index = 0; | |
229 | srcptr = inbuf; | |
230 | while (*srcptr) { | |
231 | unsigned char a = base64->charmap[(unsigned char)*(srcptr++)]; | |
232 | unsigned char b = base64->charmap[(unsigned char)*(srcptr++)]; | |
233 | unsigned char c = base64->charmap[(unsigned char)*(srcptr++)]; | |
234 | unsigned char d = base64->charmap[(unsigned char)*(srcptr++)]; | |
235 | ||
236 | if (a == BASE64_INVALID || b == BASE64_INVALID || | |
237 | c == BASE64_INVALID || d == BASE64_INVALID) { | |
238 | return -5; | |
239 | } | |
240 | if (a == BASE64_PADDING || b == BASE64_PADDING) { | |
241 | return -6; | |
242 | } | |
243 | ||
244 | /* Update the first byte */ | |
245 | outbuf[index++] = (a << 2) | ((b & 0x30) >> 4); | |
246 | ||
247 | /* Update the second byte */ | |
248 | if (c == BASE64_PADDING) { | |
249 | break; | |
250 | } | |
251 | outbuf[index++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); | |
252 | ||
253 | /* Update the third byte */ | |
254 | if (d == BASE64_PADDING) { | |
255 | break; | |
256 | } | |
257 | outbuf[index++] = ((c & 0x03) << 6) | d; | |
258 | } | |
259 | if (index != outbuflen) { | |
260 | free(inbuf); | |
261 | free(outbuf); | |
262 | return -7; | |
263 | } | |
264 | ||
265 | free(inbuf); | |
266 | *dst = outbuf; | |
267 | return outbuflen; | |
268 | } |