1 /* Icecast
2 *
3 * This program is distributed under the GNU General Public License, version 2.
4 * A copy of this license is included with this source.
5 *
6 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7 * Michael Smith <msmith@xiph.org>,
8 * oddsock <oddsock@xiph.org>,
9 * Karl Heyes <karl@xiph.org>
10 * and others (see AUTHORS for details).
11 * Copyright 2012-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
12 */
13
14 #ifdef HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17
18 #include <sys/types.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22
23 #ifndef _WIN32
24 #include <sys/time.h>
25 #include <sys/socket.h>
26 #include <unistd.h>
27 #ifdef HAVE_POLL
28 #include <sys/poll.h>
29 #endif
30 #else
31 #include <winsock2.h>
32 #include <windows.h>
33 #include <stdio.h>
34 #define snprintf _snprintf
35 #define strcasecmp stricmp
36 #define strncasecmp strnicmp
37 #endif
38
39 #include "net/sock.h"
40 #include "thread/thread.h"
41
42 #include "cfgfile.h"
43 #include "util.h"
44 #include "compat.h"
45 #include "refbuf.h"
46 #include "connection.h"
47 #include "client.h"
48 #include "source.h"
49
50 #define CATMODULE "util"
51
52 #include "logging.h"
53
54 /* Abstract out an interface to use either poll or select depending on which
55 * is available (poll is preferred) to watch a single fd.
56 *
57 * timeout is in milliseconds.
58 *
59 * returns > 0 if activity on the fd occurs before the timeout.
60 * 0 if no activity occurs
61 * < 0 for error.
62 */
util_timed_wait_for_fd(sock_t fd,int timeout)63 int util_timed_wait_for_fd(sock_t fd, int timeout)
64 {
65 #ifdef HAVE_POLL
66 struct pollfd ufds;
67
68 ufds.fd = fd;
69 ufds.events = POLLIN;
70 ufds.revents = 0;
71
72 return poll(&ufds, 1, timeout);
73 #else
74 fd_set rfds;
75 struct timeval tv, *p=NULL;
76
77 FD_ZERO(&rfds);
78 FD_SET(fd, &rfds);
79
80 if(timeout >= 0) {
81 tv.tv_sec = timeout/1000;
82 tv.tv_usec = (timeout % 1000)*1000;
83 p = &tv;
84 }
85 return select(fd+1, &rfds, NULL, NULL, p);
86 #endif
87 }
88
util_read_header(sock_t sock,char * buff,unsigned long len,int entire)89 int util_read_header(sock_t sock, char *buff, unsigned long len, int entire)
90 {
91 int read_bytes, ret;
92 unsigned long pos;
93 char c;
94 ice_config_t *config;
95 int header_timeout;
96
97 config = config_get_config();
98 header_timeout = config->header_timeout;
99 config_release_config();
100
101 read_bytes = 1;
102 pos = 0;
103 ret = 0;
104
105 while ((read_bytes == 1) && (pos < (len - 1))) {
106 read_bytes = 0;
107
108 if (util_timed_wait_for_fd(sock, header_timeout*1000) > 0) {
109
110 if ((read_bytes = recv(sock, &c, 1, 0))) {
111 if (c != '\r') buff[pos++] = c;
112 if (entire) {
113 if ((pos > 1) && (buff[pos - 1] == '\n' &&
114 buff[pos - 2] == '\n')) {
115 ret = 1;
116 break;
117 }
118 }
119 else {
120 if ((pos > 1) && (buff[pos - 1] == '\n')) {
121 ret = 1;
122 break;
123 }
124 }
125 }
126 } else {
127 break;
128 }
129 }
130
131 if (ret) buff[pos] = '\0';
132
133 return ret;
134 }
135
util_get_extension(const char * path)136 char *util_get_extension(const char *path) {
137 char *ext = strrchr(path, '.');
138
139 if(ext == NULL)
140 return "";
141 else
142 return ext+1;
143 }
144
util_check_valid_extension(const char * uri)145 int util_check_valid_extension(const char *uri) {
146 int ret = 0;
147 char *p2;
148
149 if (uri) {
150 p2 = strrchr(uri, '.');
151 if (p2) {
152 p2++;
153 if (strncmp(p2, "xsl", strlen("xsl")) == 0) {
154 /* Build the full path for the request, concatenating the webroot from the config.
155 ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
156 */
157 ret = XSLT_CONTENT;
158 }
159 if (strncmp(p2, "htm", strlen("htm")) == 0) {
160 /* Build the full path for the request, concatenating the webroot from the config.
161 ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
162 */
163 ret = HTML_CONTENT;
164 }
165 if (strncmp(p2, "html", strlen("html")) == 0) {
166 /* Build the full path for the request, concatenating the webroot from the config.
167 ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
168 */
169 ret = HTML_CONTENT;
170 }
171
172 }
173 }
174 return ret;
175 }
176
hex(char c)177 static int hex(char c)
178 {
179 if(c >= '0' && c <= '9')
180 return c - '0';
181 else if(c >= 'A' && c <= 'F')
182 return c - 'A' + 10;
183 else if(c >= 'a' && c <= 'f')
184 return c - 'a' + 10;
185 else
186 return -1;
187 }
188
verify_path(char * path)189 static int verify_path(char *path) {
190 int dir = 0, indotseq = 0;
191
192 while(*path) {
193 if(*path == '/' || *path == '\\') {
194 if(indotseq)
195 return 0;
196 if(dir)
197 return 0;
198 dir = 1;
199 path++;
200 continue;
201 }
202
203 if(dir || indotseq) {
204 if(*path == '.')
205 indotseq = 1;
206 else
207 indotseq = 0;
208 }
209
210 dir = 0;
211 path++;
212 }
213
214 return 1;
215 }
216
util_get_path_from_uri(char * uri)217 char *util_get_path_from_uri(char *uri) {
218 char *path = util_normalise_uri(uri);
219 char *fullpath;
220
221 if(!path)
222 return NULL;
223 else {
224 fullpath = util_get_path_from_normalised_uri(path);
225 free(path);
226 return fullpath;
227 }
228 }
229
util_get_path_from_normalised_uri(const char * uri)230 char *util_get_path_from_normalised_uri(const char *uri) {
231 char *fullpath;
232 char *webroot;
233 ice_config_t *config = config_get_config();
234
235 webroot = config->webroot_dir;
236
237 fullpath = malloc(strlen(uri) + strlen(webroot) + 1);
238 if (fullpath)
239 sprintf (fullpath, "%s%s", webroot, uri);
240 config_release_config();
241
242 return fullpath;
243 }
244
245 static char hexchars[16] = {
246 '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
247 };
248
249 static char safechars[256] = {
250 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
251 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
252 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
253 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
254 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
255 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
256 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
257 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
258 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
259 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
260 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
261 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
262 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
263 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
264 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
265 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
266 };
267
util_url_escape(const char * src)268 char *util_url_escape (const char *src)
269 {
270 size_t len;
271 char *dst;
272 unsigned char *source = (unsigned char *)src;
273 size_t i, j;
274
275 if (!src)
276 return NULL;
277
278 len = strlen(src);
279 /* Efficiency not a big concern here, keep the code simple/conservative */
280 dst = calloc(1, len*3 + 1);
281
282 for(i = 0, j = 0; i < len; i++) {
283 if(safechars[source[i]]) {
284 dst[j++] = source[i];
285 } else {
286 dst[j++] = '%';
287 dst[j++] = hexchars[(source[i] >> 4) & 0x0F];
288 dst[j++] = hexchars[ source[i] & 0x0F];
289 }
290 }
291
292 dst[j] = 0;
293 return dst;
294 }
295
util_url_unescape(const char * src)296 char *util_url_unescape (const char *src)
297 {
298 int len = strlen(src);
299 char *decoded;
300 int i;
301 char *dst;
302 int done = 0;
303
304 decoded = calloc(1, len + 1);
305
306 dst = decoded;
307
308 for(i=0; i < len; i++) {
309 switch(src[i]) {
310 case '%':
311 if(i+2 >= len) {
312 free(decoded);
313 return NULL;
314 }
315 if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
316 free(decoded);
317 return NULL;
318 }
319
320 *dst++ = hex(src[i+1]) * 16 + hex(src[i+2]);
321 i+= 2;
322 break;
323 case '#':
324 done = 1;
325 break;
326 case 0:
327 ICECAST_LOG_ERROR("Fatal internal logic error in util_url_unescape()");
328 free(decoded);
329 return NULL;
330 break;
331 default:
332 *dst++ = src[i];
333 break;
334 }
335 if(done)
336 break;
337 }
338
339 *dst = 0; /* null terminator */
340
341 return decoded;
342 }
343
344 /* Get an absolute path (from the webroot dir) from a URI. Return NULL if the
345 * path contains 'disallowed' sequences like foo/../ (which could be used to
346 * escape from the webroot) or if it cannot be URI-decoded.
347 * Caller should free the path.
348 */
util_normalise_uri(const char * uri)349 char *util_normalise_uri(const char *uri) {
350 char *path;
351 #ifdef _WIN32
352 size_t len;
353 #endif
354
355 if(uri[0] != '/')
356 return NULL;
357
358 path = util_url_unescape(uri);
359
360 if(path == NULL) {
361 ICECAST_LOG_WARN("Error decoding URI: %s\n", uri);
362 return NULL;
363 }
364
365 #ifdef _WIN32
366 /* If we are on Windows, strip trailing dots, as Win API strips it anyway */
367 for (len = strlen(path); len > 0 && path[len-1] == '.'; len--)
368 path[len-1] = '\0';
369 #endif
370
371 /* We now have a full URI-decoded path. Check it for allowability */
372 if(verify_path(path))
373 return path;
374 else {
375 ICECAST_LOG_WARN("Rejecting invalid path \"%s\"", path);
376 free(path);
377 return NULL;
378 }
379 }
380
381 static char base64table[64] = {
382 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
383 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
384 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
385 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
386 };
387
388 static signed char base64decode[256] = {
389 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
390 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
391 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
392 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -1, -2, -2,
393 -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
394 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
395 -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
396 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
397 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
398 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
399 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
400 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
401 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
402 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
403 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
404 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
405 };
406
util_bin_to_hex(unsigned char * data,int len)407 char *util_bin_to_hex(unsigned char *data, int len)
408 {
409 char *hex = malloc(len*2 + 1);
410 int i;
411
412 for(i = 0; i < len; i++) {
413 hex[i*2] = hexchars[(data[i]&0xf0) >> 4];
414 hex[i*2+1] = hexchars[data[i]&0x0f];
415 }
416
417 hex[len*2] = 0;
418
419 return hex;
420 }
421
422 /* This isn't efficient, but it doesn't need to be */
util_base64_encode(const char * data)423 char *util_base64_encode(const char *data)
424 {
425 int len = strlen(data);
426 char *out = malloc(len*4/3 + 4);
427 char *result = out;
428 int chunk;
429
430 while(len > 0) {
431 chunk = (len >3)?3:len;
432 *out++ = base64table[(*data & 0xFC)>>2];
433 *out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)];
434 switch(chunk) {
435 case 3:
436 *out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)];
437 *out++ = base64table[(*(data+2)) & 0x3F];
438 break;
439 case 2:
440 *out++ = base64table[((*(data+1) & 0x0F)<<2)];
441 *out++ = '=';
442 break;
443 case 1:
444 *out++ = '=';
445 *out++ = '=';
446 break;
447 }
448 data += chunk;
449 len -= chunk;
450 }
451 *out = 0;
452
453 return result;
454 }
455
util_base64_decode(const char * data)456 char *util_base64_decode(const char *data)
457 {
458 const unsigned char *input = (const unsigned char *)data;
459 int len = strlen (data);
460 char *out = malloc(len*3/4 + 5);
461 char *result = out;
462 signed char vals[4];
463
464 while(len > 0) {
465 if(len < 4)
466 {
467 free(result);
468 return NULL; /* Invalid Base64 data */
469 }
470
471 vals[0] = base64decode[*input++];
472 vals[1] = base64decode[*input++];
473 vals[2] = base64decode[*input++];
474 vals[3] = base64decode[*input++];
475
476 if(vals[0] < 0 || vals[1] < 0 || vals[2] < -1 || vals[3] < -1) {
477 len -= 4;
478 continue;
479 }
480
481 *out++ = vals[0]<<2 | vals[1]>>4;
482 /* vals[3] and (if that is) vals[2] can be '=' as padding, which is
483 looked up in the base64decode table as '-1'. Check for this case,
484 and output zero-terminators instead of characters if we've got
485 padding. */
486 if(vals[2] >= 0)
487 *out++ = ((vals[1]&0x0F)<<4) | (vals[2]>>2);
488 else
489 *out++ = 0;
490
491 if(vals[3] >= 0)
492 *out++ = ((vals[2]&0x03)<<6) | (vals[3]);
493 else
494 *out++ = 0;
495
496 len -= 4;
497 }
498 *out = 0;
499
500 return result;
501 }
502
503 /* TODO, FIXME: handle memory allocation errors better. */
_build_headers_loop(char ** ret,size_t * len,ice_config_http_header_t * header,int status)504 static inline void _build_headers_loop(char **ret, size_t *len, ice_config_http_header_t *header, int status) {
505 size_t headerlen;
506 const char *name;
507 const char *value;
508 char * r = *ret;
509
510 if (!header)
511 return;
512
513 do {
514 /* filter out header's we don't use. */
515 if (header->status != 0 && header->status != status) continue;
516
517 /* get the name of the header */
518 name = header->name;
519
520 /* handle type of the header */
521 value = NULL;
522 switch (header->type) {
523 case HTTP_HEADER_TYPE_STATIC:
524 value = header->value;
525 break;
526 }
527
528 /* check data */
529 if (!name || !value)
530 continue;
531
532 /* append the header to the buffer */
533 headerlen = strlen(name) + strlen(value) + 4;
534 *len += headerlen;
535 r = realloc(r, *len);
536 strcat(r, name);
537 strcat(r, ": ");
538 strcat(r, value);
539 strcat(r, "\r\n");
540 } while ((header = header->next));
541 *ret = r;
542 }
_build_headers(int status,ice_config_t * config,source_t * source)543 static inline char * _build_headers(int status, ice_config_t *config, source_t *source) {
544 mount_proxy *mountproxy = NULL;
545 char *ret = NULL;
546 size_t len = 1;
547
548 if (source)
549 mountproxy = config_find_mount(config, source->mount, MOUNT_TYPE_NORMAL);
550
551 ret = calloc(1, 1);
552 *ret = 0;
553
554 _build_headers_loop(&ret, &len, config->http_headers, status);
555 if (mountproxy && mountproxy->http_headers)
556 _build_headers_loop(&ret, &len, mountproxy->http_headers, status);
557
558 return ret;
559 }
560
util_http_build_header(char * out,size_t len,ssize_t offset,int cache,int status,const char * statusmsg,const char * contenttype,const char * charset,const char * datablock,struct source_tag * source)561 ssize_t util_http_build_header(char * out, size_t len, ssize_t offset,
562 int cache,
563 int status, const char * statusmsg,
564 const char * contenttype, const char * charset,
565 const char * datablock,
566 struct source_tag * source) {
567 const char * http_version = "1.0";
568 ice_config_t *config;
569 time_t now;
570 struct tm result;
571 struct tm *gmtime_result;
572 char currenttime_buffer[80];
573 char status_buffer[80];
574 char contenttype_buffer[80];
575 ssize_t ret;
576 char * extra_headers;
577
578 if (!out)
579 return -1;
580
581 if (offset == -1)
582 offset = strlen (out);
583
584 out += offset;
585 len -= offset;
586
587 if (status == -1)
588 {
589 status_buffer[0] = '\0';
590 }
591 else
592 {
593 if (!statusmsg)
594 {
595 switch (status)
596 {
597 case 200: statusmsg = "OK"; break;
598 case 206: statusmsg = "Partial Content"; http_version = "1.1"; break;
599 case 400: statusmsg = "Bad Request"; break;
600 case 401: statusmsg = "Authentication Required"; break;
601 case 403: statusmsg = "Forbidden"; break;
602 case 404: statusmsg = "File Not Found"; break;
603 case 416: statusmsg = "Request Range Not Satisfiable"; break;
604 default: statusmsg = "(unknown status code)"; break;
605 }
606 }
607 snprintf (status_buffer, sizeof (status_buffer), "HTTP/%s %d %s\r\n", http_version, status, statusmsg);
608 }
609
610 if (contenttype)
611 {
612 if (charset)
613 snprintf (contenttype_buffer, sizeof (contenttype_buffer), "Content-Type: %s; charset=%s\r\n",
614 contenttype, charset);
615 else
616 snprintf (contenttype_buffer, sizeof (contenttype_buffer), "Content-Type: %s\r\n",
617 contenttype);
618 }
619 else
620 {
621 contenttype_buffer[0] = '\0';
622 }
623
624 time(&now);
625 #ifndef _WIN32
626 gmtime_result = gmtime_r(&now, &result);
627 #else
628 /* gmtime() on W32 breaks POSIX and IS thread-safe (uses TLS) */
629 gmtime_result = gmtime (&now);
630 if (gmtime_result)
631 memcpy (&result, gmtime_result, sizeof (result));
632 #endif
633
634 if (gmtime_result)
635 strftime(currenttime_buffer, sizeof(currenttime_buffer), "Date: %a, %d %b %Y %X GMT\r\n", gmtime_result);
636 else
637 currenttime_buffer[0] = '\0';
638
639 config = config_get_config();
640 extra_headers = _build_headers(status, config, source);
641 ret = snprintf (out, len, "%sServer: %s\r\nConnection: Close\r\n%s%s%s%s%s%s%s",
642 status_buffer,
643 config->server_id,
644 currenttime_buffer,
645 contenttype_buffer,
646 (status == 401 ? "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n" : ""),
647 (cache ? "" : "Cache-Control: no-cache, no-store\r\n"
648 "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
649 "Pragma: no-cache\r\n"),
650 extra_headers,
651 (datablock ? "\r\n" : ""),
652 (datablock ? datablock : ""));
653 free(extra_headers);
654 config_release_config();
655
656 return ret;
657 }
658
659
util_dict_new(void)660 util_dict *util_dict_new(void)
661 {
662 return (util_dict *)calloc(1, sizeof(util_dict));
663 }
664
util_dict_free(util_dict * dict)665 void util_dict_free(util_dict *dict)
666 {
667 util_dict *next;
668
669 while (dict) {
670 next = dict->next;
671
672 if (dict->key)
673 free (dict->key);
674 if (dict->val)
675 free (dict->val);
676 free (dict);
677
678 dict = next;
679 }
680 }
681
util_dict_get(util_dict * dict,const char * key)682 const char *util_dict_get(util_dict *dict, const char *key)
683 {
684 while (dict) {
685 if (!strcmp(key, dict->key))
686 return dict->val;
687 dict = dict->next;
688 }
689 return NULL;
690 }
691
util_dict_set(util_dict * dict,const char * key,const char * val)692 int util_dict_set(util_dict *dict, const char *key, const char *val)
693 {
694 util_dict *prev;
695
696 if (!dict || !key) {
697 ICECAST_LOG_ERROR("NULL values passed to util_dict_set()");
698 return 0;
699 }
700
701 prev = NULL;
702 while (dict) {
703 if (!dict->key || !strcmp(dict->key, key))
704 break;
705 prev = dict;
706 dict = dict->next;
707 }
708
709 if (!dict) {
710 dict = util_dict_new();
711 if (!dict) {
712 ICECAST_LOG_ERROR("unable to allocate new dictionary");
713 return 0;
714 }
715 if (prev)
716 prev->next = dict;
717 }
718
719 if (dict->key)
720 free (dict->val);
721 else if (!(dict->key = strdup(key))) {
722 if (prev)
723 prev->next = NULL;
724 util_dict_free (dict);
725
726 ICECAST_LOG_ERROR("unable to allocate new dictionary key");
727 return 0;
728 }
729
730 dict->val = strdup(val);
731 if (!dict->val) {
732 ICECAST_LOG_ERROR("unable to allocate new dictionary value");
733 return 0;
734 }
735
736 return 1;
737 }
738
739 /* given a dictionary, URL-encode each val and
740 stringify it in order as key=val&key=val... if val
741 is set, or just key&key if val is NULL.
742 TODO: Memory management needs overhaul. */
util_dict_urlencode(util_dict * dict,char delim)743 char *util_dict_urlencode(util_dict *dict, char delim)
744 {
745 char *res, *tmp;
746 char *enc;
747 int start = 1;
748
749 for (res = NULL; dict; dict = dict->next) {
750 /* encode key */
751 if (!dict->key)
752 continue;
753 if (start) {
754 if (!(res = malloc(strlen(dict->key) + 1))) {
755 return NULL;
756 }
757 sprintf(res, "%s", dict->key);
758 start = 0;
759 } else {
760 if (!(tmp = realloc(res, strlen(res) + strlen(dict->key) + 2))) {
761 free(res);
762 return NULL;
763 } else
764 res = tmp;
765 sprintf(res + strlen(res), "%c%s", delim, dict->key);
766 }
767
768 /* encode value */
769 if (!dict->val)
770 continue;
771 if (!(enc = util_url_escape(dict->val))) {
772 free(res);
773 return NULL;
774 }
775
776 if (!(tmp = realloc(res, strlen(res) + strlen(enc) + 2))) {
777 free(enc);
778 free(res);
779 return NULL;
780 } else
781 res = tmp;
782 sprintf(res + strlen(res), "=%s", enc);
783 free(enc);
784 }
785
786 return res;
787 }
788
789 #ifndef HAVE_LOCALTIME_R
localtime_r(const time_t * timep,struct tm * result)790 struct tm *localtime_r (const time_t *timep, struct tm *result)
791 {
792 static mutex_t localtime_lock;
793 static int initialised = 0;
794 struct tm *tm;
795
796 if (initialised == 0)
797 {
798 thread_mutex_create (&localtime_lock);
799 initialised = 1;
800 }
801 thread_mutex_lock (&localtime_lock);
802 tm = localtime (timep);
803 memcpy (result, tm, sizeof (*result));
804 thread_mutex_unlock (&localtime_lock);
805 return result;
806 }
807 #endif
808
809
810 /* helper function for converting a passed string in one character set to another
811 * we use libxml2 for this
812 */
util_conv_string(const char * string,const char * in_charset,const char * out_charset)813 char *util_conv_string (const char *string, const char *in_charset, const char *out_charset)
814 {
815 xmlCharEncodingHandlerPtr in, out;
816 char *ret = NULL;
817
818 if (string == NULL || in_charset == NULL || out_charset == NULL)
819 return NULL;
820
821 in = xmlFindCharEncodingHandler (in_charset);
822 out = xmlFindCharEncodingHandler (out_charset);
823
824 if (in && out)
825 {
826 xmlBufferPtr orig = xmlBufferCreate ();
827 xmlBufferPtr utf8 = xmlBufferCreate ();
828 xmlBufferPtr conv = xmlBufferCreate ();
829
830 ICECAST_LOG_INFO("converting metadata from %s to %s", in_charset, out_charset);
831 xmlBufferCCat (orig, string);
832 if (xmlCharEncInFunc (in, utf8, orig) > 0)
833 {
834 xmlCharEncOutFunc (out, conv, NULL);
835 if (xmlCharEncOutFunc (out, conv, utf8) >= 0)
836 ret = strdup ((const char *)xmlBufferContent (conv));
837 }
838 xmlBufferFree (orig);
839 xmlBufferFree (utf8);
840 xmlBufferFree (conv);
841 }
842 xmlCharEncCloseFunc (in);
843 xmlCharEncCloseFunc (out);
844
845 return ret;
846 }
847
848
get_line(FILE * file,char * buf,size_t siz)849 int get_line(FILE *file, char *buf, size_t siz)
850 {
851 if(fgets(buf, (int)siz, file)) {
852 size_t len = strlen(buf);
853 if(len > 0 && buf[len-1] == '\n') {
854 buf[--len] = 0;
855 if(len > 0 && buf[len-1] == '\r')
856 buf[--len] = 0;
857 }
858 return 1;
859 }
860 return 0;
861 }
862
863