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