cfgfile.c 94.4 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
#include "cfgfile.h"
31
#include "global.h"
32
33
#include "refbuf.h"
#include "client.h"
34
35
36
37
#include "logging.h"
#include "util.h"
#include "auth.h"
#include "event.h"
38
#include "tls.h"
39

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

Marvin Scholz's avatar
Marvin Scholz committed
46
47
48
49
50
#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
51
#define CONFIG_DEFAULT_QUEUE_SIZE_LIMIT (500*1024)
Marvin Scholz's avatar
Marvin Scholz committed
52
53
54
55
56
57
58
#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"
59
#define CONFIG_DEFAULT_SHOUTCAST_USER   "source"
Marvin Scholz's avatar
Marvin Scholz committed
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
101
102
#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"
103
104

#ifndef _WIN32
Marvin Scholz's avatar
Marvin Scholz committed
105
106
107
108
109
110
#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"
111
#else
Marvin Scholz's avatar
Marvin Scholz committed
112
113
114
115
116
117
#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"
118
119
120
121
122
123
124
125
126
127
128
129
#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
130
131
132
133
134
135
136
137
138
139

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);

140
141
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
142
143
144
145
146

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

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

150
151
152
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
153
154
operation_mode config_str_to_omode(const char *str)
{
155
156
157
158
159
160
161
162
    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;
163
164
    } else if (strcasecmp(str, "strict") == 0) {
        return OMODE_STRICT;
165
166
167
168
169
170
    } else {
        ICECAST_LOG_ERROR("Unknown operation mode \"%s\", falling back to DEFAULT.", str);
        return OMODE_DEFAULT;
    }
}

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

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

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

Marvin Scholz's avatar
Marvin Scholz committed
188
189
void config_shutdown(void)
{
190
191
192
193
194
195
196
197
198
199
200
201
    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);
}

202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
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
226
227
static inline int __parse_public(const char *str)
{
228
229
230
231
232
233
234
235
236
237
238
239
    /* 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);
}

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
272
273
/* 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;
}

274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/* 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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
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
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
355
356

    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
357
358
359
360
static void __append_option_tag(xmlNodePtr  parent,
                                const char *name,
                                const char *value)
{
Philipp Schafft's avatar
Philipp Schafft committed
361
362
363
364
365
366
367
368
369
370
    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
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
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
386

387
    if (!stack || (!client_add && !client_remove))
Philipp Schafft's avatar
Philipp Schafft committed
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
418
419
        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);
420
421
422
423
424
425
426
    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
427
428
429
430

    xmlFreeNode(role);
}

Marvin Scholz's avatar
Marvin Scholz committed
431
432
433
434
435
static void __append_old_style_exec_event(event_registration_t **list,
                                          const char            *trigger,
                                          const char            *executable)
{
    xmlNodePtr            exec;
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
    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
452
453
454
455
456
457
458
459
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;
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
    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
479
480
481
static void config_clear_http_header(ice_config_http_header_t *header)
{
    ice_config_http_header_t *old;
482

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

Marvin Scholz's avatar
Marvin Scholz committed
492
493
static inline ice_config_http_header_t *config_copy_http_header(ice_config_http_header_t *header)
{
494
495
496
497
498
499
500
501
502
503
504
505
506
507
    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
508
509
        if (!cur)
            return ret; /* TODO: do better error handling */
510

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

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

534
static void config_clear_mount(mount_proxy *mount)
535
{
536
537
538
539
540
541
542
543
544
545
546
547
    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
548
    if (mount->authstack)           auth_stack_release(mount->authstack);
549

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

555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
static void config_clear_resource(resource_t *resource)
{
    resource_t *nextresource;

    while (resource) {
        nextresource = resource->next;
        xmlFree(resource->source);
        xmlFree(resource->destination);
        xmlFree(resource->bind_address);
        xmlFree(resource->vhost);
        free(resource);
        resource = nextresource;
    }
}

Marvin Scholz's avatar
Marvin Scholz committed
570
listener_t *config_clear_listener(listener_t *listener)
571
572
573
574
575
{
    listener_t *next = NULL;
    if (listener)
    {
        next = listener->next;
576
577
        if (listener->bind_address)     xmlFree(listener->bind_address);
        if (listener->shoutcast_mount)  xmlFree(listener->shoutcast_mount);
578
579
580
581
        free (listener);
    }
    return next;
}
582

583
584
void config_clear(ice_config_t *c)
{
Marvin Scholz's avatar
Marvin Scholz committed
585
586
587
588
589
590
    ice_config_dir_t    *dirnode,
                        *nextdirnode;
    relay_server        *relay,
                        *nextrelay;
    mount_proxy         *mount,
                        *nextmount;
591
#ifdef USE_YP
Marvin Scholz's avatar
Marvin Scholz committed
592
    int                 i;
593
#endif
594

595
    free(c->config_filename);
596

Marvin Scholz's avatar
Marvin Scholz committed
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
    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);
612
    if (c->shoutcast_mount) xmlFree(c->shoutcast_mount);
613
    if (c->shoutcast_user)  xmlFree(c->shoutcast_user);
Philipp Schafft's avatar
Philipp Schafft committed
614
    if (c->authstack)       auth_stack_release(c->authstack);
Marvin Scholz's avatar
Marvin Scholz committed
615
    if (c->master_server)   xmlFree(c->master_server);
616
    if (c->master_username) xmlFree(c->master_username);
617
    if (c->master_password) xmlFree(c->master_password);
Marvin Scholz's avatar
Marvin Scholz committed
618
619
    if (c->user)            xmlFree(c->user);
    if (c->group)           xmlFree(c->group);
Philipp Schafft's avatar
Philipp Schafft committed
620
    if (c->mimetypes_fn)    xmlFree(c->mimetypes_fn);
621

622
623
624
625
    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);

626
627
    event_registration_release(c->event);

628
    while ((c->listen_sock = config_clear_listener(c->listen_sock)));
629

630
631
    thread_mutex_lock(&(_locks.relay_lock));
    relay = c->relay;
632
    while (relay) {
633
634
635
        nextrelay = relay->next;
        xmlFree(relay->server);
        xmlFree(relay->mount);
Karl Heyes's avatar
Karl Heyes committed
636
        xmlFree(relay->localmount);
637
638
639
640
641
642
        free(relay);
        relay = nextrelay;
    }
    thread_mutex_unlock(&(_locks.relay_lock));

    mount = c->mounts;
643
    while (mount) {
644
        nextmount = mount->next;
645
        config_clear_mount(mount);
646
647
648
        mount = nextmount;
    }

649
    config_clear_resource(c->resources);
650
651

    dirnode = c->dir_list;
652
    while (dirnode) {
653
654
655
656
657
        nextdirnode = dirnode->next;
        xmlFree(dirnode->host);
        free(dirnode);
        dirnode = nextdirnode;
    }
658
#ifdef USE_YP
Karl Heyes's avatar
Karl Heyes committed
659
    i = 0;
Marvin Scholz's avatar
Marvin Scholz committed
660
    while (i < c->num_yp_directories) {
661
        xmlFree(c->yp_url[i]);
Karl Heyes's avatar
Karl Heyes committed
662
663
664
        i++;
    }
#endif
665

666
    config_clear_http_header(c->http_headers);
667
668
669
    memset(c, 0, sizeof(ice_config_t));
}

Marvin Scholz's avatar
Marvin Scholz committed
670
671
672
void config_reread_config(void)
{
    int           ret;
673
    ice_config_t *config;
Marvin Scholz's avatar
Marvin Scholz committed
674
    ice_config_t  new_config;
675
676
677
678
679
680
681
682
683
684
685
    /* 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
686
            break;
687
688
            case CONFIG_ENOROOT:
                ICECAST_LOG_ERROR("Root element not found in %s", config->config_filename);
Marvin Scholz's avatar
Marvin Scholz committed
689
            break;
690
691
692
            case CONFIG_EBADROOT:
                ICECAST_LOG_ERROR("Not an icecast2 config file: %s",
                        config->config_filename);
Marvin Scholz's avatar
Marvin Scholz committed
693
            break;
694
695
            default:
                ICECAST_LOG_ERROR("Parse error in reading %s", config->config_filename);
Marvin Scholz's avatar
Marvin Scholz committed
696
            break;
697
698
699
700
701
702
        }
        config_release_config();
    } else {
        config_clear(config);
        config_set_config(&new_config);
        config = config_get_config_unlocked();
Marvin Scholz's avatar
Marvin Scholz committed
703
        restart_logging(config);
704
        connection_reread_config(config);
Marvin Scholz's avatar
Marvin Scholz committed
705
706
707
        yp_recheck_config(config);
        fserve_recheck_mime_types(config);
        stats_global(config);
708
709
710
711
712
        config_release_config();
        slave_update_all_mounts();
    }
}

713
714
715
716
717
718
719
720
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
721
    xmlDocPtr  doc;
722
723
    xmlNodePtr node;

Marvin Scholz's avatar
Marvin Scholz committed
724
725
    if (filename == NULL || strcmp(filename, "") == 0)
        return CONFIG_EINSANE;
726

727
    doc = xmlParseFile(filename);
Marvin Scholz's avatar
Marvin Scholz committed
728
    if (doc == NULL)
729
730
731
732
733
734
735
        return CONFIG_EPARSE;
    node = xmlDocGetRootElement(doc);
    if (node == NULL) {
        xmlFreeDoc(doc);
        return CONFIG_ENOROOT;
    }

736
    if (xmlStrcmp(node->name, XMLSTR("icecast")) != 0) {
737
738
739
740
741
        xmlFreeDoc(doc);
        return CONFIG_EBADROOT;
    }

    config_init_configuration(configuration);
Marvin Scholz's avatar
Marvin Scholz committed
742
    configuration->config_filename = strdup(filename);
743
744
    _parse_root(doc, node->xmlChildrenNode, configuration);
    xmlFreeDoc(doc);
745
    _merge_mounts_all(configuration);
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
    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)
{
761
    thread_rwlock_unlock(&(_locks.config_lock));
762
763
764
765
}

ice_config_t *config_get_config(void)
{
766
767
768
769
770
771
772
    thread_rwlock_rlock(&(_locks.config_lock));
    return &_current_configuration;
}

ice_config_t *config_grab_config(void)
{
    thread_rwlock_wlock(&(_locks.config_lock));
773
774
775
776
    return &_current_configuration;
}

/* MUST be called with the lock held! */
Marvin Scholz's avatar
Marvin Scholz committed
777
778
void config_set_config(ice_config_t *config)
{
779
780
781
782
783
784
785
786
787
788
    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
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
    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);
809
810
    configuration
        ->shoutcast_user = (char *) xmlCharStrdup(CONFIG_DEFAULT_SHOUTCAST_USER);
Marvin Scholz's avatar
Marvin Scholz committed
811
812
813
814
815
816
817
818
819
    configuration
        ->fileserve  = CONFIG_DEFAULT_FILESERVE;
    configuration
        ->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
    configuration
        ->on_demand = 0;
    configuration
        ->dir_list = NULL;
    configuration
820
        ->hostname = (char *) xmlCharStrdup(CONFIG_DEFAULT_HOSTNAME);
Marvin Scholz's avatar
Marvin Scholz committed
821
    configuration
822
        ->mimetypes_fn = (char *) xmlCharStrdup(MIMETYPESFILE);
Marvin Scholz's avatar
Marvin Scholz committed
823
824
825
826
827
828
829
    configuration
        ->master_server = NULL;
    configuration
        ->master_server_port = 0;
    configuration
        ->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
    configuration
830
        ->master_username = (char *) xmlCharStrdup(CONFIG_DEFAULT_MASTER_USERNAME);
Marvin Scholz's avatar
Marvin Scholz committed
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
    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
861
    /* default to a typical prebuffer size used by clients */
Marvin Scholz's avatar
Marvin Scholz committed
862
863
    configuration
        ->burst_size = CONFIG_DEFAULT_BURST_SIZE;
864
865
    configuration->tls_context
        .cipher_list = (char *) xmlCharStrdup(CONFIG_DEFAULT_CIPHER_LIST);
866
867
}

Marvin Scholz's avatar
Marvin Scholz committed
868
869
static inline void __check_hostname(ice_config_t *configuration)
{
870
871
    char *p;

872
    /* ensure we have a non-NULL buffer: */
873
    if (!configuration->hostname)
874
        configuration->hostname = (char *)xmlCharStrdup(CONFIG_DEFAULT_HOSTNAME);
875

876
    /* convert to lower case: */
Marvin Scholz's avatar
Marvin Scholz committed
877
    for (p = configuration->hostname; *p; p++) {
878
879
        if ( *p >= 'A' && *p <= 'Z' )
            *p += 'a' - 'A';
Marvin Scholz's avatar
Marvin Scholz committed
880
    }
881
882
883
884
885
886
887

    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
888
889
            ICECAST_LOG_ERROR("Can not check hostname \"%s\".",
                configuration->hostname);
890
891
        break;
        case HOSTCHECK_NOT_FQDN:
Marvin Scholz's avatar
Marvin Scholz committed
892
            ICECAST_LOG_WARN("Warning, <hostname> seems not to be set to a "
893
                "fully qualified domain name (FQDN). This may cause problems, "
Marvin Scholz's avatar
Marvin Scholz committed
894
                "e.g. with YP directory listings.");
895
896
        break;
        case HOSTCHECK_IS_LOCALHOST:
Marvin Scholz's avatar
Marvin Scholz committed
897
            ICECAST_LOG_WARN("Warning, <hostname> not configured, using "
898
899
900
901
                "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 */
902
903
        break;
        case HOSTCHECK_IS_IPV4:
Marvin Scholz's avatar
Marvin Scholz committed
904
905
906
            ICECAST_LOG_WARN("Warning, <hostname> seems to be set to an IPv4 "
                "address. This may cause problems, e.g. with YP directory "
                "listings.");
907
908
        break;
        case HOSTCHECK_IS_IPV6:
Marvin Scholz's avatar
Marvin Scholz committed
909
910
911
            ICECAST_LOG_WARN("Warning, <hostname> seems to be set to an IPv6 "
                "address. This may cause problems, e.g. with YP directory "
                "listings.");
912
913
        break;
        case HOSTCHECK_BADCHAR:
914
            ICECAST_LOG_WARN("Warning, <hostname> contains unusual "
Marvin Scholz's avatar
Marvin Scholz committed
915
916
                "characters. This may cause problems, e.g. with YP directory "
                "listings.");
917
918
919
920
        break;
    }
}

Marvin Scholz's avatar
Marvin Scholz committed
921
922
923
static void _parse_root(xmlDocPtr       doc,
                        xmlNodePtr      node,
                        ice_config_t   *configuration)
924
925
{
    char *tmp;
Philipp Schafft's avatar
Philipp Schafft committed
926
    char *source_password = NULL;
927

Marvin Scholz's avatar
Marvin Scholz committed
928
    configuration
929
        ->listen_sock       = calloc(1, sizeof(*configuration->listen_sock));
Marvin Scholz's avatar
Marvin Scholz committed
930
931
932
933
    configuration
        ->listen_sock->port = 8000;
    configuration
        ->listen_sock_count = 1;
934

935
    do {
Marvin Scholz's avatar
Marvin Scholz committed
936
937
938
939
940
941
942
        if (node == NULL)
            break;
        if (xmlIsBlankNode(node))
            continue;
        if (xmlStrcmp(node->name, XMLSTR("location")) == 0) {
            if (configuration->location)
                xmlFree(configuration->location);
943
            configuration->location = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
944
        } else if (xmlStrcmp(node->name, XMLSTR("admin")) == 0) {
Marvin Scholz's avatar
Marvin Scholz committed
945
946
            if (configuration->admin)
                xmlFree(configuration->admin);
947
            configuration->admin = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
948
        } else if (xmlStrcmp(node->name, XMLSTR("server-id")) == 0) {
Marvin Scholz's avatar
Marvin Scholz committed
949
            xmlFree(configuration->server_id);
950
            configuration->server_id = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
951
952
953
954
            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
955
            _parse_authentication(doc, node->xmlChildrenNode, configuration, &source_password);
Marvin Scholz's avatar
Marvin Scholz committed
956
        } else if (xmlStrcmp(node->name, XMLSTR("source-password")) == 0) {
957
            /* TODO: This is the backwards-compatibility location */
Marvin Scholz's avatar
Marvin Scholz committed
958
959
            ICECAST_LOG_WARN("<source-password> defined outside "
                "<authentication>. This is deprecated and will be removed in "
960
961
                "version 2.X.0");
	    /* FIXME Settle target version for removal of this functionality! */
Philipp Schafft's avatar
Philipp Schafft committed
962
963
964
            if (source_password)
                xmlFree(source_password);
            source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
965
        } else if (xmlStrcmp(node->name, XMLSTR("icelogin")) == 0) {
966
            ICECAST_LOG_ERROR("<icelogin> support has been removed.");
Marvin Scholz's avatar
Marvin Scholz committed
967
        } else if (xmlStrcmp(node->name, XMLSTR("fileserve")) == 0) {
968
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
969
            configuration->fileserve = util_str_to_bool(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
970
971
972
            if (tmp)
                xmlFree(tmp);
        } else if (xmlStrcmp(node->name, XMLSTR("relays-on-demand")) == 0) {
973
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
974
            configuration->on_demand = util_str_to_bool(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
975
976
977
978
979
            if (tmp)
                xmlFree(tmp);
        } else if (xmlStrcmp(node->name, XMLSTR("hostname")) == 0) {
            if (configuration->hostname)
                xmlFree(configuration->hostname);
980
            configuration->hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
981
        } else if (xmlStrcmp(node->name, XMLSTR("mime-types")) == 0) {
982
            ICECAST_LOG_WARN("<mime-types> has been moved into <paths>. Please update your configuration file.");
Marvin Scholz's avatar
Marvin Scholz committed
983
984
            if (configuration->mimetypes_fn)
                xmlFree(configuration->mimetypes_fn);
985
            configuration->mimetypes_fn = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
986
        } else if (xmlStrcmp(node->name, XMLSTR("listen-socket")) == 0) {
987
            _parse_listen_socket(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
988
        } else if (xmlStrcmp(node->name, XMLSTR("port")) == 0) {
989
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
990
            if (tmp && *tmp) {
991
992
993
994
                configuration->port = atoi(tmp);
                configuration->listen_sock->port = atoi(tmp);
                xmlFree(tmp);
            } else {
995
                ICECAST_LOG_WARN("<port> setting must not be empty.");
996
            }
Marvin Scholz's avatar
Marvin Scholz committed
997
        } else if (xmlStrcmp(node->name, XMLSTR("bind-address")) == 0) {
998
            if (configuration->listen_sock->bind_address)
999
1000
                xmlFree(configuration->listen_sock->bind_address);
            configuration->listen_sock->bind_address = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
1001
1002
1003
        } else if (xmlStrcmp(node->name, XMLSTR("master-server")) == 0) {
            if (configuration->master_server)
                xmlFree(configuration->master_server);
1004
            configuration->master_server = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
1005
1006
1007
        } else if (xmlStrcmp(node->name, XMLSTR("master-username")) == 0) {
            if (configuration->master_username)
                xmlFree(configuration->master_username);
1008
            configuration->master_username = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
1009
1010
1011
        } else if (xmlStrcmp(node->name, XMLSTR("master-password")) == 0) {
            if (configuration->master_password)
                xmlFree(configuration->master_password);
1012
            configuration->master_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
1013
        } else if (xmlStrcmp(node->name, XMLSTR("master-server-port")) == 0) {
1014
            __read_int(doc, node, &configuration->master_server_port, "<master-server-port> must not be empty.");
Marvin Scholz's avatar
Marvin Scholz committed
1015
        } else if (xmlStrcmp(node->name, XMLSTR("master-update-interval")) == 0) {
1016
            __read_int(doc, node, &configuration->master_update_interval, "<master-update-interval> must not be empty.");
Marvin Scholz's avatar
Marvin Scholz committed
1017
1018
1019
        } else if (xmlStrcmp(node->name, XMLSTR("shoutcast-mount")) == 0) {
            if (configuration->shoutcast_mount)
                xmlFree(configuration->shoutcast_mount);
1020
            configuration->shoutcast_mount = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
1021
        } else if (xmlStrcmp(node->name, XMLSTR("limits")) == 0) {
1022
            _parse_limits(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
1023
        } else if (xmlStrcmp(node->name, XMLSTR("http-headers")) == 0) {
1024
            _parse_http_headers(doc, node->xmlChildrenNode, &(configuration->http_headers));
Marvin Scholz's avatar
Marvin Scholz committed
1025
        } else if (xmlStrcmp(node->name, XMLSTR("relay")) == 0) {
1026
            _parse_relay(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
1027
        } else if (xmlStrcmp(node->name, XMLSTR("mount")) == 0) {
1028
            _parse_mount(doc, node, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
1029
        } else if (xmlStrcmp(node->name, XMLSTR("directory")) == 0) {
1030
            _parse_directory(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
1031
        } else if (xmlStrcmp(node->name, XMLSTR("paths")) == 0) {
1032
            _parse_paths(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
1033
        } else if (xmlStrcmp(node->name, XMLSTR("logging")) == 0) {
1034
            _parse_logging(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
1035
        } else if (xmlStrcmp(node->name, XMLSTR("security")) == 0) {
1036
            _parse_security(doc, node->xmlChildrenNode, configuration);
1037
1038
        } else if (xmlStrcmp(node->name, XMLSTR("event-bindings")) == 0 ||
                   xmlStrcmp(node->name, XMLSTR("kartoffelsalat")) == 0) {
1039
            _parse_events(&configuration->event, node->xmlChildrenNode);
1040
1041
        }
    } while ((node = node->next));
1042

Philipp Schafft's avatar
Philipp Schafft committed
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
    /* global source password is set.
     * We need to set it on default mount.
     * If default mount has a authstack not NULL we don't need to do anything.
     */
    if (source_password) {
        mount_proxy *mount = config_find_mount(configuration, NULL, MOUNT_TYPE_DEFAULT);
        if (!mount) {
            /* create a default mount here */
            xmlNodePtr node;
            node = xmlNewNode(NULL, XMLSTR("mount"));
            xmlSetProp(node, XMLSTR("type"), XMLSTR("default"));
            _parse_mount(doc, node, configuration);
            xmlFreeNode(node);
            mount = config_find_mount(configuration, NULL, MOUNT_TYPE_DEFAULT);
        }
        if (mount) {
Marvin Scholz's avatar
Marvin Scholz committed
1059
1060
1061
1062
1063
1064
1065
            if (!mount->authstack) {
                __append_old_style_auth(&mount->authstack,
                                        "legacy-global-source",
                                        AUTH_TYPE_STATIC, "source",
                                        source_password, NULL,
                                        "source,put,get", 0, "*");
            }
Philipp Schafft's avatar
Philipp Schafft committed
1066
        } else {
1067
1068
            ICECAST_LOG_ERROR("Can not find nor create default mount, but "
                "global legacy source password set. This is bad.");
Philipp Schafft's avatar
Philipp Schafft committed
1069
        }
1070
        xmlFree(source_password);
Philipp Schafft's avatar
Philipp Schafft committed
1071
    }
1072
1073
    /* drop the first listening socket details if more than one is defined, as we only
     * have port or listen-socket not both */
Marvin Scholz's avatar
Marvin Scholz committed
1074
1075
    if (configuration->listen_sock_count > 1) {
        configuration->listen_sock = config_clear_listener(configuration->listen_sock);
1076
1077
        configuration->listen_sock_count--;
    }
1078
1079
    if (configuration->port == 0)
        configuration->port = 8000;
1080

Marvin Scholz's avatar
Marvin Scholz committed
1081
1082
1083
1084
1085
1086
1087
    /* issue some warnings on bad configurations */
    if (!configuration->fileserve)
        ICECAST_LOG_WARN("Warning, serving of static files has been disabled "
            "in the config, this will also affect files used by the web "
            "interface (stylesheets, images).");

    __check_hostname(configuration);
1088

Marvin Scholz's avatar
Marvin Scholz committed
1089
1090
1091
1092
    if (!configuration->location ||
        strcmp(configuration->location, CONFIG_DEFAULT_LOCATION) == 0) {
        ICECAST_LOG_WARN("Warning, <location> not configured, using default "
            "value \"%s\".", CONFIG_DEFAULT_LOCATION);
1093
      if (!configuration->location)
Marvin Scholz's avatar
Marvin Scholz committed
1094
1095
          configuration->location = (char *) xmlCharStrdup(CONFIG_DEFAULT_LOCATION);
    }
1096

Marvin Scholz's avatar
Marvin Scholz committed
1097
1098
1099
    if (!configuration->admin ||
        strcmp(configuration->admin, CONFIG_DEFAULT_ADMIN) == 0) {
        ICECAST_LOG_WARN("Warning, <admin> contact not configured, using "
1100
            "default value \"%s\". This breaks YP directory listings. "
1101
1102
            "YP directory support will be disabled.", CONFIG_DEFAULT_ADMIN);
            /* FIXME actually disable YP */
1103
      if (!configuration->admin)
Marvin Scholz's avatar
Marvin Scholz committed
1104
          configuration->admin = (char *) xmlCharStrdup(CONFIG_DEFAULT_ADMIN);
1105
  }
1106
1107
}

Marvin Scholz's avatar
Marvin Scholz committed
1108
1109
1110
static void _parse_limits(xmlDocPtr     doc,
                          xmlNodePtr    node,
                          ice_config_t *configuration)
1111
1112
1113
{
    char *tmp;
    do {
Marvin Scholz's avatar
Marvin Scholz committed
1114
1115
1116
1117
        if (node == NULL)
            break;
        if (xmlIsBlankNode(node))
            continue;
1118

1119
        if (xmlStrcmp(node->name, XMLSTR("clients")) == 0) {
1120
            __read_int(doc, node, &configuration->client_limit, "<clients> must not be empty.");
1121
        } else if (xmlStrcmp(node->name, XMLSTR("sources")) == 0) {
1122
            __read_int(doc, node, &configuration->source_limit, "<sources> must not be empty.");
1123
        } else if (xmlStrcmp(node->name, XMLSTR("queue-size")) == 0) {
1124
            __read_unsigned_int(doc, node, &configuration->queue_size_limit, "<queue-size> must not be empty.");
1125
        } else if (xmlStrcmp(node->name, XMLSTR("threadpool")) == 0) {
1126
1127
            ICECAST_LOG_WARN("<threadpool> functionality was removed in Icecast"
			     " version 2.3.0, please remove this from your config.");
1128
        } else if (xmlStrcmp(node->name, XMLSTR("client-timeout")) == 0) {
1129
            __read_int(doc, node, &configuration->client_timeout, "<client-timeout> must not be empty.");
1130
        } else if (xmlStrcmp(node->name, XMLSTR("header-timeout")) == 0) {
1131
            __read_int(doc, node, &configuration->header_timeout, "<header-timeout> must not be empty.");
1132
        } else if (xmlStrcmp(node->name, XMLSTR("source-timeout")) == 0) {
1133
            __read_int(doc, node, &configuration->source_timeout, "<source-timeout> must not be empty.");
1134
        } else if (xmlStrcmp(node->name, XMLSTR("burst-on-connect")) == 0) {
1135
            ICECAST_LOG_WARN("<burst-on-connect> is deprecated, use <burst-size> instead.");
1136
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
1137
            if (util_str_to_int(tmp, 0) == 0)
Karl Heyes's avatar
Karl Heyes committed
1138
                configuration->burst_size = 0;
Marvin Scholz's avatar
Marvin Scholz committed
1139
1140
            if (tmp)
                xmlFree(tmp);
1141
        } else if (xmlStrcmp(node->name, XMLSTR("burst-size")) == 0) {
1142
            __read_unsigned_int(doc, node, &configuration->burst_size, "<burst-size> must not be empty.");
1143
1144
1145
1146
        }
    } while ((node = node->next));
}

Marvin Scholz's avatar
Marvin Scholz committed
1147
1148
1149
1150
1151
1152
1153
1154
1155
static void _parse_mount_oldstyle_authentication(mount_proxy    *mount,
                                                 xmlNodePtr      node,
                                                 auth_stack_t  **authstack)
{
     int        allow_duplicate_users = 1;
     auth_t    *auth;
     char      *type;
     char      *name;
     char      *value;
Philipp Schafft's avatar
Philipp Schafft committed
1156
1157
1158
1159
1160
     xmlNodePtr child;

     child = node->xmlChildrenNode;

     while (child) {
1161
1162
1163
         if (xmlStrcmp(child->name, XMLSTR("option")) == 0) {
             name = (char *)xmlGetProp(child, XMLSTR("name"));
             value = (char *)xmlGetProp(child, XMLSTR("value"));
Philipp Schafft's avatar
Philipp Schafft committed
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
             if (name && value) {
                 if (strcmp(name, "allow_duplicate_users") == 0) {
                     allow_duplicate_users = util_str_to_bool(value);
                 }
             }
             if (name)
                 xmlFree(name);
             if (value)
                 xmlFree(value);
         }
         child = child->next;
     }

     type = (char *)xmlGetProp(node, XMLSTR("type"));
     if (strcmp(type, AUTH_TYPE_HTPASSWD) == 0) {
         if (!allow_duplicate_users)
             xmlSetProp(node, XMLSTR("connections-per-user"), XMLSTR("0"));

         auth = auth_get_authenticator(node);
         if (auth) {
             auth_stack_push(authstack, auth);
             auth_release(auth);
         }

Marvin Scholz's avatar
Marvin Scholz committed
1188
1189
         __append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS,
             NULL, NULL, "get,head,post", NULL, 0, NULL);
Philipp Schafft's avatar
Philipp Schafft committed
1190
     } else if (strcmp(type, AUTH_TYPE_URL) == 0) {
1191
         /* This block is super fun! Attention! Super fun ahead! Ladies and Gentlemen take care and watch your children! */
Philipp Schafft's avatar
Philipp Schafft committed
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
         /* Stuff that was of help:
          * $ sed 's/^.*name="\([^"]*\)".*$/         const char *\1 = NULL;/'
          * $ sed 's/^.*name="\([^"]*\)".*$/         if (\1)\n             xmlFree(\1);/'
          * $ sed 's/^.*name="\([^"]*\)".*$/                     } else if (strcmp(name, "\1") == 0) {\n                         \1 = value;\n                         value = NULL;/'
          */
         /* urls */
         char *mount_add        = NULL;
         char *mount_remove     = NULL;
         char *listener_add     = NULL;
         char *listener_remove  = NULL;
         char *stream_auth      = NULL;
         /* request credentials */
         char *username         = NULL;
         char *password         = NULL;
         /* general options */
         char *auth_header      = NULL;
         char *timelimit_header = NULL;
         char *headers          = NULL;
         char *header_prefix    = NULL;

         child = node->xmlChildrenNode;
         while (child) {
1214
1215
1216
             if (xmlStrcmp(child->name, XMLSTR("option")) == 0) {
                 name = (char *)xmlGetProp(child, XMLSTR("name"));
                 value = (char *)xmlGetProp(child, XMLSTR("value"));
Philipp Schafft's avatar
Philipp Schafft committed
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265

                 if (name && value) {
                     if (strcmp(name, "mount_add") == 0) {
                         mount_add = value;
                         value = NULL;
                     } else if (strcmp(name, "mount_add") == 0) {
                         mount_add = value;
                         value = NULL;
                     } else if (strcmp(name, "mount_remove") == 0) {
                         mount_remove = value;
                         value = NULL;
                     } else if (strcmp(name, "listener_add") == 0) {
                         listener_add = value;
                         value = NULL;
                     } else if (strcmp(name, "listener_remove") == 0) {
                         listener_remove = value;
                         value = NULL;
                     } else if (strcmp(name, "username") == 0) {
                         username = value;
                         value = NULL;
                     } else if (strcmp(name, "password") == 0) {
                         password = value;
                         value = NULL;
                     } else if (strcmp(name, "auth_header") == 0) {
                         auth_header = value;
                         value = NULL;
                     } else if (strcmp(name, "timelimit_header") == 0) {
                         timelimit_header = value;
                         value = NULL;
                     } else if (strcmp(name, "headers") == 0) {
                         headers = value;
                         value = NULL;
                     } else if (strcmp(name, "header_prefix") == 0) {
                         header_prefix = value;
                         value = NULL;
                     } else if (strcmp(name, "stream_auth") == 0) {
                         stream_auth = value;
                         value = NULL;
                     }
                 }

                 if (name)
                     xmlFree(name);
                 if (value)
                     xmlFree(value);
             }
             child = child->next;
         }

1266
         if (mount_add)
Marvin Scholz's avatar
Marvin Scholz committed
1267
1268
             __append_old_style_url_event(&mount->event, "source-connect",
                 mount_add, "mount_add", username, password);
1269
         if (mount_remove)
Marvin Scholz's avatar
Marvin Scholz committed
1270
1271
1272
1273
1274
1275
1276
1277
1278
             __append_old_style_url_event(&mount->event, "source-disconnect",
                 mount_add, "mount_remove", username, password);

         __append_old_style_urlauth(authstack, listener_add, listener_remove,
             "listener_add", "listener_r