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