1 /* util.c: libshout utility/portability functions
2 *
3 * Copyright 2002-2003 the Icecast team <team@icecast.org>,
4 * Copyright 2012-2015 Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
24
25 #include <sys/types.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29
30 #ifdef HAVE_SYS_SOCKET_H
31 # include <sys/socket.h>
32 #endif
33
34 #ifdef HAVE_WINSOCK2_H
35 # include <winsock2.h>
36 #endif
37
38 #include <shout/shout.h>
39
40 #include "util.h"
41
42 /* first all static tables, then the code */
43
44 static const char hexchars[16] = {
45 '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
46 };
47
48 /* For the next to tables see RFC3986 section 2.2 and 2.3. */
49 static const char safechars[256] = {
50 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
52 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
53 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
54 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
55 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
56 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
57 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
58 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
66 };
67
68 static const char safechars_plus_gen_delims_minus_3F_and_23[256] = {
69 /* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
70 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
71 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
72 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, /* 2x */
73 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 3x */
74 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
75 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, /* 5x */
76 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
77 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, /* 7x */
78 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8x */
79 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9x */
80 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ax */
81 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* bx */
82 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* cx */
83 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* dx */
84 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ex */
85 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* fx */
86 };
87
88 static const char base64table[64] = {
89 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
90 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
91 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
92 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
93 };
94
95
_shout_util_strdup(const char * s)96 char *_shout_util_strdup(const char *s)
97 {
98 if (!s)
99 return NULL;
100
101 return strdup(s);
102 }
103
_shout_util_read_header(int sock,char * buff,unsigned long len)104 int _shout_util_read_header(int sock, char *buff, unsigned long len)
105 {
106 int read_bytes, ret;
107 unsigned long pos;
108 char c;
109
110 read_bytes = 1;
111 pos = 0;
112 ret = 0;
113
114 while ((read_bytes == 1) && (pos < (len - 1))) {
115 read_bytes = 0;
116
117 if ((read_bytes = recv(sock, &c, 1, 0))) {
118 if (c != '\r')
119 buff[pos++] = c;
120 if ((pos > 1) && (buff[pos - 1] == '\n' &&
121 buff[pos - 2] == '\n')) {
122 ret = 1;
123 break;
124 }
125 } else {
126 break;
127 }
128 }
129
130 if (ret)
131 buff[pos] = '\0';
132
133 return ret;
134 }
135
136 /* This isn't efficient, but it doesn't need to be */
_shout_util_base64_encode(char * data)137 char *_shout_util_base64_encode(char *data)
138 {
139 size_t len = strlen(data);
140 char *out = malloc(len * 4 / 3 + 4);
141 char *result = out;
142 size_t chunk;
143
144 while (len > 0) {
145 chunk = (len > 3) ? 3 : len;
146 *out++ = base64table[(*data & 0xFC) >> 2];
147 *out++ = base64table[((*data & 0x03) << 4) | ((*(data + 1) & 0xF0) >> 4)];
148
149 switch (chunk) {
150 case 3:
151 *out++ = base64table[((*(data + 1) & 0x0F) << 2) | ((*(data + 2) & 0xC0) >> 6)];
152 *out++ = base64table[(*(data + 2)) & 0x3F];
153 break;
154 case 2:
155 *out++ = base64table[((*(data + 1) & 0x0F) << 2)];
156 *out++ = '=';
157 break;
158 case 1:
159 *out++ = '=';
160 *out++ = '=';
161 break;
162 }
163 data += chunk;
164 len -= chunk;
165 }
166 *out = 0;
167
168 return result;
169 }
170
171 /* modified from libshout1, which credits Rick Franchuk <rickf@transpect.net>.
172 * Caller must free result.
173 */
_url_encode_with_table(const char * data,const char table[256])174 static char *_url_encode_with_table(const char *data, const char table[256])
175 {
176 const char *p;
177 char *q, *dest;
178 int digit;
179 size_t n;
180
181 for (p = data, n = 0; *p; p++) {
182 n++;
183 if (!table[(unsigned char)(*p)])
184 n += 2;
185 }
186
187 if (!(dest = malloc(n+1))) return NULL;
188
189 for (p = data, q = dest; *p; p++, q++) {
190 if (table[(unsigned char)(*p)]) {
191 *q = *p;
192 } else {
193 *q++ = '%';
194 digit = (*p >> 4) & 0xF;
195 *q++ = hexchars[digit];
196 digit = *p & 0xf;
197 *q = hexchars[digit];
198 n += 2;
199 }
200 }
201 *q = '\0';
202
203 return dest;
204 }
205
_shout_util_url_encode(const char * data)206 char *_shout_util_url_encode(const char *data)
207 {
208 return _url_encode_with_table(data, safechars);
209 }
210
_shout_util_url_encode_resource(const char * data)211 char *_shout_util_url_encode_resource(const char *data)
212 {
213 return _url_encode_with_table(data, safechars_plus_gen_delims_minus_3F_and_23);
214 }
215
_shout_util_dict_new(void)216 util_dict *_shout_util_dict_new(void)
217 {
218 return (util_dict*)calloc(1, sizeof(util_dict));
219 }
220
_shout_util_dict_free(util_dict * dict)221 void _shout_util_dict_free(util_dict *dict)
222 {
223 util_dict *next;
224
225 while (dict) {
226 next = dict->next;
227
228 if (dict->key)
229 free(dict->key);
230 if (dict->val)
231 free(dict->val);
232 free(dict);
233
234 dict = next;
235 }
236 }
237
_shout_util_dict_get(util_dict * dict,const char * key)238 const char *_shout_util_dict_get(util_dict *dict, const char *key)
239 {
240 while (dict) {
241 if (dict->key && !strcmp(key, dict->key))
242 return dict->val;
243 dict = dict->next;
244 }
245
246 return NULL;
247 }
248
_shout_util_dict_set(util_dict * dict,const char * key,const char * val)249 int _shout_util_dict_set(util_dict *dict, const char *key, const char *val)
250 {
251 util_dict *prev;
252
253 if (!dict || !key) {
254 return SHOUTERR_INSANE;
255 }
256
257 prev = NULL;
258 while (dict) {
259 if (!dict->key || !strcmp(dict->key, key))
260 break;
261 prev = dict;
262 dict = dict->next;
263 }
264
265 if (!dict) {
266 dict = _shout_util_dict_new();
267 if (!dict)
268 return SHOUTERR_MALLOC;
269 if (prev)
270 prev->next = dict;
271 }
272
273 if (dict->key) {
274 free(dict->val);
275 } else if (!(dict->key = strdup(key))) {
276 if (prev)
277 prev->next = NULL;
278 _shout_util_dict_free(dict);
279
280 return SHOUTERR_MALLOC;
281 }
282
283 dict->val = strdup(val);
284 if (!dict->val) {
285 return SHOUTERR_MALLOC;
286 }
287
288 return SHOUTERR_SUCCESS;
289 }
290
291 /* given a dictionary, URL-encode each key and val and stringify them in order as
292 * key=val&key=val... if val is set, or just key&key if val is NULL.
293 *
294 * TODO: Memory management needs overhaul.
295 */
_shout_util_dict_urlencode(util_dict * dict,char delim)296 char *_shout_util_dict_urlencode(util_dict *dict, char delim)
297 {
298 size_t reslen, resoffset;
299 char *res, *tmp;
300 char *enc;
301 int start = 1;
302
303 for (res = NULL; dict; dict = dict->next) {
304 /* encode key */
305 if (!dict->key)
306 continue;
307 if (!(enc = _shout_util_url_encode(dict->key))) {
308 if (res)
309 free(res);
310 return NULL;
311 }
312 if (start) {
313 reslen = strlen(enc) + 1;
314 if (!(res = malloc(reslen))) {
315 free(enc);
316 return NULL;
317 }
318 snprintf(res, reslen, "%s", enc);
319 free(enc);
320 start = 0;
321 } else {
322 resoffset = strlen(res);
323 reslen = resoffset + strlen(enc) + 2;
324 if (!(tmp = realloc(res, reslen))) {
325 free(enc);
326 free(res);
327 return NULL;
328 } else {
329 res = tmp;
330 }
331 snprintf(res + resoffset, reslen - resoffset, "%c%s", delim, enc);
332 free(enc);
333 }
334
335 /* encode value */
336 if (!dict->val)
337 continue;
338 if (!(enc = _shout_util_url_encode(dict->val))) {
339 free(res);
340 return NULL;
341 }
342
343 resoffset = strlen(res);
344 reslen = resoffset + strlen(enc) + 2;
345 if (!(tmp = realloc(res, reslen))) {
346 free(enc);
347 free(res);
348 return NULL;
349 } else {
350 res = tmp;
351 }
352 snprintf(res + resoffset, reslen - resoffset, "=%s", enc);
353 free(enc);
354 }
355
356 return res;
357 }
358
_shout_util_dict_next(util_dict ** dict,const char ** key,const char ** val)359 const char *_shout_util_dict_next(util_dict **dict, const char **key, const char **val)
360 {
361 *key = NULL;
362 *val = NULL;
363
364 if (!dict)
365 return NULL;
366 *dict = (*dict)->next;
367 while (*dict && !(*dict)->key)
368 *dict = (*dict)->next;
369 if (!*dict)
370 return NULL;
371 *key = (*dict)->key;
372 *val = (*dict)->val;
373 return *key;
374 }
375