cfgfile.c 94.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
#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
#include "main.h"
46

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

388
    if (!stack || (!client_add && !client_remove))
Philipp Schafft's avatar
Philipp Schafft committed
389
390
391
392
393
394
395
396
397
398
399
400
401
        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 {
402
        xmlSetProp(role, XMLSTR("method"), XMLSTR("get,post,head,options"));
Philipp Schafft's avatar
Philipp Schafft committed
403
        xmlSetProp(role, XMLSTR("deny-method"), XMLSTR("*"));
404
        xmlSetProp(role, XMLSTR("allow-method"), XMLSTR("get,post,head,options"));
Philipp Schafft's avatar
Philipp Schafft committed
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
        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);
421
422
423
424
425
426
427
    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
428
429
430
431

    xmlFreeNode(role);
}

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

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

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

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

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

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

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

556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
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
571
listener_t *config_clear_listener(listener_t *listener)
572
573
574
575
576
{
    listener_t *next = NULL;
    if (listener)
    {
        next = listener->next;
577
578
        if (listener->bind_address)     xmlFree(listener->bind_address);
        if (listener->shoutcast_mount)  xmlFree(listener->shoutcast_mount);
579
580
581
582
        free (listener);
    }
    return next;
}
583

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

596
    free(c->config_filename);
597

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

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

627
628
    event_registration_release(c->event);

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

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

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

650
    config_clear_resource(c->resources);
651
652

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

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

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

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

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

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

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

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

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

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

/* MUST be called with the lock held! */
Marvin Scholz's avatar
Marvin Scholz committed
779
780
void config_set_config(ice_config_t *config)
{
781
782
783
784
785
786
787
788
789
790
    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
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);
811
812
    configuration
        ->shoutcast_user = (char *) xmlCharStrdup(CONFIG_DEFAULT_SHOUTCAST_USER);
Marvin Scholz's avatar
Marvin Scholz committed
813
814
815
816
817
818
819
820
821
    configuration
        ->fileserve  = CONFIG_DEFAULT_FILESERVE;
    configuration
        ->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
    configuration
        ->on_demand = 0;
    configuration
        ->dir_list = NULL;
    configuration
822
        ->hostname = (char *) xmlCharStrdup(CONFIG_DEFAULT_HOSTNAME);
Marvin Scholz's avatar
Marvin Scholz committed
823
    configuration
824
        ->mimetypes_fn = (char *) xmlCharStrdup(MIMETYPESFILE);
Marvin Scholz's avatar
Marvin Scholz committed
825
826
827
828
829
830
831
    configuration
        ->master_server = NULL;
    configuration
        ->master_server_port = 0;
    configuration
        ->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
    configuration
832
        ->master_username = (char *) xmlCharStrdup(CONFIG_DEFAULT_MASTER_USERNAME);
Marvin Scholz's avatar
Marvin Scholz committed
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
861
862
    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
863
    /* default to a typical prebuffer size used by clients */
Marvin Scholz's avatar
Marvin Scholz committed
864
865
    configuration
        ->burst_size = CONFIG_DEFAULT_BURST_SIZE;
866
867
    configuration->tls_context
        .cipher_list = (char *) xmlCharStrdup(CONFIG_DEFAULT_CIPHER_LIST);
868
869
}

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

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

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

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

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

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

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

Philipp Schafft's avatar
Philipp Schafft committed
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
    /* 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
1061
1062
1063
1064
1065
1066
1067
            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
1068
        } else {
1069
1070
            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
1071
        }
1072
        xmlFree(source_password);
Philipp Schafft's avatar
Philipp Schafft committed
1073
    }
1074
1075
    /* 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
1076
1077
    if (configuration->listen_sock_count > 1) {
        configuration->listen_sock = config_clear_listener(configuration->listen_sock);
1078
1079
        configuration->listen_sock_count--;
    }
1080
1081
    if (configuration->port == 0)
        configuration->port = 8000;
1082

Marvin Scholz's avatar
Marvin Scholz committed
1083
1084
1085
1086
1087
1088
1089
    /* 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);
1090

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

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

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

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

Marvin Scholz's avatar
Marvin Scholz committed
1149
1150
1151
1152
1153
1154
1155
1156
1157
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
1158
1159
1160
1161
1162
     xmlNodePtr child;

     child = node->xmlChildrenNode;

     while (child) {
1163
1164
1165
         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
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
             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
1190
         __append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS,
1191
             NULL, NULL, "get,head,post,options", NULL, 0, NULL);
Philipp Schafft's avatar
Philipp Schafft committed
1192
     } else if (strcmp(type, AUTH_TYPE_URL) == 0) {
1193
         /* 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
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
         /* 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) {
1216
1217
1218
             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
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
1266
1267

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