util.c 22.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org, 
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
11
 * Copyright 2012-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
12
13
 */

14
15
16
17
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

Jack Moffitt's avatar
Jack Moffitt committed
18
#include <sys/types.h>
19
20
#include <stdio.h>
#include <string.h>
21
#include <stdlib.h>
Jack Moffitt's avatar
Jack Moffitt committed
22
23

#ifndef _WIN32
24
25
#include <sys/time.h>
#include <sys/socket.h>
Jack Moffitt's avatar
Jack Moffitt committed
26
#include <unistd.h>
27
28
29
#ifdef HAVE_POLL
#include <sys/poll.h>
#endif
30
#else
31
#include <winsock2.h>
32
#include <windows.h>
33
34
35
36
#include <stdio.h>
#define snprintf _snprintf
#define strcasecmp stricmp
#define strncasecmp strnicmp
Jack Moffitt's avatar
Jack Moffitt committed
37
38
#endif

Karl Heyes's avatar
Karl Heyes committed
39
#include "net/sock.h"
40
#include "thread/thread.h"
Jack Moffitt's avatar
Jack Moffitt committed
41

42
#include "cfgfile.h"
Jack Moffitt's avatar
Jack Moffitt committed
43
#include "util.h"
44
#include "compat.h"
45
46
47
#include "refbuf.h"
#include "connection.h"
#include "client.h"
48
#include "source.h"
49
50
51
52

#define CATMODULE "util"

#include "logging.h"
Jack Moffitt's avatar
Jack Moffitt committed
53

54
55
56
57
58
59
60
61
62
/* Abstract out an interface to use either poll or select depending on which
 * is available (poll is preferred) to watch a single fd.
 *
 * timeout is in milliseconds.
 *
 * returns > 0 if activity on the fd occurs before the timeout.
 *           0 if no activity occurs
 *         < 0 for error.
 */
63
int util_timed_wait_for_fd(sock_t fd, int timeout)
64
65
66
67
68
69
70
71
72
73
74
{
#ifdef HAVE_POLL
    struct pollfd ufds;

    ufds.fd = fd;
    ufds.events = POLLIN;
    ufds.revents = 0;

    return poll(&ufds, 1, timeout);
#else
    fd_set rfds;
75
    struct timeval tv, *p=NULL;
76
77
78
79

    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

80
81
82
83
84
85
    if(timeout >= 0) {
        tv.tv_sec = timeout/1000;
        tv.tv_usec = (timeout % 1000)*1000;
        p = &tv;
    }
    return select(fd+1, &rfds, NULL, NULL, p);
86
87
88
#endif
}

89
int util_read_header(sock_t sock, char *buff, unsigned long len, int entire)
Jack Moffitt's avatar
Jack Moffitt committed
90
{
91
92
93
94
    int read_bytes, ret;
    unsigned long pos;
    char c;
    ice_config_t *config;
Michael Smith's avatar
Michael Smith committed
95
    int header_timeout;
Jack Moffitt's avatar
Jack Moffitt committed
96

97
    config = config_get_config();
Michael Smith's avatar
Michael Smith committed
98
99
    header_timeout = config->header_timeout;
    config_release_config();
Jack Moffitt's avatar
Jack Moffitt committed
100

101
102
103
    read_bytes = 1;
    pos = 0;
    ret = 0;
Jack Moffitt's avatar
Jack Moffitt committed
104

105
106
    while ((read_bytes == 1) && (pos < (len - 1))) {
        read_bytes = 0;
Jack Moffitt's avatar
Jack Moffitt committed
107

Michael Smith's avatar
Michael Smith committed
108
        if (util_timed_wait_for_fd(sock, header_timeout*1000) > 0) {
Jack Moffitt's avatar
Jack Moffitt committed
109

110
111
            if ((read_bytes = recv(sock, &c, 1, 0))) {
                if (c != '\r') buff[pos++] = c;
112
113
114
115
116
117
118
119
120
121
122
123
                if (entire) {
                    if ((pos > 1) && (buff[pos - 1] == '\n' && 
                                      buff[pos - 2] == '\n')) {
                        ret = 1;
                        break;
                    }
                }
                else {
                    if ((pos > 1) && (buff[pos - 1] == '\n')) {
                        ret = 1;
                        break;
                    }
124
125
126
127
128
129
130
131
132
133
                }
            }
        } else {
            break;
        }
    }

    if (ret) buff[pos] = '\0';
    
    return ret;
Jack Moffitt's avatar
Jack Moffitt committed
134
135
}

136
char *util_get_extension(const char *path) {
137
138
139
140
141
142
143
144
    char *ext = strrchr(path, '.');

    if(ext == NULL)
        return "";
    else
        return ext+1;
}

145
int util_check_valid_extension(const char *uri) {
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
    int    ret = 0;
    char    *p2;

    if (uri) {
        p2 = strrchr(uri, '.');
        if (p2) {
            p2++;
            if (strncmp(p2, "xsl", strlen("xsl")) == 0) {
                /* Build the full path for the request, concatenating the webroot from the config.
                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
                */
                ret = XSLT_CONTENT;
            }
            if (strncmp(p2, "htm", strlen("htm")) == 0) {
                /* Build the full path for the request, concatenating the webroot from the config.
                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
                */
                ret = HTML_CONTENT;
            }
            if (strncmp(p2, "html", strlen("html")) == 0) {
                /* Build the full path for the request, concatenating the webroot from the config.
                ** Here would be also a good time to prevent accesses like '../../../../etc/passwd' or somesuch.
                */
                ret = HTML_CONTENT;
            }

        }
    }
    return ret;
175
}
Jack Moffitt's avatar
Jack Moffitt committed
176

177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
static int hex(char c)
{
    if(c >= '0' && c <= '9')
        return c - '0';
    else if(c >= 'A' && c <= 'F')
        return c - 'A' + 10;
    else if(c >= 'a' && c <= 'f')
        return c - 'a' + 10;
    else
        return -1;
}

static int verify_path(char *path) {
    int dir = 0, indotseq = 0;

    while(*path) {
        if(*path == '/' || *path == '\\') {
            if(indotseq)
                return 0;
            if(dir)
                return 0;
            dir = 1;
            path++;
            continue;
        }

        if(dir || indotseq) {
            if(*path == '.')
                indotseq = 1;
            else
                indotseq = 0;
        }
        
        dir = 0;
        path++;
    }

    return 1;
}

217
218
219
220
221
222
223
224
225
226
227
228
229
char *util_get_path_from_uri(char *uri) {
    char *path = util_normalise_uri(uri);
    char *fullpath;

    if(!path)
        return NULL;
    else {
        fullpath = util_get_path_from_normalised_uri(path);
        free(path);
        return fullpath;
    }
}

230
char *util_get_path_from_normalised_uri(const char *uri) {
231
    char *fullpath;
Michael Smith's avatar
Michael Smith committed
232
233
    char *webroot;
    ice_config_t *config = config_get_config();
234

Michael Smith's avatar
Michael Smith committed
235
236
237
    webroot = config->webroot_dir;

    fullpath = malloc(strlen(uri) + strlen(webroot) + 1);
238
239
240
    if (fullpath)
        sprintf (fullpath, "%s%s", webroot, uri);
    config_release_config();
241
242
243
244

    return fullpath;
}

245
static char hexchars[16] = {
Michael Smith's avatar
Michael Smith committed
246
    '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
};

static char safechars[256] = {
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0,
      0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,
      0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
};

268
char *util_url_escape (const char *src)
269
270
271
272
{
    int len = strlen(src);
    /* Efficiency not a big concern here, keep the code simple/conservative */
    char *dst = calloc(1, len*3 + 1); 
273
    unsigned char *source = (unsigned char *)src;
274
275
276
277
278
279
280
281
    int i,j=0;

    for(i=0; i < len; i++) {
        if(safechars[source[i]]) {
            dst[j++] = source[i];
        }
        else {
            dst[j] = '%';
282
283
            dst[j+1] = hexchars[ (source[i] >> 4) & 0xf ];
            dst[j+2] = hexchars[ source[i] & 0xf ];
284
285
286
287
288
289
290
291
            j+= 3;
        }
    }

    dst[j] = 0;
    return dst;
}

292
char *util_url_unescape (const char *src)
293
294
{
    int len = strlen(src);
295
    char *decoded;
296
    int i;
297
    char *dst;
298
299
    int done = 0;

300
    decoded = calloc(1, len + 1);
301

302
    dst = decoded;
303

304
305
    for(i=0; i < len; i++) {
        switch(src[i]) {
306
            case '%':
307
308
                if(i+2 >= len) {
                    free(decoded);
309
310
                    return NULL;
                }
311
312
                if(hex(src[i+1]) == -1 || hex(src[i+2]) == -1 ) {
                    free(decoded);
313
314
315
                    return NULL;
                }

316
                *dst++ = hex(src[i+1]) * 16  + hex(src[i+2]);
317
318
319
320
321
322
                i+= 2;
                break;
            case '#':
                done = 1;
                break;
            case 0:
323
                ICECAST_LOG_ERROR("Fatal internal logic error in util_url_unescape()");
324
                free(decoded);
325
326
327
                return NULL;
                break;
            default:
328
                *dst++ = src[i];
329
330
331
332
333
334
335
336
                break;
        }
        if(done)
            break;
    }

    *dst = 0; /* null terminator */

337
338
339
340
341
342
343
344
    return decoded;
}

/* Get an absolute path (from the webroot dir) from a URI. Return NULL if the
 * path contains 'disallowed' sequences like foo/../ (which could be used to
 * escape from the webroot) or if it cannot be URI-decoded.
 * Caller should free the path.
 */
345
char *util_normalise_uri(const char *uri) {
346
347
348
349
350
    char *path;

    if(uri[0] != '/')
        return NULL;

351
    path = util_url_unescape(uri);
352
353

    if(path == NULL) {
354
        ICECAST_LOG_WARN("Error decoding URI: %s\n", uri);
355
356
357
        return NULL;
    }

358
359
    /* We now have a full URI-decoded path. Check it for allowability */
    if(verify_path(path))
360
        return path;
361
    else {
362
        ICECAST_LOG_WARN("Rejecting invalid path \"%s\"", path);
363
364
365
366
        free(path);
        return NULL;
    }
}
Jack Moffitt's avatar
Jack Moffitt committed
367

368
369
370
371
372
373
374
static char base64table[64] = {
    'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
    'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
    'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
    'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};

375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
static signed char base64decode[256] = {
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -1, -2, -2,
     -2,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
     -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
};

Michael Smith's avatar
Michael Smith committed
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
char *util_bin_to_hex(unsigned char *data, int len)
{
    char *hex = malloc(len*2 + 1);
    int i;

    for(i = 0; i < len; i++) {
        hex[i*2] = hexchars[(data[i]&0xf0) >> 4];
        hex[i*2+1] = hexchars[data[i]&0x0f];
    }

    hex[len*2] = 0;

    return hex;
}

409
/* This isn't efficient, but it doesn't need to be */
410
char *util_base64_encode(const char *data)
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
{
    int len = strlen(data);
    char *out = malloc(len*4/3 + 4);
    char *result = out;
    int chunk;

    while(len > 0) {
        chunk = (len >3)?3:len;
        *out++ = base64table[(*data & 0xFC)>>2];
        *out++ = base64table[((*data & 0x03)<<4) | ((*(data+1) & 0xF0) >> 4)];
        switch(chunk) {
            case 3:
                *out++ = base64table[((*(data+1) & 0x0F)<<2) | ((*(data+2) & 0xC0)>>6)];
                *out++ = base64table[(*(data+2)) & 0x3F];
                break;
            case 2:
                *out++ = base64table[((*(data+1) & 0x0F)<<2)];
                *out++ = '=';
                break;
            case 1:
                *out++ = '=';
                *out++ = '=';
                break;
        }
        data += chunk;
        len -= chunk;
    }
438
    *out = 0;
439
440
441
442

    return result;
}

443
char *util_base64_decode(const char *data)
444
{
445
446
    const unsigned char *input = (const unsigned char *)data;
    int len = strlen (data);
447
448
449
450
451
452
453
454
455
456
457
    char *out = malloc(len*3/4 + 5);
    char *result = out;
    signed char vals[4];

    while(len > 0) {
        if(len < 4)
        {
            free(result);
            return NULL; /* Invalid Base64 data */
        }

458
459
460
461
        vals[0] = base64decode[*input++];
        vals[1] = base64decode[*input++];
        vals[2] = base64decode[*input++];
        vals[3] = base64decode[*input++];
462
463

        if(vals[0] < 0 || vals[1] < 0 || vals[2] < -1 || vals[3] < -1) {
464
            len -= 4;
465
466
467
468
            continue;
        }

        *out++ = vals[0]<<2 | vals[1]>>4;
469
470
471
472
        /* vals[3] and (if that is) vals[2] can be '=' as padding, which is
           looked up in the base64decode table as '-1'. Check for this case,
           and output zero-terminators instead of characters if we've got
           padding. */
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
        if(vals[2] >= 0)
            *out++ = ((vals[1]&0x0F)<<4) | (vals[2]>>2);
        else
            *out++ = 0;

        if(vals[3] >= 0)
            *out++ = ((vals[2]&0x03)<<6) | (vals[3]);
        else
            *out++ = 0;

        len -= 4;
    }
    *out = 0;

    return result;
}

490
/* TODO, FIXME: handle memory allocation errors better. */
491
static inline void   _build_headers_loop(char **ret, size_t *len, ice_config_http_header_t *header, int status) {
492
493
494
    size_t headerlen;
    const char *name;
    const char *value;
495
    char * r = *ret;
496

497
498
499
500
501
502
503
504
    if (!header)
        return;

    do {
        /* filter out header's we don't use. */
        if (header->status != 0 && header->status != status) continue;

        /* get the name of the header */
505
        name = header->name;
506
507

        /* handle type of the header */
508
        value = NULL;
509
510
511
512
513
        switch (header->type) {
            case HTTP_HEADER_TYPE_STATIC:
                value = header->value;
                break;
        }
514

515
516
517
518
        /* check data */
        if (!name || !value)
            continue;

519
        /* append the header to the buffer */
520
        headerlen = strlen(name) + strlen(value) + 4;
521
522
523
524
525
526
        *len += headerlen;
        r = realloc(r, *len);
        strcat(r, name);
        strcat(r, ": ");
        strcat(r, value);
        strcat(r, "\r\n");
527
    } while ((header = header->next));
528
529
530
531
532
533
534
535
536
537
538
539
540
    *ret = r;
}
static inline char * _build_headers(int status, ice_config_t *config, source_t *source) {
    mount_proxy *mountproxy = NULL;
    char *ret = NULL;
    size_t len = 1;

    if (source)
        mountproxy = config_find_mount(config, source->mount, MOUNT_TYPE_NORMAL);

    ret = calloc(1, 1);
    *ret = 0;

541
    _build_headers_loop(&ret, &len, config->http_headers, status);
542
    if (mountproxy && mountproxy->http_headers)
543
        _build_headers_loop(&ret, &len, mountproxy->http_headers, status);
544

545
546
547
    return ret;
}

548
549
550
551
ssize_t 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,
552
553
        const char * datablock,
        struct source_tag * source) {
554
555
556
557
    const char * http_version = "1.0";
    ice_config_t *config;
    time_t now;
    struct tm result;
558
559
    struct tm *gmtime_result;
    char currenttime_buffer[80];
560
561
562
    char status_buffer[80];
    char contenttype_buffer[80];
    ssize_t ret;
563
    char * extra_headers;
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611

    if (!out)
        return -1;

    if (offset == -1)
        offset = strlen (out);

    out += offset;
    len -= offset;

    if (status == -1)
    {
        status_buffer[0] = '\0';
    }
    else
    {
        if (!statusmsg)
	{
	    switch (status)
	    {
	        case 200: statusmsg = "OK"; break;
		case 206: statusmsg = "Partial Content"; http_version = "1.1"; break;
		case 400: statusmsg = "Bad Request"; break;
		case 401: statusmsg = "Authentication Required"; break;
		case 403: statusmsg = "Forbidden"; break;
		case 404: statusmsg = "File Not Found"; break;
		case 416: statusmsg = "Request Range Not Satisfiable"; break;
		default:  statusmsg = "(unknown status code)"; break;
	    }
	}
	snprintf (status_buffer, sizeof (status_buffer), "HTTP/%s %d %s\r\n", http_version, status, statusmsg);
    }

    if (contenttype)
    {
    	if (charset)
            snprintf (contenttype_buffer, sizeof (contenttype_buffer), "Content-Type: %s; charset=%s\r\n",
	                                                               contenttype, charset);
	else
            snprintf (contenttype_buffer, sizeof (contenttype_buffer), "Content-Type: %s\r\n",
                                                                       contenttype);
    }
    else
    {
        contenttype_buffer[0] = '\0';
    }

    time(&now);
612
613
614
615
616
617
618
619
620
621
#ifndef _WIN32
    gmtime_result = gmtime_r(&now, &result);
#else
    /* gmtime() on W32 breaks POSIX and IS thread-safe (uses TLS) */
    gmtime_result = gmtime (&now);
    if (gmtime_result)
        memcpy (&result, gmtime_result, sizeof (result));
#endif

    if (gmtime_result)
622
        strftime(currenttime_buffer, sizeof(currenttime_buffer), "Date: %a, %d %b %Y %X GMT\r\n", gmtime_result);
623
624
    else
        currenttime_buffer[0] = '\0';
625
626

    config = config_get_config();
627
    extra_headers = _build_headers(status, config, source);
628
    ret = snprintf (out, len, "%sServer: %s\r\n%s%s%s%s%s%s%s",
629
630
631
632
633
                              status_buffer,
			      config->server_id,
			      currenttime_buffer,
			      contenttype_buffer,
			      (status == 401 ? "WWW-Authenticate: Basic realm=\"Icecast2 Server\"\r\n" : ""),
634
635
636
                              (cache     ? "" : "Cache-Control: no-cache\r\n"
                                                "Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n"
                                                "Pragma: no-cache\r\n"),
637
                              extra_headers,
638
639
                              (datablock ? "\r\n" : ""),
                              (datablock ? datablock : ""));
640
    free(extra_headers);
641
642
643
644
645
646
    config_release_config();

    return ret;
}


647
648
util_dict *util_dict_new(void)
{
649
    return (util_dict *)calloc(1, sizeof(util_dict));
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
}

void util_dict_free(util_dict *dict)
{
    util_dict *next;

    while (dict) {
        next = dict->next;

        if (dict->key)
            free (dict->key);
        if (dict->val)
            free (dict->val);
        free (dict);

        dict = next;
    }
}

const char *util_dict_get(util_dict *dict, const char *key)
{
671
672
673
674
675
    while (dict) {
        if (!strcmp(key, dict->key))
            return dict->val;
        dict = dict->next;
    }
Michael Smith's avatar
Michael Smith committed
676
    return NULL;
677
678
679
680
}

int util_dict_set(util_dict *dict, const char *key, const char *val)
{
681
    util_dict *prev;
682

683
    if (!dict || !key) {
684
        ICECAST_LOG_ERROR("NULL values passed to util_dict_set()");
685
686
687
        return 0;
    }

688
689
690
691
692
693
694
695
696
697
698
    prev = NULL;
    while (dict) {
        if (!dict->key || !strcmp(dict->key, key))
            break;
        prev = dict;
        dict = dict->next;
    }

    if (!dict) {
        dict = util_dict_new();
        if (!dict) {
699
            ICECAST_LOG_ERROR("unable to allocate new dictionary");
700
            return 0;
701
        }
702
703
704
        if (prev)
            prev->next = dict;
    }
705

706
707
708
709
710
711
    if (dict->key)
        free (dict->val);
    else if (!(dict->key = strdup(key))) {
        if (prev)
            prev->next = NULL;
        util_dict_free (dict);
712

713
        ICECAST_LOG_ERROR("unable to allocate new dictionary key");
714
        return 0;
715
    }
716

717
718
    dict->val = strdup(val);
    if (!dict->val) {
719
        ICECAST_LOG_ERROR("unable to allocate new dictionary value");
720
        return 0;
721
    }
722

723
    return 1;
724
725
}

726
727
/* given a dictionary, URL-encode each val and 
   stringify it in order as key=val&key=val... if val 
728
729
730
731
   is set, or just key&key if val is NULL.
  TODO: Memory management needs overhaul. */
char *util_dict_urlencode(util_dict *dict, char delim)
{
732
733
734
735
736
737
738
739
740
    char *res, *tmp;
    char *enc;
    int start = 1;

    for (res = NULL; dict; dict = dict->next) {
        /* encode key */
        if (!dict->key)
            continue;
        if (start) {
741
            if (!(res = malloc(strlen(dict->key) + 1))) {
742
743
                return NULL;
            }
744
            sprintf(res, "%s", dict->key);
745
746
            start = 0;
        } else {
747
            if (!(tmp = realloc(res, strlen(res) + strlen(dict->key) + 2))) {
748
749
750
751
                free(res);
                return NULL;
            } else
                res = tmp;
752
            sprintf(res + strlen(res), "%c%s", delim, dict->key);
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
        }

        /* encode value */
        if (!dict->val)
            continue;
        if (!(enc = util_url_escape(dict->val))) {
            free(res);
            return NULL;
        }

        if (!(tmp = realloc(res, strlen(res) + strlen(enc) + 2))) {
            free(enc);
            free(res);
            return NULL;
        } else
            res = tmp;
        sprintf(res + strlen(res), "=%s", enc);
        free(enc);
    }

    return res;
774
}
Michael Smith's avatar
Michael Smith committed
775

776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
#ifndef HAVE_LOCALTIME_R
struct tm *localtime_r (const time_t *timep, struct tm *result)
{
     static mutex_t localtime_lock;
     static int initialised = 0;
     struct tm *tm;

     if (initialised == 0)
     {
         thread_mutex_create (&localtime_lock);
         initialised = 1;
     }
     thread_mutex_lock (&localtime_lock);
     tm = localtime (timep);
     memcpy (result, tm, sizeof (*result));
     thread_mutex_unlock (&localtime_lock);
     return result;
}
#endif
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816


/* helper function for converting a passed string in one character set to another
 * we use libxml2 for this
 */
char *util_conv_string (const char *string, const char *in_charset, const char *out_charset)
{
    xmlCharEncodingHandlerPtr in, out;
    char *ret = NULL;

    if (string == NULL || in_charset == NULL || out_charset == NULL)
        return NULL;

    in  = xmlFindCharEncodingHandler (in_charset);
    out = xmlFindCharEncodingHandler (out_charset);

    if (in && out)
    {
        xmlBufferPtr orig = xmlBufferCreate ();
        xmlBufferPtr utf8 = xmlBufferCreate ();
        xmlBufferPtr conv = xmlBufferCreate ();

817
        ICECAST_LOG_INFO("converting metadata from %s to %s", in_charset, out_charset);
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
        xmlBufferCCat (orig, string);
        if (xmlCharEncInFunc (in, utf8, orig) > 0)
        {
            xmlCharEncOutFunc (out, conv, NULL);
            if (xmlCharEncOutFunc (out, conv, utf8) >= 0)
                ret = strdup ((const char *)xmlBufferContent (conv));
        }
        xmlBufferFree (orig);
        xmlBufferFree (utf8);
        xmlBufferFree (conv);
    }
    xmlCharEncCloseFunc (in);
    xmlCharEncCloseFunc (out);

    return ret;
}

835
836
837
838
839
840
841
842
843
844
845
846
847
848
849

int get_line(FILE *file, char *buf, size_t siz)
{
    if(fgets(buf, (int)siz, file)) {
        size_t len = strlen(buf);
        if(len > 0 && buf[len-1] == '\n') {
            buf[--len] = 0;
            if(len > 0 && buf[len-1] == '\r')
                buf[--len] = 0;
        }
        return 1;
    }
    return 0;
}