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