Move src to src/lib, include to src/include, test to src/test.
[deb_shairplay.git] / src / lib / base64.c
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 }