cfgfile.c 93.5 KB
Newer Older
1
2
3
4
5
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
6
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7
8
9
10
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
11
12
 * Copyright 2011,      Dave 'justdave' Miller <justdave@mozilla.com>.
 * Copyright 2011-2014, Thomas B. "dm8tbr" Ruecker <thomas@ruecker.fi>,
13
 * Copyright 2011-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
14
15
 */

16
17
18
19
20
21
22
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
23
#ifndef _WIN32
24
#include <fnmatch.h>
25
#endif
26
27
28
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

Marvin Scholz's avatar
Marvin Scholz committed
29
#include "common/thread/thread.h"
30
31
32
#include "cfgfile.h"
#include "refbuf.h"
#include "client.h"
33
34
35
36
#include "logging.h"
#include "util.h"
#include "auth.h"
#include "event.h"
37
#include "tls.h"
38

39
40
41
42
/* for config_reread_config() */
#include "yp.h"
#include "fserve.h"
#include "stats.h"
43
#include "connection.h"
44

Marvin Scholz's avatar
Marvin Scholz committed
45
46
47
48
49
#define CATMODULE                       "CONFIG"
#define CONFIG_DEFAULT_LOCATION         "Earth"
#define CONFIG_DEFAULT_ADMIN            "icemaster@localhost"
#define CONFIG_DEFAULT_CLIENT_LIMIT     256
#define CONFIG_DEFAULT_SOURCE_LIMIT     16
50
#define CONFIG_DEFAULT_QUEUE_SIZE_LIMIT (500*1024)
Marvin Scholz's avatar
Marvin Scholz committed
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#define CONFIG_DEFAULT_BURST_SIZE       (64*1024)
#define CONFIG_DEFAULT_THREADPOOL_SIZE  4
#define CONFIG_DEFAULT_CLIENT_TIMEOUT   30
#define CONFIG_DEFAULT_HEADER_TIMEOUT   15
#define CONFIG_DEFAULT_SOURCE_TIMEOUT   10
#define CONFIG_DEFAULT_MASTER_USERNAME  "relay"
#define CONFIG_DEFAULT_SHOUTCAST_MOUNT  "/stream"
#define CONFIG_DEFAULT_FILESERVE        1
#define CONFIG_DEFAULT_TOUCH_FREQ       5
#define CONFIG_DEFAULT_HOSTNAME         "localhost"
#define CONFIG_DEFAULT_PLAYLIST_LOG     NULL
#define CONFIG_DEFAULT_ACCESS_LOG       "access.log"
#define CONFIG_DEFAULT_ERROR_LOG        "error.log"
#define CONFIG_DEFAULT_LOG_LEVEL        ICECAST_LOGLEVEL_INFO
#define CONFIG_DEFAULT_CHROOT           0
#define CONFIG_DEFAULT_CHUID            0
#define CONFIG_DEFAULT_USER             NULL
#define CONFIG_DEFAULT_GROUP            NULL
#define CONFIG_MASTER_UPDATE_INTERVAL   120
#define CONFIG_YP_URL_TIMEOUT           10
#define CONFIG_DEFAULT_CIPHER_LIST      "ECDHE-RSA-AES128-GCM-SHA256:"\
                                        "ECDHE-ECDSA-AES128-GCM-SHA256:"\
                                        "ECDHE-RSA-AES256-GCM-SHA384:"\
                                        "ECDHE-ECDSA-AES256-GCM-SHA384:"\
                                        "DHE-RSA-AES128-GCM-SHA256:"\
                                        "DHE-DSS-AES128-GCM-SHA256:"\
                                        "kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:"\
                                        "ECDHE-ECDSA-AES128-SHA256:"\
                                        "ECDHE-RSA-AES128-SHA:"\
                                        "ECDHE-ECDSA-AES128-SHA:"\
                                        "ECDHE-RSA-AES256-SHA384:"\
                                        "ECDHE-ECDSA-AES256-SHA384:"\
                                        "ECDHE-RSA-AES256-SHA:"\
                                        "ECDHE-ECDSA-AES256-SHA:"\
                                        "DHE-RSA-AES128-SHA256:"\
                                        "DHE-RSA-AES128-SHA:"\
                                        "DHE-DSS-AES128-SHA256:"\
                                        "DHE-RSA-AES256-SHA256:"\
                                        "DHE-DSS-AES256-SHA:"\
                                        "DHE-RSA-AES256-SHA:"\
                                        "ECDHE-RSA-DES-CBC3-SHA:"\
                                        "ECDHE-ECDSA-DES-CBC3-SHA:"\
                                        "AES128-GCM-SHA256:AES256-GCM-SHA384:"\
                                        "AES128-SHA256:AES256-SHA256:"\
                                        "AES128-SHA:AES256-SHA:AES:"\
                                        "DES-CBC3-SHA:HIGH:!aNULL:!eNULL:"\
                                        "!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:"\
                                        "!EDH-DSS-DES-CBC3-SHA:"\
                                        "!EDH-RSA-DES-CBC3-SHA:"\
                                        "!KRB5-DES-CBC3-SHA"
101
102

#ifndef _WIN32
Marvin Scholz's avatar
Marvin Scholz committed
103
104
105
106
107
108
#define CONFIG_DEFAULT_BASE_DIR         "/usr/local/icecast"
#define CONFIG_DEFAULT_LOG_DIR          "/usr/local/icecast/logs"
#define CONFIG_DEFAULT_WEBROOT_DIR      "/usr/local/icecast/webroot"
#define CONFIG_DEFAULT_ADMINROOT_DIR    "/usr/local/icecast/admin"
#define CONFIG_DEFAULT_NULL_FILE        "/dev/null"
#define MIMETYPESFILE                   "/etc/mime.types"
109
#else
Marvin Scholz's avatar
Marvin Scholz committed
110
111
112
113
114
115
#define CONFIG_DEFAULT_BASE_DIR         ".\\"
#define CONFIG_DEFAULT_LOG_DIR          ".\\logs"
#define CONFIG_DEFAULT_WEBROOT_DIR      ".\\webroot"
#define CONFIG_DEFAULT_ADMINROOT_DIR    ".\\admin"
#define CONFIG_DEFAULT_NULL_FILE        "nul:"
#define MIMETYPESFILE                   ".\\mime.types"
116
117
118
119
120
121
122
123
124
125
126
127
#endif

static ice_config_t _current_configuration;
static ice_config_locks _locks;

static void _set_defaults(ice_config_t *c);
static void _parse_root(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_limits(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_directory(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_paths(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_logging(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_security(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
Marvin Scholz's avatar
Marvin Scholz committed
128
129
130
131
132
133
134
135
136
137

static void _parse_authentication(xmlDocPtr                 doc,
                                  xmlNodePtr                node,
                                  ice_config_t             *c,
                                  char                    **source_password);

static void _parse_http_headers(xmlDocPtr                   doc,
                                xmlNodePtr                  node,
                                ice_config_http_header_t  **http_headers);

138
139
static void _parse_relay(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
Marvin Scholz's avatar
Marvin Scholz committed
140
141
142
143
144

static void _parse_listen_socket(xmlDocPtr                  doc,
                                 xmlNodePtr                 node,
                                 ice_config_t              *c);

145
static void _add_server(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
146
static void _parse_events(event_registration_t **events, xmlNodePtr node);
147

148
149
150
static void merge_mounts(mount_proxy * dst, mount_proxy * src);
static inline void _merge_mounts_all(ice_config_t *c);

Marvin Scholz's avatar
Marvin Scholz committed
151
152
operation_mode config_str_to_omode(const char *str)
{
153
154
155
156
157
158
159
160
    if (!str || !*str)
        return OMODE_DEFAULT;
    if (strcasecmp(str, "default") == 0) {
        return OMODE_DEFAULT;
    } else if (strcasecmp(str, "normal") == 0) {
        return OMODE_NORMAL;
    } else if (strcasecmp(str, "legacy-compat") == 0 || strcasecmp(str, "legacy") == 0) {
        return OMODE_LEGACY;
161
162
    } else if (strcasecmp(str, "strict") == 0) {
        return OMODE_STRICT;
163
164
165
166
167
168
    } else {
        ICECAST_LOG_ERROR("Unknown operation mode \"%s\", falling back to DEFAULT.", str);
        return OMODE_DEFAULT;
    }
}

Marvin Scholz's avatar
Marvin Scholz committed
169
170
static void create_locks(void)
{
171
    thread_mutex_create(&_locks.relay_lock);
172
    thread_rwlock_create(&_locks.config_lock);
173
174
}

Marvin Scholz's avatar
Marvin Scholz committed
175
176
static void release_locks(void)
{
177
    thread_mutex_destroy(&_locks.relay_lock);
178
    thread_rwlock_destroy(&_locks.config_lock);
179
180
}

Marvin Scholz's avatar
Marvin Scholz committed
181
182
void config_initialize(void)
{
183
184
185
    create_locks();
}

Marvin Scholz's avatar
Marvin Scholz committed
186
187
void config_shutdown(void)
{
188
189
190
191
192
193
194
195
196
197
198
199
    config_get_config();
    config_clear(&_current_configuration);
    config_release_config();
    release_locks();
}

void config_init_configuration(ice_config_t *configuration)
{
    memset(configuration, 0, sizeof(ice_config_t));
    _set_defaults(configuration);
}

200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
static inline void __read_int(xmlDocPtr doc, xmlNodePtr node, int *val, const char *warning)
{
    char *str = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
    if (!str || !*str) {
        ICECAST_LOG_WARN("%s", warning);
    } else {
        *val = util_str_to_int(str, *val);
    }
    if (str)
        xmlFree(str);
}

static inline void __read_unsigned_int(xmlDocPtr doc, xmlNodePtr node, unsigned int *val, const char *warning)
{
    char *str = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
    if (!str || !*str) {
        ICECAST_LOG_WARN("%s", warning);
    } else {
        *val = util_str_to_unsigned_int(str, *val);
    }
    if (str)
        xmlFree(str);
}         

Marvin Scholz's avatar
Marvin Scholz committed
224
225
static inline int __parse_public(const char *str)
{
226
227
228
229
230
231
232
233
234
235
236
237
    /* values that are not bool */
    if (strcasecmp(str, "client") == 0)
        return -1;

    /* old way of doing so */
    if (strcmp(str, "-1") == 0)
        return -1;

    /* ok, only normal bool left! */
    return util_str_to_bool(str);
}

238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
/* This converts TLS mode strings to (tlsmode_t).
 * In older versions of Icecast2 this was just a bool.
 * So we need to handle boolean values as well.
 * See also: util_str_to_bool().
 */
static tlsmode_t str_to_tlsmode(const char *str) {
    /* consider NULL and empty strings as auto mode */
    if (!str || !*str)
        return ICECAST_TLSMODE_AUTO;

    if (strcasecmp(str, "disabled") == 0) {
        return ICECAST_TLSMODE_DISABLED;
    } else if (strcasecmp(str, "auto") == 0) {
        return ICECAST_TLSMODE_AUTO;
    } else if (strcasecmp(str, "auto_no_plain") == 0) {
        return ICECAST_TLSMODE_AUTO_NO_PLAIN;
    } else if (strcasecmp(str, "rfc2817") == 0) {
        return ICECAST_TLSMODE_RFC2817;
    } else if (strcasecmp(str, "rfc2818") == 0 ||
               /* boolean-style values */
               strcasecmp(str, "true") == 0 ||
               strcasecmp(str, "yes")  == 0 ||
               strcasecmp(str, "on")   == 0 ) {
        return ICECAST_TLSMODE_RFC2818;
    }

    /* old style numbers: consider everyting non-zero RFC2818 */
    if (atoi(str))
        return ICECAST_TLSMODE_RFC2818;

    /* we default to auto mode */
    return ICECAST_TLSMODE_AUTO;
}

272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/* This checks for the TLS implementation of a node */
static int __check_node_impl(xmlNodePtr node, const char *def)
{
    char *impl;
    int res;

    impl = (char *)xmlGetProp(node, XMLSTR("implementation"));
    if (!impl)
        impl = (char *)xmlGetProp(node, XMLSTR("impl"));
    if (!impl)
        impl = (char *)xmlStrdup(XMLSTR(def));

    res = tls_check_impl(impl);

    xmlFree(impl);

    return res;
}


Marvin Scholz's avatar
Marvin Scholz committed
292
293
294
295
296
297
298
299
300
301
302
303
304
305
static void __append_old_style_auth(auth_stack_t       **stack,
                                    const char          *name,
                                    const char          *type,
                                    const char          *username,
                                    const char          *password,
                                    const char          *method,
                                    const char          *allow_method,
                                    int                  allow_web,
                                    const char          *allow_admin)
{
    xmlNodePtr  role,
                user,
                pass;
    auth_t     *auth;
Philipp Schafft's avatar
Philipp Schafft committed
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354

    if (!type)
        return;

    role = xmlNewNode(NULL, XMLSTR("role"));

    xmlSetProp(role, XMLSTR("type"), XMLSTR(type));
    xmlSetProp(role, XMLSTR("deny-method"), XMLSTR("*"));
    if (allow_method)
        xmlSetProp(role, XMLSTR("allow-method"), XMLSTR(allow_method));

    if (name)
        xmlSetProp(role, XMLSTR("name"), XMLSTR(name));

    if (method)
        xmlSetProp(role, XMLSTR("method"), XMLSTR(method));

    if (allow_web) {
        xmlSetProp(role, XMLSTR("allow-web"), XMLSTR("*"));
    } else {
        xmlSetProp(role, XMLSTR("deny-web"), XMLSTR("*"));
    }

    if (allow_admin && strcmp(allow_admin, "*") == 0) {
        xmlSetProp(role, XMLSTR("allow-admin"), XMLSTR("*"));
    } else {
        xmlSetProp(role, XMLSTR("deny-admin"), XMLSTR("*"));
        if (allow_admin)
            xmlSetProp(role, XMLSTR("allow-admin"), XMLSTR(allow_admin));
    }

    if (username) {
        user = xmlNewChild(role, NULL, XMLSTR("option"), NULL);
        xmlSetProp(user, XMLSTR("name"), XMLSTR("username"));
        xmlSetProp(user, XMLSTR("value"), XMLSTR(username));
    }
    if (password) {
        pass = xmlNewChild(role, NULL, XMLSTR("option"), NULL);
        xmlSetProp(pass, XMLSTR("name"), XMLSTR("password"));
        xmlSetProp(pass, XMLSTR("value"), XMLSTR(password));
    }

    auth = auth_get_authenticator(role);
    auth_stack_push(stack, auth);
    auth_release(auth);

    xmlFreeNode(role);
}

Marvin Scholz's avatar
Marvin Scholz committed
355
356
357
358
static void __append_option_tag(xmlNodePtr  parent,
                                const char *name,
                                const char *value)
{
Philipp Schafft's avatar
Philipp Schafft committed
359
360
361
362
363
364
365
366
367
368
    xmlNodePtr node;

    if (!name || !value)
        return;

    node = xmlNewChild(parent, NULL, XMLSTR("option"), NULL);
    xmlSetProp(node, XMLSTR("name"), XMLSTR(name));
    xmlSetProp(node, XMLSTR("value"), XMLSTR(value));
}

Marvin Scholz's avatar
Marvin Scholz committed
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
static void __append_old_style_urlauth(auth_stack_t **stack,
                                       const char    *client_add,
                                       const char    *client_remove,
                                       const char    *action_add,
                                       const char    *action_remove,
                                       const char    *username,
                                       const char    *password,
                                       int            is_source,
                                       const char    *auth_header,
                                       const char    *timelimit_header,
                                       const char    *headers,
                                       const char    *header_prefix)
{
    xmlNodePtr   role;
    auth_t      *auth;
Philipp Schafft's avatar
Philipp Schafft committed
384

385
    if (!stack || (!client_add && !client_remove))
Philipp Schafft's avatar
Philipp Schafft committed
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
        return;

    role = xmlNewNode(NULL, XMLSTR("role"));

    xmlSetProp(role, XMLSTR("type"), XMLSTR("url"));

    if (is_source) {
        xmlSetProp(role, XMLSTR("method"), XMLSTR("source,put"));
        xmlSetProp(role, XMLSTR("deny-method"), XMLSTR("*"));
        xmlSetProp(role, XMLSTR("allow-method"), XMLSTR("source,put"));
        xmlSetProp(role, XMLSTR("allow-web"), XMLSTR("*"));
        xmlSetProp(role, XMLSTR("allow-admin"), XMLSTR("*"));
    } else {
        xmlSetProp(role, XMLSTR("method"), XMLSTR("get,post,head"));
        xmlSetProp(role, XMLSTR("deny-method"), XMLSTR("*"));
        xmlSetProp(role, XMLSTR("allow-method"), XMLSTR("get,post,head"));
        xmlSetProp(role, XMLSTR("allow-web"), XMLSTR("*"));
        xmlSetProp(role, XMLSTR("deny-admin"), XMLSTR("*"));
    }

    __append_option_tag(role, "client_add", client_add);
    __append_option_tag(role, "client_remove", client_remove);
    __append_option_tag(role, "action_add", action_add);
    __append_option_tag(role, "action_remove", action_remove);
    __append_option_tag(role, "username", username);
    __append_option_tag(role, "password", password);
    __append_option_tag(role, "auth_header", auth_header);
    __append_option_tag(role, "timelimit_header", timelimit_header);
    __append_option_tag(role, "headers", headers);
    __append_option_tag(role, "header_prefix", header_prefix);

    auth = auth_get_authenticator(role);
418
419
420
421
422
423
424
    if (auth) {
        auth_stack_push(stack, auth);
        auth_release(auth);
        ICECAST_LOG_DEBUG("Pushed authenticator %p on stack %p.", auth, stack);
    } else {
        ICECAST_LOG_DEBUG("Failed to set up authenticator.");
    }
Philipp Schafft's avatar
Philipp Schafft committed
425
426
427
428

    xmlFreeNode(role);
}

Marvin Scholz's avatar
Marvin Scholz committed
429
430
431
432
433
static void __append_old_style_exec_event(event_registration_t **list,
                                          const char            *trigger,
                                          const char            *executable)
{
    xmlNodePtr            exec;
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
    event_registration_t *er;

    exec = xmlNewNode(NULL, XMLSTR("event"));

    xmlSetProp(exec, XMLSTR("type"), XMLSTR("exec"));
    xmlSetProp(exec, XMLSTR("trigger"), XMLSTR(trigger));

    __append_option_tag(exec, "executable", executable);

    er = event_new_from_xml_node(exec);
    event_registration_push(list, er);
    event_registration_release(er);

    xmlFreeNode(exec);
}

Marvin Scholz's avatar
Marvin Scholz committed
450
451
452
453
454
455
456
457
static void __append_old_style_url_event(event_registration_t   **list,
                                         const char              *trigger,
                                         const char              *url,
                                         const char              *action,
                                         const char              *username,
                                         const char              *password)
{
    xmlNodePtr            exec;
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
    event_registration_t *er;

    exec = xmlNewNode(NULL, XMLSTR("event"));

    xmlSetProp(exec, XMLSTR("type"), XMLSTR("url"));
    xmlSetProp(exec, XMLSTR("trigger"), XMLSTR(trigger));

    __append_option_tag(exec, "url", url);
    __append_option_tag(exec, "action", action);
    __append_option_tag(exec, "username", username);
    __append_option_tag(exec, "password", password);

    er = event_new_from_xml_node(exec);
    event_registration_push(list, er);
    event_registration_release(er);

    xmlFreeNode(exec);
}

Marvin Scholz's avatar
Marvin Scholz committed
477
478
479
static void config_clear_http_header(ice_config_http_header_t *header)
{
    ice_config_http_header_t *old;
480

Marvin Scholz's avatar
Marvin Scholz committed
481
482
483
484
485
486
487
    while (header) {
        xmlFree(header->name);
        xmlFree(header->value);
        old = header;
        header = header->next;
        free(old);
    }
488
489
}

Marvin Scholz's avatar
Marvin Scholz committed
490
491
static inline ice_config_http_header_t *config_copy_http_header(ice_config_http_header_t *header)
{
492
493
494
495
496
497
498
499
500
501
502
503
504
505
    ice_config_http_header_t *ret = NULL;
    ice_config_http_header_t *cur = NULL;
    ice_config_http_header_t *old = NULL;

    while (header) {
        if (cur) {
            cur->next = calloc(1, sizeof(ice_config_http_header_t));
            old = cur;
            cur = cur->next;
        } else {
            ret = calloc(1, sizeof(ice_config_http_header_t));
            cur = ret;
        }

Marvin Scholz's avatar
Marvin Scholz committed
506
507
        if (!cur)
            return ret; /* TODO: do better error handling */
508

509
510
511
512
        cur->type   = header->type;
        cur->name   = (char *)xmlCharStrdup(header->name);
        cur->value  = (char *)xmlCharStrdup(header->value);
        cur->status = header->status;
513
514

        if (!cur->name || !cur->value) {
Marvin Scholz's avatar
Marvin Scholz committed
515
516
517
518
            if (cur->name)
                xmlFree(cur->name);
            if (cur->value)
                xmlFree(cur->value);
519
520
521
522
523
524
525
526
527
528
529
530
531
            if (old) {
                old->next = NULL;
            } else {
                ret = NULL;
            }
            free(cur);
            return ret;
        }
        header = header->next;
    }
    return ret;
}

532
static void config_clear_mount(mount_proxy *mount)
533
{
534
535
536
537
538
539
540
541
542
543
544
545
    if (mount->mountname)           xmlFree(mount->mountname);
    if (mount->dumpfile)            xmlFree(mount->dumpfile);
    if (mount->intro_filename)      xmlFree(mount->intro_filename);
    if (mount->fallback_mount)      xmlFree(mount->fallback_mount);
    if (mount->stream_name)         xmlFree(mount->stream_name);
    if (mount->stream_description)  xmlFree(mount->stream_description);
    if (mount->stream_url)          xmlFree(mount->stream_url);
    if (mount->stream_genre)        xmlFree(mount->stream_genre);
    if (mount->bitrate)             xmlFree(mount->bitrate);
    if (mount->type)                xmlFree(mount->type);
    if (mount->charset)             xmlFree(mount->charset);
    if (mount->cluster_password)    xmlFree(mount->cluster_password);
Marvin Scholz's avatar
Marvin Scholz committed
546
    if (mount->authstack)           auth_stack_release(mount->authstack);
547

548
    event_registration_release(mount->event);
549
    config_clear_http_header(mount->http_headers);
Marvin Scholz's avatar
Marvin Scholz committed
550
    free(mount);
551
552
}

Marvin Scholz's avatar
Marvin Scholz committed
553
listener_t *config_clear_listener(listener_t *listener)
554
555
556
557
558
{
    listener_t *next = NULL;
    if (listener)
    {
        next = listener->next;
559
560
        if (listener->bind_address)     xmlFree(listener->bind_address);
        if (listener->shoutcast_mount)  xmlFree(listener->shoutcast_mount);
561
562
563
564
        free (listener);
    }
    return next;
}
565

566
567
void config_clear(ice_config_t *c)
{
Marvin Scholz's avatar
Marvin Scholz committed
568
569
570
571
572
573
574
575
    ice_config_dir_t    *dirnode,
                        *nextdirnode;
    relay_server        *relay,
                        *nextrelay;
    mount_proxy         *mount,
                        *nextmount;
    aliases             *alias,
                        *nextalias;
576
#ifdef USE_YP
Marvin Scholz's avatar
Marvin Scholz committed
577
    int                 i;
578
#endif
579

580
    free(c->config_filename);
581

Marvin Scholz's avatar
Marvin Scholz committed
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
    xmlFree(c->server_id);
    if (c->location)        xmlFree(c->location);
    if (c->admin)           xmlFree(c->admin);
    if (c->hostname)        xmlFree(c->hostname);
    if (c->base_dir)        xmlFree(c->base_dir);
    if (c->log_dir)         xmlFree(c->log_dir);
    if (c->webroot_dir)     xmlFree(c->webroot_dir);
    if (c->adminroot_dir)   xmlFree(c->adminroot_dir);
    if (c->null_device)     xmlFree(c->null_device);
    if (c->pidfile)         xmlFree(c->pidfile);
    if (c->banfile)         xmlFree(c->banfile);
    if (c->allowfile)       xmlFree(c->allowfile);
    if (c->playlist_log)    xmlFree(c->playlist_log);
    if (c->access_log)      xmlFree(c->access_log);
    if (c->error_log)       xmlFree(c->error_log);
597
    if (c->shoutcast_mount) xmlFree(c->shoutcast_mount);
Philipp Schafft's avatar
Philipp Schafft committed
598
    if (c->authstack)       auth_stack_release(c->authstack);
Marvin Scholz's avatar
Marvin Scholz committed
599
    if (c->master_server)   xmlFree(c->master_server);
600
    if (c->master_username) xmlFree(c->master_username);
601
    if (c->master_password) xmlFree(c->master_password);
Marvin Scholz's avatar
Marvin Scholz committed
602
603
    if (c->user)            xmlFree(c->user);
    if (c->group)           xmlFree(c->group);
Philipp Schafft's avatar
Philipp Schafft committed
604
    if (c->mimetypes_fn)    xmlFree(c->mimetypes_fn);
605

606
607
608
609
    if (c->tls_context.cert_file)       xmlFree(c->tls_context.cert_file);
    if (c->tls_context.key_file)        xmlFree(c->tls_context.key_file);
    if (c->tls_context.cipher_list)     xmlFree(c->tls_context.cipher_list);

610
611
    event_registration_release(c->event);

612
    while ((c->listen_sock = config_clear_listener(c->listen_sock)));
613

614
615
    thread_mutex_lock(&(_locks.relay_lock));
    relay = c->relay;
616
    while (relay) {
617
618
619
        nextrelay = relay->next;
        xmlFree(relay->server);
        xmlFree(relay->mount);
Karl Heyes's avatar
Karl Heyes committed
620
        xmlFree(relay->localmount);
621
622
623
624
625
626
        free(relay);
        relay = nextrelay;
    }
    thread_mutex_unlock(&(_locks.relay_lock));

    mount = c->mounts;
627
    while (mount) {
628
        nextmount = mount->next;
629
        config_clear_mount(mount);
630
631
632
633
        mount = nextmount;
    }

    alias = c->aliases;
634
    while (alias) {
635
636
637
638
        nextalias = alias->next;
        xmlFree(alias->source);
        xmlFree(alias->destination);
        xmlFree(alias->bind_address);
639
        xmlFree(alias->vhost);
640
641
642
643
644
        free(alias);
        alias = nextalias;
    }

    dirnode = c->dir_list;
645
    while (dirnode) {
646
647
648
649
650
        nextdirnode = dirnode->next;
        xmlFree(dirnode->host);
        free(dirnode);
        dirnode = nextdirnode;
    }
651
#ifdef USE_YP
Karl Heyes's avatar
Karl Heyes committed
652
    i = 0;
Marvin Scholz's avatar
Marvin Scholz committed
653
    while (i < c->num_yp_directories) {
654
        xmlFree(c->yp_url[i]);
Karl Heyes's avatar
Karl Heyes committed
655
656
657
        i++;
    }
#endif
658

659
    config_clear_http_header(c->http_headers);
660
661
662
    memset(c, 0, sizeof(ice_config_t));
}

Marvin Scholz's avatar
Marvin Scholz committed
663
664
665
void config_reread_config(void)
{
    int           ret;
666
    ice_config_t *config;
Marvin Scholz's avatar
Marvin Scholz committed
667
    ice_config_t  new_config;
668
669
670
671
672
673
674
675
676
677
678
    /* reread config file */

    config = config_grab_config(); /* Both to get the lock, and to be able
                                     to find out the config filename */
    xmlSetGenericErrorFunc("config", log_parse_failure);
    ret = config_parse_file(config->config_filename, &new_config);
    if(ret < 0) {
        ICECAST_LOG_ERROR("Error parsing config, not replacing existing config");
        switch (ret) {
            case CONFIG_EINSANE:
                ICECAST_LOG_ERROR("Config filename null or blank");
Marvin Scholz's avatar
Marvin Scholz committed
679
            break;
680
681
            case CONFIG_ENOROOT:
                ICECAST_LOG_ERROR("Root element not found in %s", config->config_filename);
Marvin Scholz's avatar
Marvin Scholz committed
682
            break;
683
684
685
            case CONFIG_EBADROOT:
                ICECAST_LOG_ERROR("Not an icecast2 config file: %s",
                        config->config_filename);
Marvin Scholz's avatar
Marvin Scholz committed
686
            break;
687
688
            default:
                ICECAST_LOG_ERROR("Parse error in reading %s", config->config_filename);
Marvin Scholz's avatar
Marvin Scholz committed
689
            break;
690
691
692
693
694
695
        }
        config_release_config();
    } else {
        config_clear(config);
        config_set_config(&new_config);
        config = config_get_config_unlocked();
Marvin Scholz's avatar
Marvin Scholz committed
696
        restart_logging(config);
697
        connection_reread_config(config);
Marvin Scholz's avatar
Marvin Scholz committed
698
699
700
        yp_recheck_config(config);
        fserve_recheck_mime_types(config);
        stats_global(config);
701
702
703
704
705
        config_release_config();
        slave_update_all_mounts();
    }
}

706
707
708
709
710
711
712
713
int config_initial_parse_file(const char *filename)
{
    /* Since we're already pointing at it, we don't need to copy it in place */
    return config_parse_file(filename, &_current_configuration);
}

int config_parse_file(const char *filename, ice_config_t *configuration)
{
Marvin Scholz's avatar
Marvin Scholz committed
714
    xmlDocPtr  doc;
715
716
    xmlNodePtr node;

Marvin Scholz's avatar
Marvin Scholz committed
717
718
    if (filename == NULL || strcmp(filename, "") == 0)
        return CONFIG_EINSANE;
719

720
    doc = xmlParseFile(filename);
Marvin Scholz's avatar
Marvin Scholz committed
721
    if (doc == NULL)
722
723
724
725
726
727
728
        return CONFIG_EPARSE;
    node = xmlDocGetRootElement(doc);
    if (node == NULL) {
        xmlFreeDoc(doc);
        return CONFIG_ENOROOT;
    }

729
    if (xmlStrcmp(node->name, XMLSTR("icecast")) != 0) {
730
731
732
733
734
        xmlFreeDoc(doc);
        return CONFIG_EBADROOT;
    }

    config_init_configuration(configuration);
Marvin Scholz's avatar
Marvin Scholz committed
735
    configuration->config_filename = strdup(filename);
736
737
    _parse_root(doc, node->xmlChildrenNode, configuration);
    xmlFreeDoc(doc);
738
    _merge_mounts_all(configuration);
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
    return 0;
}

int config_parse_cmdline(int arg, char **argv)
{
    return 0;
}

ice_config_locks *config_locks(void)
{
    return &_locks;
}

void config_release_config(void)
{
754
    thread_rwlock_unlock(&(_locks.config_lock));
755
756
757
758
}

ice_config_t *config_get_config(void)
{
759
760
761
762
763
764
765
    thread_rwlock_rlock(&(_locks.config_lock));
    return &_current_configuration;
}

ice_config_t *config_grab_config(void)
{
    thread_rwlock_wlock(&(_locks.config_lock));
766
767
768
769
    return &_current_configuration;
}

/* MUST be called with the lock held! */
Marvin Scholz's avatar
Marvin Scholz committed
770
771
void config_set_config(ice_config_t *config)
{
772
773
774
775
776
777
778
779
780
781
    memcpy(&_current_configuration, config, sizeof(ice_config_t));
}

ice_config_t *config_get_config_unlocked(void)
{
    return &_current_configuration;
}

static void _set_defaults(ice_config_t *configuration)
{
Marvin Scholz's avatar
Marvin Scholz committed
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
    configuration
        ->location = (char *) xmlCharStrdup(CONFIG_DEFAULT_LOCATION);
    configuration
        ->server_id = (char *) xmlCharStrdup(ICECAST_VERSION_STRING);
    configuration
        ->admin = (char *) xmlCharStrdup(CONFIG_DEFAULT_ADMIN);
    configuration
        ->client_limit = CONFIG_DEFAULT_CLIENT_LIMIT;
    configuration
        ->source_limit = CONFIG_DEFAULT_SOURCE_LIMIT;
    configuration
        ->queue_size_limit = CONFIG_DEFAULT_QUEUE_SIZE_LIMIT;
    configuration
        ->client_timeout = CONFIG_DEFAULT_CLIENT_TIMEOUT;
    configuration
        ->header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
    configuration
        ->source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
    configuration
        ->shoutcast_mount = (char *) xmlCharStrdup(CONFIG_DEFAULT_SHOUTCAST_MOUNT);
    configuration
        ->fileserve  = CONFIG_DEFAULT_FILESERVE;
    configuration
        ->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
    configuration
        ->on_demand = 0;
    configuration
        ->dir_list = NULL;
    configuration
811
        ->hostname = (char *) xmlCharStrdup(CONFIG_DEFAULT_HOSTNAME);
Marvin Scholz's avatar
Marvin Scholz committed
812
    configuration
813
        ->mimetypes_fn = (char *) xmlCharStrdup(MIMETYPESFILE);
Marvin Scholz's avatar
Marvin Scholz committed
814
815
816
817
818
819
820
    configuration
        ->master_server = NULL;
    configuration
        ->master_server_port = 0;
    configuration
        ->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
    configuration
821
        ->master_username = (char *) xmlCharStrdup(CONFIG_DEFAULT_MASTER_USERNAME);
Marvin Scholz's avatar
Marvin Scholz committed
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
    configuration
        ->master_password = NULL;
    configuration
        ->base_dir = (char *) xmlCharStrdup(CONFIG_DEFAULT_BASE_DIR);
    configuration
        ->log_dir = (char *) xmlCharStrdup(CONFIG_DEFAULT_LOG_DIR);
    configuration
        ->null_device = (char *) xmlCharStrdup(CONFIG_DEFAULT_NULL_FILE);
    configuration
        ->webroot_dir = (char *) xmlCharStrdup(CONFIG_DEFAULT_WEBROOT_DIR);
    configuration
        ->adminroot_dir = (char *) xmlCharStrdup(CONFIG_DEFAULT_ADMINROOT_DIR);
    configuration
        ->playlist_log = (char *) xmlCharStrdup(CONFIG_DEFAULT_PLAYLIST_LOG);
    configuration
        ->access_log = (char *) xmlCharStrdup(CONFIG_DEFAULT_ACCESS_LOG);
    configuration
        ->error_log = (char *) xmlCharStrdup(CONFIG_DEFAULT_ERROR_LOG);
    configuration
        ->loglevel = CONFIG_DEFAULT_LOG_LEVEL;
    configuration
        ->chroot = CONFIG_DEFAULT_CHROOT;
    configuration
        ->chuid = CONFIG_DEFAULT_CHUID;
    configuration
        ->user = NULL;
    configuration
        ->group = NULL;
    configuration
        ->num_yp_directories = 0;
Karl Heyes's avatar
Karl Heyes committed
852
    /* default to a typical prebuffer size used by clients */
Marvin Scholz's avatar
Marvin Scholz committed
853
854
    configuration
        ->burst_size = CONFIG_DEFAULT_BURST_SIZE;
855
856
    configuration->tls_context
        .cipher_list = (char *) xmlCharStrdup(CONFIG_DEFAULT_CIPHER_LIST);
857
858
}

Marvin Scholz's avatar
Marvin Scholz committed
859
860
static inline void __check_hostname(ice_config_t *configuration)
{
861
862
    char *p;

863
    /* ensure we have a non-NULL buffer: */
864
    if (!configuration->hostname)
865
        configuration->hostname = (char *)xmlCharStrdup(CONFIG_DEFAULT_HOSTNAME);
866

867
    /* convert to lower case: */
Marvin Scholz's avatar
Marvin Scholz committed
868
    for (p = configuration->hostname; *p; p++) {
869
870
        if ( *p >= 'A' && *p <= 'Z' )
            *p += 'a' - 'A';
Marvin Scholz's avatar
Marvin Scholz committed
871
    }
872
873
874
875
876
877
878

    configuration->sane_hostname = 0;
    switch (util_hostcheck(configuration->hostname)) {
        case HOSTCHECK_SANE:
            configuration->sane_hostname = 1;
        break;
        case HOSTCHECK_ERROR:
Marvin Scholz's avatar
Marvin Scholz committed
879
880
            ICECAST_LOG_ERROR("Can not check hostname \"%s\".",
                configuration->hostname);
881
882
        break;
        case HOSTCHECK_NOT_FQDN:
Marvin Scholz's avatar
Marvin Scholz committed
883
            ICECAST_LOG_WARN("Warning, <hostname> seems not to be set to a "
884
                "fully qualified domain name (FQDN). This may cause problems, "
Marvin Scholz's avatar
Marvin Scholz committed
885
                "e.g. with YP directory listings.");
886
887
        break;
        case HOSTCHECK_IS_LOCALHOST:
Marvin Scholz's avatar
Marvin Scholz committed
888
            ICECAST_LOG_WARN("Warning, <hostname> not configured, using "
889
890
891
892
                "default value \"%s\". This will cause problems, e.g. "
                "this breaks YP directory listings. YP directory listing "
                "support will be disabled.", CONFIG_DEFAULT_HOSTNAME);
                /* FIXME actually disable YP */
893
894
        break;
        case HOSTCHECK_IS_IPV4:
Marvin Scholz's avatar
Marvin Scholz committed
895
896
897
            ICECAST_LOG_WARN("Warning, <hostname> seems to be set to an IPv4 "
                "address. This may cause problems, e.g. with YP directory "
                "listings.");
898
899
        break;
        case HOSTCHECK_IS_IPV6:
Marvin Scholz's avatar
Marvin Scholz committed
900
901
902
            ICECAST_LOG_WARN("Warning, <hostname> seems to be set to an IPv6 "
                "address. This may cause problems, e.g. with YP directory "
                "listings.");
903
904
        break;
        case HOSTCHECK_BADCHAR:
905
            ICECAST_LOG_WARN("Warning, <hostname> contains unusual "
Marvin Scholz's avatar
Marvin Scholz committed
906
907
                "characters. This may cause problems, e.g. with YP directory "
                "listings.");
908
909
910
911
        break;
    }
}

Marvin Scholz's avatar
Marvin Scholz committed
912
913
914
static void _parse_root(xmlDocPtr       doc,
                        xmlNodePtr      node,
                        ice_config_t   *configuration)
915
916
{
    char *tmp;
Philipp Schafft's avatar
Philipp Schafft committed
917
    char *source_password = NULL;
918

Marvin Scholz's avatar
Marvin Scholz committed
919
    configuration
920
        ->listen_sock       = calloc(1, sizeof(*configuration->listen_sock));
Marvin Scholz's avatar
Marvin Scholz committed
921
922
923
924
    configuration
        ->listen_sock->port = 8000;
    configuration
        ->listen_sock_count = 1;
925

926
    do {
Marvin Scholz's avatar
Marvin Scholz committed
927
928
929
930
931
932
933
        if (node == NULL)
            break;
        if (xmlIsBlankNode(node))
            continue;
        if (xmlStrcmp(node->name, XMLSTR("location")) == 0) {
            if (configuration->location)
                xmlFree(configuration->location);
934
            configuration->location = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
935
        } else if (xmlStrcmp(node->name, XMLSTR("admin")) == 0) {
Marvin Scholz's avatar
Marvin Scholz committed
936
937
            if (configuration->admin)
                xmlFree(configuration->admin);
938
            configuration->admin = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
939
        } else if (xmlStrcmp(node->name, XMLSTR("server-id")) == 0) {
Marvin Scholz's avatar
Marvin Scholz committed
940
            xmlFree(configuration->server_id);
941
            configuration->server_id = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
942
943
944
945
            ICECAST_LOG_WARN("Warning, server version string override "
                "detected. This may lead to unexpected client software "
                "behavior.");
        } else if (xmlStrcmp(node->name, XMLSTR("authentication")) == 0) {
Philipp Schafft's avatar
Philipp Schafft committed
946
            _parse_authentication(doc, node->xmlChildrenNode, configuration, &source_password);
Marvin Scholz's avatar
Marvin Scholz committed
947
        } else if (xmlStrcmp(node->name, XMLSTR("source-password")) == 0) {
948
            /* TODO: This is the backwards-compatibility location */
Marvin Scholz's avatar
Marvin Scholz committed
949
950
            ICECAST_LOG_WARN("<source-password> defined outside "
                "<authentication>. This is deprecated and will be removed in "
951
952
                "version 2.X.0");
	    /* FIXME Settle target version for removal of this functionality! */
Philipp Schafft's avatar
Philipp Schafft committed
953
954
955
            if (source_password)
                xmlFree(source_password);
            source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
956
        } else if (xmlStrcmp(node->name, XMLSTR("icelogin")) == 0) {
957
            ICECAST_LOG_ERROR("<icelogin> support has been removed.");
Marvin Scholz's avatar
Marvin Scholz committed
958
        } else if (xmlStrcmp(node->name, XMLSTR("fileserve")) == 0) {
959
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
960
            configuration->fileserve = util_str_to_bool(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
961
962
963
            if (tmp)
                xmlFree(tmp);
        } else if (xmlStrcmp(node->name, XMLSTR("relays-on-demand")) == 0) {
964
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
965
            configuration->on_demand = util_str_to_bool(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
966
967
968
969
970
            if (tmp)
                xmlFree(tmp);
        } else if (xmlStrcmp(node->name, XMLSTR("hostname")) == 0) {
            if (configuration->hostname)
                xmlFree(configuration->hostname);
971
            configuration->hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
972
        } else if (xmlStrcmp(node->name, XMLSTR("mime-types")) == 0) {
973
            ICECAST_LOG_WARN("<mime-types> has been moved into <paths>. Please update your configuration file.");
Marvin Scholz's avatar
Marvin Scholz committed
974
975
            if (configuration->mimetypes_fn)
                xmlFree(configuration->mimetypes_fn);
976
            configuration->mimetypes_fn = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
977
        } else if (xmlStrcmp(node->name, XMLSTR("listen-socket")) == 0) {
978
            _parse_listen_socket(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
979
        } else if (xmlStrcmp(node->name, XMLSTR("port")) == 0) {
980
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
981
            if (tmp && *tmp) {
982
983
984
985
                configuration->port = atoi(tmp);
                configuration->listen_sock->port = atoi(tmp);
                xmlFree(tmp);
            } else {
986
                ICECAST_LOG_WARN("<port> setting must not be empty.");
987
            }
Marvin Scholz's avatar
Marvin Scholz committed
988
        } else if (xmlStrcmp(node->name, XMLSTR("bind-address")) == 0) {
989
            if (configuration->listen_sock->bind_address)
990
991
                xmlFree(configuration->listen_sock->bind_address);
            configuration->listen_sock->bind_address = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
992
993
994
        } else if (xmlStrcmp(node->name, XMLSTR("master-server")) == 0) {
            if (configuration->master_server)
                xmlFree(configuration->master_server);
995
            configuration->master_server = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
996
997
998
        } else if (xmlStrcmp(node->name, XMLSTR("master-username")) == 0) {
            if (configuration->master_username)
                xmlFree(configuration->master_username);
999
            configuration->master_username = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
1000
        } else if (xmlStrcmp(node->name, XMLSTR("master-password")) == 0) {