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

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

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

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

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

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

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

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

136
137
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
138
139
140
141
142

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

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

146
147
148
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
149
150
operation_mode config_str_to_omode(const char *str)
{
151
152
153
154
155
156
157
158
159
160
161
162
163
164
    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;
    } else {
        ICECAST_LOG_ERROR("Unknown operation mode \"%s\", falling back to DEFAULT.", str);
        return OMODE_DEFAULT;
    }
}

Marvin Scholz's avatar
Marvin Scholz committed
165
166
static void create_locks(void)
{
167
    thread_mutex_create(&_locks.relay_lock);
168
    thread_rwlock_create(&_locks.config_lock);
169
170
}

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

Marvin Scholz's avatar
Marvin Scholz committed
177
178
void config_initialize(void)
{
179
180
181
    create_locks();
}

Marvin Scholz's avatar
Marvin Scholz committed
182
183
void config_shutdown(void)
{
184
185
186
187
188
189
190
191
192
193
194
195
    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);
}

Marvin Scholz's avatar
Marvin Scholz committed
196
197
static inline int __parse_public(const char *str)
{
198
199
200
201
202
203
204
205
206
207
208
209
    /* 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);
}

Marvin Scholz's avatar
Marvin Scholz committed
210
211
212
213
214
215
216
217
218
219
220
221
222
223
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272

    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
273
274
275
276
static void __append_option_tag(xmlNodePtr  parent,
                                const char *name,
                                const char *value)
{
Philipp Schafft's avatar
Philipp Schafft committed
277
278
279
280
281
282
283
284
285
286
    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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
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
302

303
    if (!stack || (!client_add && !client_remove))
Philipp Schafft's avatar
Philipp Schafft committed
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
        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);
336
337
338
339
340
341
342
    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
343
344
345
346

    xmlFreeNode(role);
}

Marvin Scholz's avatar
Marvin Scholz committed
347
348
349
350
351
static void __append_old_style_exec_event(event_registration_t **list,
                                          const char            *trigger,
                                          const char            *executable)
{
    xmlNodePtr            exec;
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
    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
368
369
370
371
372
373
374
375
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;
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
    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
395
396
397
398
399
400
401
402
403
404
405
static void config_clear_http_header(ice_config_http_header_t *header)
{
    ice_config_http_header_t *old;
    
    while (header) {
        xmlFree(header->name);
        xmlFree(header->value);
        old = header;
        header = header->next;
        free(old);
    }
406
407
}

Marvin Scholz's avatar
Marvin Scholz committed
408
409
static inline ice_config_http_header_t *config_copy_http_header(ice_config_http_header_t *header)
{
410
411
412
413
414
415
416
417
418
419
420
421
422
423
    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
424
425
        if (!cur)
            return ret; /* TODO: do better error handling */
426

427
428
429
430
        cur->type   = header->type;
        cur->name   = (char *)xmlCharStrdup(header->name);
        cur->value  = (char *)xmlCharStrdup(header->value);
        cur->status = header->status;
431
432

        if (!cur->name || !cur->value) {
Marvin Scholz's avatar
Marvin Scholz committed
433
434
435
436
            if (cur->name)
                xmlFree(cur->name);
            if (cur->value)
                xmlFree(cur->value);
437
438
439
440
441
442
443
444
445
446
447
448
449
            if (old) {
                old->next = NULL;
            } else {
                ret = NULL;
            }
            free(cur);
            return ret;
        }
        header = header->next;
    }
    return ret;
}

450
static void config_clear_mount(mount_proxy *mount)
451
{
Marvin Scholz's avatar
Marvin Scholz committed
452
453
454
455
456
    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);
457
    if (mount->stream_description)  xmlFree (mount->stream_description);
Marvin Scholz's avatar
Marvin Scholz committed
458
459
460
461
462
    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);
463
    if (mount->cluster_password)    xmlFree (mount->cluster_password);
Marvin Scholz's avatar
Marvin Scholz committed
464
    if (mount->authstack)           auth_stack_release(mount->authstack);
465

466
    event_registration_release(mount->event);
467
    config_clear_http_header(mount->http_headers);
Marvin Scholz's avatar
Marvin Scholz committed
468
    free(mount);
469
470
}

Marvin Scholz's avatar
Marvin Scholz committed
471
listener_t *config_clear_listener(listener_t *listener)
472
473
474
475
476
477
{
    listener_t *next = NULL;
    if (listener)
    {
        next = listener->next;
        if (listener->bind_address)     xmlFree (listener->bind_address);
478
        if (listener->shoutcast_mount)  xmlFree (listener->shoutcast_mount);
479
480
481
482
        free (listener);
    }
    return next;
}
483

484
485
void config_clear(ice_config_t *c)
{
Marvin Scholz's avatar
Marvin Scholz committed
486
487
488
489
490
491
492
493
    ice_config_dir_t    *dirnode,
                        *nextdirnode;
    relay_server        *relay,
                        *nextrelay;
    mount_proxy         *mount,
                        *nextmount;
    aliases             *alias,
                        *nextalias;
494
#ifdef USE_YP
Marvin Scholz's avatar
Marvin Scholz committed
495
    int                 i;
496
#endif
497

498
    free(c->config_filename);
499

Marvin Scholz's avatar
Marvin Scholz committed
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
    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->cert_file)       xmlFree(c->cert_file);
    if (c->cipher_list)     xmlFree(c->cipher_list);
    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);
517
    if (c->shoutcast_mount) xmlFree(c->shoutcast_mount);
Philipp Schafft's avatar
Philipp Schafft committed
518
    if (c->authstack)       auth_stack_release(c->authstack);
Marvin Scholz's avatar
Marvin Scholz committed
519
    if (c->master_server)   xmlFree(c->master_server);
520
    if (c->master_username) xmlFree(c->master_username);
521
    if (c->master_password) xmlFree(c->master_password);
Marvin Scholz's avatar
Marvin Scholz committed
522
523
524
    if (c->user)            xmlFree(c->user);
    if (c->group)           xmlFree(c->group);
    if (c->mimetypes_fn)    xmlFree (c->mimetypes_fn);
525

526
527
    event_registration_release(c->event);

Marvin Scholz's avatar
Marvin Scholz committed
528
    while ((c->listen_sock = config_clear_listener (c->listen_sock)));
529

530
531
    thread_mutex_lock(&(_locks.relay_lock));
    relay = c->relay;
532
    while (relay) {
533
534
535
        nextrelay = relay->next;
        xmlFree(relay->server);
        xmlFree(relay->mount);
Karl Heyes's avatar
Karl Heyes committed
536
        xmlFree(relay->localmount);
537
538
539
540
541
542
        free(relay);
        relay = nextrelay;
    }
    thread_mutex_unlock(&(_locks.relay_lock));

    mount = c->mounts;
543
    while (mount) {
544
        nextmount = mount->next;
545
        config_clear_mount(mount);
546
547
548
549
        mount = nextmount;
    }

    alias = c->aliases;
550
    while (alias) {
551
552
553
554
        nextalias = alias->next;
        xmlFree(alias->source);
        xmlFree(alias->destination);
        xmlFree(alias->bind_address);
555
        xmlFree(alias->vhost);
556
557
558
559
560
        free(alias);
        alias = nextalias;
    }

    dirnode = c->dir_list;
561
    while (dirnode) {
562
563
564
565
566
        nextdirnode = dirnode->next;
        xmlFree(dirnode->host);
        free(dirnode);
        dirnode = nextdirnode;
    }
567
#ifdef USE_YP
Karl Heyes's avatar
Karl Heyes committed
568
    i = 0;
Marvin Scholz's avatar
Marvin Scholz committed
569
    while (i < c->num_yp_directories) {
Karl Heyes's avatar
Karl Heyes committed
570
571
572
573
        xmlFree (c->yp_url[i]);
        i++;
    }
#endif
574

575
    config_clear_http_header(c->http_headers);
576
577
578
    memset(c, 0, sizeof(ice_config_t));
}

Marvin Scholz's avatar
Marvin Scholz committed
579
580
581
void config_reread_config(void)
{
    int           ret;
582
    ice_config_t *config;
Marvin Scholz's avatar
Marvin Scholz committed
583
    ice_config_t  new_config;
584
585
586
587
588
589
590
591
592
593
594
    /* 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
595
            break;
596
597
            case CONFIG_ENOROOT:
                ICECAST_LOG_ERROR("Root element not found in %s", config->config_filename);
Marvin Scholz's avatar
Marvin Scholz committed
598
            break;
599
600
601
            case CONFIG_EBADROOT:
                ICECAST_LOG_ERROR("Not an icecast2 config file: %s",
                        config->config_filename);
Marvin Scholz's avatar
Marvin Scholz committed
602
            break;
603
604
            default:
                ICECAST_LOG_ERROR("Parse error in reading %s", config->config_filename);
Marvin Scholz's avatar
Marvin Scholz committed
605
            break;
606
607
608
609
610
611
        }
        config_release_config();
    } else {
        config_clear(config);
        config_set_config(&new_config);
        config = config_get_config_unlocked();
Marvin Scholz's avatar
Marvin Scholz committed
612
613
614
615
        restart_logging(config);
        yp_recheck_config(config);
        fserve_recheck_mime_types(config);
        stats_global(config);
616
617
618
619
620
        config_release_config();
        slave_update_all_mounts();
    }
}

621
622
623
624
625
626
627
628
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
629
    xmlDocPtr  doc;
630
631
    xmlNodePtr node;

Marvin Scholz's avatar
Marvin Scholz committed
632
633
    if (filename == NULL || strcmp(filename, "") == 0)
        return CONFIG_EINSANE;
634

635
    doc = xmlParseFile(filename);
Marvin Scholz's avatar
Marvin Scholz committed
636
    if (doc == NULL)
637
638
639
640
641
642
643
        return CONFIG_EPARSE;
    node = xmlDocGetRootElement(doc);
    if (node == NULL) {
        xmlFreeDoc(doc);
        return CONFIG_ENOROOT;
    }

644
    if (xmlStrcmp (node->name, XMLSTR("icecast")) != 0) {
645
646
647
648
649
        xmlFreeDoc(doc);
        return CONFIG_EBADROOT;
    }

    config_init_configuration(configuration);
Marvin Scholz's avatar
Marvin Scholz committed
650
    configuration->config_filename = strdup(filename);
651
652
    _parse_root(doc, node->xmlChildrenNode, configuration);
    xmlFreeDoc(doc);
653
    _merge_mounts_all(configuration);
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
    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)
{
669
    thread_rwlock_unlock(&(_locks.config_lock));
670
671
672
673
}

ice_config_t *config_get_config(void)
{
674
675
676
677
678
679
680
    thread_rwlock_rlock(&(_locks.config_lock));
    return &_current_configuration;
}

ice_config_t *config_grab_config(void)
{
    thread_rwlock_wlock(&(_locks.config_lock));
681
682
683
684
    return &_current_configuration;
}

/* MUST be called with the lock held! */
Marvin Scholz's avatar
Marvin Scholz committed
685
686
void config_set_config(ice_config_t *config)
{
687
688
689
690
691
692
693
694
695
696
    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
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
    configuration
        ->location = (char *) xmlCharStrdup(CONFIG_DEFAULT_LOCATION);
    configuration
        ->server_id = (char *) xmlCharStrdup(ICECAST_VERSION_STRING);
    configuration
        ->admin = (char *) xmlCharStrdup(CONFIG_DEFAULT_ADMIN);
    configuration
        ->client_limit = CONFIG_DEFAULT_CLIENT_LIMIT;
    configuration
        ->source_limit = CONFIG_DEFAULT_SOURCE_LIMIT;
    configuration
        ->queue_size_limit = CONFIG_DEFAULT_QUEUE_SIZE_LIMIT;
    configuration
        ->client_timeout = CONFIG_DEFAULT_CLIENT_TIMEOUT;
    configuration
        ->header_timeout = CONFIG_DEFAULT_HEADER_TIMEOUT;
    configuration
        ->source_timeout = CONFIG_DEFAULT_SOURCE_TIMEOUT;
    configuration
        ->shoutcast_mount = (char *) xmlCharStrdup(CONFIG_DEFAULT_SHOUTCAST_MOUNT);
    configuration
        ->fileserve  = CONFIG_DEFAULT_FILESERVE;
    configuration
        ->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
    configuration
        ->on_demand = 0;
    configuration
        ->dir_list = NULL;
    configuration
        ->hostname = (char *) xmlCharStrdup (CONFIG_DEFAULT_HOSTNAME);
    configuration
        ->mimetypes_fn = (char *) xmlCharStrdup (MIMETYPESFILE);
    configuration
        ->master_server = NULL;
    configuration
        ->master_server_port = 0;
    configuration
        ->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
    configuration
        ->master_username = (char *) xmlCharStrdup (CONFIG_DEFAULT_MASTER_USERNAME);
    configuration
        ->master_password = NULL;
    configuration
        ->base_dir = (char *) xmlCharStrdup(CONFIG_DEFAULT_BASE_DIR);
    configuration
        ->log_dir = (char *) xmlCharStrdup(CONFIG_DEFAULT_LOG_DIR);
    configuration
        ->cipher_list = (char *) xmlCharStrdup(CONFIG_DEFAULT_CIPHER_LIST);
    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
769
    /* default to a typical prebuffer size used by clients */
Marvin Scholz's avatar
Marvin Scholz committed
770
771
    configuration
        ->burst_size = CONFIG_DEFAULT_BURST_SIZE;
772
773
}

Marvin Scholz's avatar
Marvin Scholz committed
774
775
static inline void __check_hostname(ice_config_t *configuration)
{
776
777
    char *p;

778
    /* ensure we have a non-NULL buffer: */
779
780
781
    if (!configuration->hostname)
        configuration->hostname = (char *)xmlCharStrdup (CONFIG_DEFAULT_HOSTNAME);

782
    /* convert to lower case: */
Marvin Scholz's avatar
Marvin Scholz committed
783
    for (p = configuration->hostname; *p; p++) {
784
785
        if ( *p >= 'A' && *p <= 'Z' )
            *p += 'a' - 'A';
Marvin Scholz's avatar
Marvin Scholz committed
786
    }
787
788
789
790
791
792
793

    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
794
795
            ICECAST_LOG_ERROR("Can not check hostname \"%s\".",
                configuration->hostname);
796
797
        break;
        case HOSTCHECK_NOT_FQDN:
Marvin Scholz's avatar
Marvin Scholz committed
798
            ICECAST_LOG_WARN("Warning, <hostname> seems not to be set to a "
799
                "fully qualified domain name (FQDN). This may cause problems, "
Marvin Scholz's avatar
Marvin Scholz committed
800
                "e.g. with YP directory listings.");
801
802
        break;
        case HOSTCHECK_IS_LOCALHOST:
Marvin Scholz's avatar
Marvin Scholz committed
803
            ICECAST_LOG_WARN("Warning, <hostname> not configured, using "
804
805
806
807
                "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 */
808
809
        break;
        case HOSTCHECK_IS_IPV4:
Marvin Scholz's avatar
Marvin Scholz committed
810
811
812
            ICECAST_LOG_WARN("Warning, <hostname> seems to be set to an IPv4 "
                "address. This may cause problems, e.g. with YP directory "
                "listings.");
813
814
        break;
        case HOSTCHECK_IS_IPV6:
Marvin Scholz's avatar
Marvin Scholz committed
815
816
817
            ICECAST_LOG_WARN("Warning, <hostname> seems to be set to an IPv6 "
                "address. This may cause problems, e.g. with YP directory "
                "listings.");
818
819
        break;
        case HOSTCHECK_BADCHAR:
820
            ICECAST_LOG_WARN("Warning, <hostname> contains unusual "
Marvin Scholz's avatar
Marvin Scholz committed
821
822
                "characters. This may cause problems, e.g. with YP directory "
                "listings.");
823
824
825
826
        break;
    }
}

Marvin Scholz's avatar
Marvin Scholz committed
827
828
829
static void _parse_root(xmlDocPtr       doc,
                        xmlNodePtr      node,
                        ice_config_t   *configuration)
830
831
{
    char *tmp;
Philipp Schafft's avatar
Philipp Schafft committed
832
    char *source_password = NULL;
833

Marvin Scholz's avatar
Marvin Scholz committed
834
835
836
837
838
839
    configuration
        ->listen_sock       = calloc (1, sizeof (*configuration->listen_sock));
    configuration
        ->listen_sock->port = 8000;
    configuration
        ->listen_sock_count = 1;
840

841
    do {
Marvin Scholz's avatar
Marvin Scholz committed
842
843
844
845
846
847
848
        if (node == NULL)
            break;
        if (xmlIsBlankNode(node))
            continue;
        if (xmlStrcmp(node->name, XMLSTR("location")) == 0) {
            if (configuration->location)
                xmlFree(configuration->location);
849
            configuration->location = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
850
        } else if (xmlStrcmp (node->name, XMLSTR("admin")) == 0) {
Marvin Scholz's avatar
Marvin Scholz committed
851
852
            if (configuration->admin)
                xmlFree(configuration->admin);
853
            configuration->admin = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
854
        } else if (xmlStrcmp (node->name, XMLSTR("server-id")) == 0) {
Marvin Scholz's avatar
Marvin Scholz committed
855
            xmlFree(configuration->server_id);
856
            configuration->server_id = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
857
858
859
860
            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
861
            _parse_authentication(doc, node->xmlChildrenNode, configuration, &source_password);
Marvin Scholz's avatar
Marvin Scholz committed
862
        } else if (xmlStrcmp(node->name, XMLSTR("source-password")) == 0) {
863
            /* TODO: This is the backwards-compatibility location */
Marvin Scholz's avatar
Marvin Scholz committed
864
865
            ICECAST_LOG_WARN("<source-password> defined outside "
                "<authentication>. This is deprecated and will be removed in "
866
867
                "version 2.X.0");
	    /* FIXME Settle target version for removal of this functionality! */
Philipp Schafft's avatar
Philipp Schafft committed
868
869
870
            if (source_password)
                xmlFree(source_password);
            source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
871
        } else if (xmlStrcmp(node->name, XMLSTR("icelogin")) == 0) {
872
            ICECAST_LOG_ERROR("<icelogin> support has been removed.");
Marvin Scholz's avatar
Marvin Scholz committed
873
        } else if (xmlStrcmp(node->name, XMLSTR("fileserve")) == 0) {
874
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
875
            configuration->fileserve = util_str_to_bool(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
876
877
878
            if (tmp)
                xmlFree(tmp);
        } else if (xmlStrcmp(node->name, XMLSTR("relays-on-demand")) == 0) {
879
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
880
            configuration->on_demand = util_str_to_bool(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
881
882
883
884
885
            if (tmp)
                xmlFree(tmp);
        } else if (xmlStrcmp(node->name, XMLSTR("hostname")) == 0) {
            if (configuration->hostname)
                xmlFree(configuration->hostname);
886
            configuration->hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
887
888
889
        } else if (xmlStrcmp(node->name, XMLSTR("mime-types")) == 0) {
            if (configuration->mimetypes_fn)
                xmlFree(configuration->mimetypes_fn);
890
            configuration->mimetypes_fn = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
891
        } else if (xmlStrcmp(node->name, XMLSTR("listen-socket")) == 0) {
892
            _parse_listen_socket(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
893
        } else if (xmlStrcmp(node->name, XMLSTR("port")) == 0) {
894
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
895
896
897
898
899
            if (tmp) {
                configuration->port = atoi(tmp);
                configuration->listen_sock->port = atoi(tmp);
                xmlFree(tmp);
            } else {
900
                ICECAST_LOG_WARN("<port> setting must not be empty.");
901
            }
Marvin Scholz's avatar
Marvin Scholz committed
902
        } else if (xmlStrcmp(node->name, XMLSTR("bind-address")) == 0) {
903
            if (configuration->listen_sock->bind_address)
904
905
                xmlFree(configuration->listen_sock->bind_address);
            configuration->listen_sock->bind_address = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
906
907
908
        } else if (xmlStrcmp(node->name, XMLSTR("master-server")) == 0) {
            if (configuration->master_server)
                xmlFree(configuration->master_server);
909
            configuration->master_server = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
910
911
912
        } else if (xmlStrcmp(node->name, XMLSTR("master-username")) == 0) {
            if (configuration->master_username)
                xmlFree(configuration->master_username);
913
            configuration->master_username = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
914
915
916
        } else if (xmlStrcmp(node->name, XMLSTR("master-password")) == 0) {
            if (configuration->master_password)
                xmlFree(configuration->master_password);
917
            configuration->master_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
918
        } else if (xmlStrcmp(node->name, XMLSTR("master-server-port")) == 0) {
919
920
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->master_server_port = atoi(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
921
922
            xmlFree(tmp);
        } else if (xmlStrcmp(node->name, XMLSTR("master-update-interval")) == 0) {
923
924
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->master_update_interval = atoi(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
925
926
927
928
            xmlFree(tmp);
        } else if (xmlStrcmp(node->name, XMLSTR("shoutcast-mount")) == 0) {
            if (configuration->shoutcast_mount)
                xmlFree(configuration->shoutcast_mount);
929
            configuration->shoutcast_mount = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Marvin Scholz's avatar
Marvin Scholz committed
930
        } else if (xmlStrcmp(node->name, XMLSTR("limits")) == 0) {
931
            _parse_limits(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
932
        } else if (xmlStrcmp(node->name, XMLSTR("http-headers")) == 0) {
933
            _parse_http_headers(doc, node->xmlChildrenNode, &(configuration->http_headers));
Marvin Scholz's avatar
Marvin Scholz committed
934
        } else if (xmlStrcmp(node->name, XMLSTR("relay")) == 0) {
935
            _parse_relay(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
936
        } else if (xmlStrcmp(node->name, XMLSTR("mount")) == 0) {
937
            _parse_mount(doc, node, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
938
        } else if (xmlStrcmp(node->name, XMLSTR("directory")) == 0) {
939
            _parse_directory(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
940
        } else if (xmlStrcmp(node->name, XMLSTR("paths")) == 0) {
941
            _parse_paths(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
942
        } else if (xmlStrcmp(node->name, XMLSTR("logging")) == 0) {
943
            _parse_logging(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
944
        } else if (xmlStrcmp(node->name, XMLSTR("security")) == 0) {
945
            _parse_security(doc, node->xmlChildrenNode, configuration);
Marvin Scholz's avatar
Marvin Scholz committed
946
        } else if (xmlStrcmp(node->name, XMLSTR("kartoffelsalat")) == 0) {
947
948
            /* BEFORE RELEASE NEXT REVIEW: Should this tag really be <kartoffelsalat>? */
            _parse_events(&configuration->event, node->xmlChildrenNode);
949
950
        }
    } while ((node = node->next));
951

Philipp Schafft's avatar
Philipp Schafft committed
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
    /* 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
968
969
970
971
972
973
974
            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
975
        } else {
976
977
            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
978
        }
979
        xmlFree(source_password);
Philipp Schafft's avatar
Philipp Schafft committed
980
    }
981
982
    /* 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
983
984
    if (configuration->listen_sock_count > 1) {
        configuration->listen_sock = config_clear_listener(configuration->listen_sock);
985
986
        configuration->listen_sock_count--;
    }
987
988
    if (configuration->port == 0)
        configuration->port = 8000;
989

Marvin Scholz's avatar
Marvin Scholz committed
990
991
992
993
994
995
996
997
998
999
1000
1001
    /* 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);
    
    if (!configuration->location ||
        strcmp(configuration->location, CONFIG_DEFAULT_LOCATION) == 0) {
        ICECAST_LOG_WARN("Warning, <location> not configured, using default "
            "value \"%s\".", CONFIG_DEFAULT_LOCATION);
1002
      if (!configuration->location)
Marvin Scholz's avatar
Marvin Scholz committed
1003
1004
1005
1006
1007
1008
          configuration->location = (char *) xmlCharStrdup(CONFIG_DEFAULT_LOCATION);
    }
    
    if (!configuration->admin ||
        strcmp(configuration->admin, CONFIG_DEFAULT_ADMIN) == 0) {
        ICECAST_LOG_WARN("Warning, <admin> contact not configured, using "
1009
1010
1011
            "default value \"%s\". This breaks YP directory listings."
            "YP directory support will be disabled.", CONFIG_DEFAULT_ADMIN);
            /* FIXME actually disable YP */
1012
      if (!configuration->admin)
Marvin Scholz's avatar
Marvin Scholz committed
1013
          configuration->admin = (char *) xmlCharStrdup(CONFIG_DEFAULT_ADMIN);
1014
  }
1015
1016
}

Marvin Scholz's avatar
Marvin Scholz committed
1017
1018
1019
static void _parse_limits(xmlDocPtr     doc,
                          xmlNodePtr    node,
                          ice_config_t *configuration)
1020
1021
1022
{
    char *tmp;
    do {
Marvin Scholz's avatar
Marvin Scholz committed
1023
1024
1025
1026
        if (node == NULL)
            break;
        if (xmlIsBlankNode(node))
            continue;
1027

1028
        if (xmlStrcmp (node->name, XMLSTR("clients")) == 0) {
1029
1030
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->client_limit = atoi(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
1031
1032
            if (tmp)
                xmlFree(tmp);
1033
        } else if (xmlStrcmp (node->name, XMLSTR("sources")) == 0) {
1034
1035
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->source_limit = atoi(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
1036
1037
            if (tmp)
                xmlFree(tmp);
1038
        } else if (xmlStrcmp (node->name, XMLSTR("queue-size")) == 0) {
1039
1040
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->queue_size_limit = atoi(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
1041
1042
            if (tmp)
                xmlFree(tmp);
1043
        } else if (xmlStrcmp (node->name, XMLSTR("threadpool")) == 0) {
1044
1045
            ICECAST_LOG_WARN("<threadpool> functionality was removed in Icecast"
			     " version 2.3.0, please remove this from your config.");
1046
        } else if (xmlStrcmp (node->name, XMLSTR("client-timeout")) == 0) {
1047
1048
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->client_timeout = atoi(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
1049
1050
            if (tmp)
                xmlFree(tmp);
1051
        } else if (xmlStrcmp (node->name, XMLSTR("header-timeout")) == 0) {
1052
1053
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->header_timeout = atoi(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
1054
1055
            if (tmp)
                xmlFree(tmp);
1056
        } else if (xmlStrcmp (node->name, XMLSTR("source-timeout")) == 0) {
1057
1058
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->source_timeout = atoi(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
1059
1060
            if (tmp)
                xmlFree(tmp);
1061
        } else if (xmlStrcmp (node->name, XMLSTR("burst-on-connect")) == 0) {
1062
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Karl Heyes's avatar
Karl Heyes committed
1063
1064
            if (atoi(tmp) == 0)
                configuration->burst_size = 0;
Marvin Scholz's avatar
Marvin Scholz committed
1065
1066
            if (tmp)
                xmlFree(tmp);
1067
        } else if (xmlStrcmp (node->name, XMLSTR("burst-size")) == 0) {
Karl Heyes's avatar
Karl Heyes committed
1068
1069
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->burst_size = atoi(tmp);
Marvin Scholz's avatar
Marvin Scholz committed
1070
1071
            if (tmp)
                xmlFree(tmp);
1072
1073
1074
1075
        }
    } while ((node = node->next));
}

Marvin Scholz's avatar
Marvin Scholz committed
1076
1077
1078
1079
1080
1081
1082
1083
1084
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
1085
1086
1087
1088
1089
     xmlNodePtr child;

     child = node->xmlChildrenNode;

     while (child) {
1090
1091
1092
         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
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
             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
1117
1118
         __append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS,
             NULL, NULL, "get,head,post", NULL, 0, NULL);
Philipp Schafft's avatar
Philipp Schafft committed
1119
     } else if (strcmp(type, AUTH_TYPE_URL) == 0) {
1120
         /* 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
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
         /* 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) {
1143
1144
1145
             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
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194

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

1195
         if (mount_add)
Marvin Scholz's avatar
Marvin Scholz committed
1196
1197
             __append_old_style_url_event(&mount->event, "source-connect",
                 mount_add, "mount_add", username, password);
1198
         if (mount_remove)
Marvin Scholz's avatar
Marvin Scholz committed
1199
1200
1201
1202
1203
1204
1205
1206
1207
             __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_remove", username, password, 0,
             auth_header, timelimit_header, headers, header_prefix);
         __append_old_style_urlauth(authstack, stream_auth, NULL, "stream_auth",
             NULL, username, password, 1, auth_header, timelimit_header,
             headers, header_prefix);
Philipp Schafft's avatar
Philipp Schafft committed
1208
         if (listener_add)
Marvin Scholz's avatar
Marvin Scholz committed
1209
1210
             __append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL,
                 NULL, "get,put,head", NULL, 0, NULL);
Philipp Schafft's avatar
Philipp Schafft committed
1211
         if (stream_auth)
Marvin Scholz's avatar
Marvin Scholz committed
1212
1213
             __append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL,
                 NULL, "source,put", NULL, 0, NULL);
Philipp Schafft's avatar
Philipp Schafft committed
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237

         if (mount_add)
             xmlFree(mount_add);
         if (mount_remove)
             xmlFree(mount_remove);
         if (listener_add)
             xmlFree(listener_add);
         if (listener_remove)
             xmlFree(listener_remove);
         if (username)
             xmlFree(username);
         if (password)
             xmlFree(password);
         if (auth_header)
             xmlFree(auth_header);
         if (timelimit_header)
             xmlFree(timelimit_header);
         if (headers)
             xmlFree(headers);
         if (header_prefix)
             xmlFree(header_prefix);
         if (stream_auth)
             xmlFree(stream_auth);
     } else {
Marvin Scholz's avatar
Marvin Scholz committed
1238
         ICECAST_LOG_ERROR("Unknown authentication type in legacy mode. "
1239
             "Anonymous listeners and global login for sources disabled.");
Marvin Scholz's avatar
Marvin Scholz committed
1240
1241
         __append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL,
             NULL, NULL, NULL, 0, NULL);
Philipp Schafft's avatar
Philipp Schafft committed
1242
1243
1244
1245
     }
     xmlFree(type);
}

Marvin Scholz's avatar
Marvin Scholz committed
1246
1247
1248
static void _parse_mount(xmlDocPtr      doc,
                         xmlNodePtr     node,
                         ice_config_t  *configuration)
1249
{
Marvin Scholz's avatar
Marvin Scholz committed
1250
1251
1252
1253
1254
1255
1256
    char         *tmp;
    mount_proxy  *mount      = calloc(1, sizeof(mount_proxy));
    mount_proxy  *current    = configuration->mounts;
    mount_proxy  *last       = NULL;
    char         *username   = NULL;
    char         *password   = NULL;
    auth_stack_t *authstack  = NULL;
1257

Karl Heyes's avatar
Karl Heyes committed
1258
    /* default <mount> settings */
Marvin Scholz's avatar
Marvin Scholz committed
1259
1260
1261
1262
1263
1264
    mount->mounttype            = MOUNT_TYPE_NORMAL;
    mount->max_listeners        = -1;
    mount->burst_size           = -1;
    mount->mp3_meta_interval    = -1;
    mount->yp_public            = -1;
    mount->next                 = NULL;
1265

1266
1267
1268
    tmp = (char *)xmlGetProp(node, XMLSTR("type"));
    if (tmp) {
        if (strcmp(tmp, "normal") == 0) {
1269
            mount->mounttype = MOUNT_TYPE_NORMAL;
Marvin Scholz's avatar
Marvin Scholz committed
1270
        } else if (strcmp(tmp, "default") == 0) {
1271
            mount->mounttype = MOUNT_TYPE_DEFAULT;
Marvin Scholz's avatar
Marvin Scholz committed
1272
        } else {
1273
            ICECAST_LOG_WARN("Unknown mountpoint type: %s", tmp);
Marvin Scholz's avatar
Marvin Scholz committed
1274
            config_clear_mount(mount);
1275
            return;
1276
1277
        }
        xmlFree(tmp);
1278
1279
1280
1281
    }

    node = node->xmlChildrenNode;

1282
    do {
Marvin Scholz's avatar
Marvin Scholz committed
1283
1284
1285
1286
        if (node == NULL)
            break;
        if (xmlIsBlankNode(node))
            continue;
Karl Heyes's avatar