cfgfile.c 76.6 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 33
#include "cfgfile.h"
#include "refbuf.h"
#include "client.h"
#include "logging.h" 
34
#include "util.h" 
Philipp Schafft's avatar
Philipp Schafft committed
35
#include "auth.h" 
36

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

42 43 44 45 46
#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
47
#define CONFIG_DEFAULT_QUEUE_SIZE_LIMIT (500*1024)
48
#define CONFIG_DEFAULT_BURST_SIZE (64*1024)
49 50 51 52
#define CONFIG_DEFAULT_THREADPOOL_SIZE 4
#define CONFIG_DEFAULT_CLIENT_TIMEOUT 30
#define CONFIG_DEFAULT_HEADER_TIMEOUT 15
#define CONFIG_DEFAULT_SOURCE_TIMEOUT 10
53
#define CONFIG_DEFAULT_MASTER_USERNAME "relay"
54
#define CONFIG_DEFAULT_SHOUTCAST_MOUNT "/stream"
55 56 57
#define CONFIG_DEFAULT_FILESERVE 1
#define CONFIG_DEFAULT_TOUCH_FREQ 5
#define CONFIG_DEFAULT_HOSTNAME "localhost"
58
#define CONFIG_DEFAULT_PLAYLIST_LOG NULL
59 60
#define CONFIG_DEFAULT_ACCESS_LOG "access.log"
#define CONFIG_DEFAULT_ERROR_LOG "error.log"
61
#define CONFIG_DEFAULT_LOG_LEVEL 3
62 63 64 65 66 67
#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
68
#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"
69 70 71 72 73 74

#ifndef _WIN32
#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"
75
#define CONFIG_DEFAULT_NULL_FILE "/dev/null"
76
#define MIMETYPESFILE "/etc/mime.types"
77 78 79 80 81
#else
#define CONFIG_DEFAULT_BASE_DIR ".\\"
#define CONFIG_DEFAULT_LOG_DIR ".\\logs"
#define CONFIG_DEFAULT_WEBROOT_DIR ".\\webroot"
#define CONFIG_DEFAULT_ADMINROOT_DIR ".\\admin"
82
#define CONFIG_DEFAULT_NULL_FILE "nul:"
83
#define MIMETYPESFILE ".\\mime.types"
84 85 86 87 88 89 90 91 92 93 94 95 96
#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);
static void _parse_authentication(xmlDocPtr doc, xmlNodePtr node, 
Philipp Schafft's avatar
Philipp Schafft committed
97
        ice_config_t *c, char **source_password);
98 99
static void _parse_http_headers(xmlDocPtr doc, xmlNodePtr node,
        ice_config_http_header_t **http_headers);
100 101 102 103 104 105
static void _parse_relay(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);
static void _parse_listen_socket(xmlDocPtr doc, xmlNodePtr node, 
        ice_config_t *c);
static void _add_server(xmlDocPtr doc, xmlNodePtr node, ice_config_t *c);

106 107 108
static void merge_mounts(mount_proxy * dst, mount_proxy * src);
static inline void _merge_mounts_all(ice_config_t *c);

109
static void create_locks(void) {
110
    thread_mutex_create(&_locks.relay_lock);
111
    thread_rwlock_create(&_locks.config_lock);
112 113
}

114
static void release_locks(void) {
115
    thread_mutex_destroy(&_locks.relay_lock);
116
    thread_rwlock_destroy(&_locks.config_lock);
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
}

void config_initialize(void) {
    create_locks();
}

void config_shutdown(void) {
    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);
}

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
static inline int __parse_public(const char *str) {
    /* 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);
}

static inline int __parse_loglevel(const char *str) {
    if (strcasecmp(str, "debug") == 0 || strcasecmp(str, "DBUG") == 0)
        return 4;
    if (strcasecmp(str, "information") == 0 || strcasecmp(str, "INFO") == 0)
        return 3;
    if (strcasecmp(str, "warning") == 0 || strcasecmp(str, "WARN") == 0)
        return 2;
    if (strcasecmp(str, "error") == 0 || strcasecmp(str, "EROR") == 0)
        return 1;

    /* gussing it is old-style numerical setting */
    return atoi(str);
}

Philipp Schafft's avatar
Philipp Schafft committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 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
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;

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

static void __append_option_tag (xmlNodePtr parent, const char *name, const char *value) {
    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));
}

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;

    if (!stack || !client_add || !!client_remove)
        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);
    auth_stack_push(stack, auth);
    auth_release(auth);

    xmlFreeNode(role);
}

269 270 271 272 273 274 275 276 277 278 279 280
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);
 }
}

281
static inline ice_config_http_header_t * config_copy_http_header(ice_config_http_header_t *header) {
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
    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;
        }

        if (!cur) return ret; /* TODO: do better error handling */

298 299 300 301
        cur->type   = header->type;
        cur->name   = (char *)xmlCharStrdup(header->name);
        cur->value  = (char *)xmlCharStrdup(header->value);
        cur->status = header->status;
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

        if (!cur->name || !cur->value) {
            if (cur->name) xmlFree(cur->name);
            if (cur->value) xmlFree(cur->value);
            if (old) {
                old->next = NULL;
            } else {
                ret = NULL;
            }
            free(cur);
            return ret;
        }

        header = header->next;
    }

    return ret;
}

321
static void config_clear_mount(mount_proxy *mount)
322
{
323 324 325 326 327 328 329 330 331 332 333 334 335 336
    if (mount->mountname)       xmlFree (mount->mountname);
    if (mount->dumpfile)        xmlFree (mount->dumpfile);
    if (mount->intro_filename)  xmlFree (mount->intro_filename);
    if (mount->on_connect)      xmlFree (mount->on_connect);
    if (mount->on_disconnect)   xmlFree (mount->on_disconnect);
    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);
Philipp Schafft's avatar
Philipp Schafft committed
337
    if (mount->authstack)       auth_stack_release(mount->authstack);
338

339
    config_clear_http_header(mount->http_headers);
340 341 342
    free (mount);
}

343 344 345 346 347 348 349
listener_t *config_clear_listener (listener_t *listener)
{
    listener_t *next = NULL;
    if (listener)
    {
        next = listener->next;
        if (listener->bind_address)     xmlFree (listener->bind_address);
350
        if (listener->shoutcast_mount)  xmlFree (listener->shoutcast_mount);
351 352 353 354
        free (listener);
    }
    return next;
}
355

356 357 358 359 360 361
void config_clear(ice_config_t *c)
{
    ice_config_dir_t *dirnode, *nextdirnode;
    relay_server *relay, *nextrelay;
    mount_proxy *mount, *nextmount;
    aliases *alias, *nextalias;
362
#ifdef USE_YP
363
    int i;
364
#endif
365

366
    free(c->config_filename);
367

Karl Heyes's avatar
Karl Heyes committed
368
    xmlFree (c->server_id);
369 370 371 372 373 374 375
    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);
Karl Heyes's avatar
Karl Heyes committed
376
    if (c->cert_file) xmlFree(c->cert_file);
377
    if (c->cipher_list) xmlFree(c->cipher_list);
Karl Heyes's avatar
Karl Heyes committed
378
    if (c->pidfile)
379
        xmlFree(c->pidfile);
380 381
    if (c->banfile) xmlFree(c->banfile);
    if (c->allowfile) xmlFree(c->allowfile);
382 383 384 385
    if (c->playlist_log) xmlFree(c->playlist_log);
    if (c->access_log) xmlFree(c->access_log);
    if (c->error_log) xmlFree(c->error_log);
    if (c->shoutcast_mount) xmlFree(c->shoutcast_mount);
Philipp Schafft's avatar
Philipp Schafft committed
386
    if (c->authstack)       auth_stack_release(c->authstack);
387
    if (c->master_server) xmlFree(c->master_server);
388
    if (c->master_username) xmlFree(c->master_username);
389 390 391
    if (c->master_password) xmlFree(c->master_password);
    if (c->user) xmlFree(c->user);
    if (c->group) xmlFree(c->group);
392
    if (c->mimetypes_fn) xmlFree (c->mimetypes_fn);
393

394 395 396
    while ((c->listen_sock = config_clear_listener (c->listen_sock)))
        ;

397 398
    thread_mutex_lock(&(_locks.relay_lock));
    relay = c->relay;
399
    while (relay) {
400 401 402
        nextrelay = relay->next;
        xmlFree(relay->server);
        xmlFree(relay->mount);
Karl Heyes's avatar
Karl Heyes committed
403
        xmlFree(relay->localmount);
404 405 406 407 408 409
        free(relay);
        relay = nextrelay;
    }
    thread_mutex_unlock(&(_locks.relay_lock));

    mount = c->mounts;
410
    while (mount) {
411
        nextmount = mount->next;
412
        config_clear_mount(mount);
413 414 415 416
        mount = nextmount;
    }

    alias = c->aliases;
417
    while (alias) {
418 419 420 421
        nextalias = alias->next;
        xmlFree(alias->source);
        xmlFree(alias->destination);
        xmlFree(alias->bind_address);
422
        xmlFree(alias->vhost);
423 424 425 426 427
        free(alias);
        alias = nextalias;
    }

    dirnode = c->dir_list;
428
    while (dirnode) {
429 430 431 432 433
        nextdirnode = dirnode->next;
        xmlFree(dirnode->host);
        free(dirnode);
        dirnode = nextdirnode;
    }
434
#ifdef USE_YP
Karl Heyes's avatar
Karl Heyes committed
435
    i = 0;
436
    while (i < c->num_yp_directories)
Karl Heyes's avatar
Karl Heyes committed
437 438 439 440 441
    {
        xmlFree (c->yp_url[i]);
        i++;
    }
#endif
442

443 444
    config_clear_http_header(c->http_headers);

445 446 447
    memset(c, 0, sizeof(ice_config_t));
}

448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
void config_reread_config(void) {
    int ret;
    ice_config_t *config;
    ice_config_t new_config;
    /* 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");
                break;
            case CONFIG_ENOROOT:
                ICECAST_LOG_ERROR("Root element not found in %s", config->config_filename);
                break;
            case CONFIG_EBADROOT:
                ICECAST_LOG_ERROR("Not an icecast2 config file: %s",
                        config->config_filename);
                break;
            default:
                ICECAST_LOG_ERROR("Parse error in reading %s", config->config_filename);
                break;
        }
        config_release_config();
    } else {
        config_clear(config);
        config_set_config(&new_config);
        config = config_get_config_unlocked();
        restart_logging (config);
        yp_recheck_config (config);
        fserve_recheck_mime_types (config);
        stats_global (config);
        config_release_config();
        slave_update_all_mounts();
    }
}

489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
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)
{
    xmlDocPtr doc;
    xmlNodePtr node;

    if (filename == NULL || strcmp(filename, "") == 0) return CONFIG_EINSANE;
    
    doc = xmlParseFile(filename);
    if (doc == NULL) {
        return CONFIG_EPARSE;
    }

    node = xmlDocGetRootElement(doc);
    if (node == NULL) {
        xmlFreeDoc(doc);
        return CONFIG_ENOROOT;
    }

513
    if (xmlStrcmp (node->name, XMLSTR("icecast")) != 0) {
514 515 516 517 518 519
        xmlFreeDoc(doc);
        return CONFIG_EBADROOT;
    }

    config_init_configuration(configuration);

520
    configuration->config_filename = strdup (filename);
521 522 523 524 525

    _parse_root(doc, node->xmlChildrenNode, configuration);

    xmlFreeDoc(doc);

526 527
    _merge_mounts_all(configuration);

528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
    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)
{
543
    thread_rwlock_unlock(&(_locks.config_lock));
544 545 546 547
}

ice_config_t *config_get_config(void)
{
548 549 550 551 552 553 554
    thread_rwlock_rlock(&(_locks.config_lock));
    return &_current_configuration;
}

ice_config_t *config_grab_config(void)
{
    thread_rwlock_wlock(&(_locks.config_lock));
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
    return &_current_configuration;
}

/* MUST be called with the lock held! */
void config_set_config(ice_config_t *config) {
    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)
{
570 571 572
    configuration->location = (char *) xmlCharStrdup(CONFIG_DEFAULT_LOCATION);
    configuration->server_id = (char *) xmlCharStrdup(ICECAST_VERSION_STRING);
    configuration->admin = (char *) xmlCharStrdup(CONFIG_DEFAULT_ADMIN);
573 574 575 576 577 578
    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;
579
    configuration->shoutcast_mount = (char *)xmlCharStrdup (CONFIG_DEFAULT_SHOUTCAST_MOUNT);
580 581
    configuration->fileserve = CONFIG_DEFAULT_FILESERVE;
    configuration->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
582
    configuration->on_demand = 0;
583
    configuration->dir_list = NULL;
584 585
    configuration->hostname = (char *)xmlCharStrdup (CONFIG_DEFAULT_HOSTNAME);
    configuration->mimetypes_fn = (char *)xmlCharStrdup (MIMETYPESFILE);
586 587 588
    configuration->master_server = NULL;
    configuration->master_server_port = 0;
    configuration->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
589
    configuration->master_username = (char *)xmlCharStrdup (CONFIG_DEFAULT_MASTER_USERNAME);
590
    configuration->master_password = NULL;
591 592
    configuration->base_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_BASE_DIR);
    configuration->log_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_LOG_DIR);
593
    configuration->cipher_list = (char *)xmlCharStrdup (CONFIG_DEFAULT_CIPHER_LIST);
594
    configuration->null_device = (char *)xmlCharStrdup (CONFIG_DEFAULT_NULL_FILE);
595 596 597 598 599
    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);
600 601 602
    configuration->loglevel = CONFIG_DEFAULT_LOG_LEVEL;
    configuration->chroot = CONFIG_DEFAULT_CHROOT;
    configuration->chuid = CONFIG_DEFAULT_CHUID;
603 604
    configuration->user = NULL;
    configuration->group = NULL;
605
    configuration->num_yp_directories = 0;
Karl Heyes's avatar
Karl Heyes committed
606
    /* default to a typical prebuffer size used by clients */
607
    configuration->burst_size = CONFIG_DEFAULT_BURST_SIZE;
608 609
}

610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
static inline void __check_hostname(ice_config_t *configuration) {
    char *p;

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

    // convert to lower case:
    for (p = configuration->hostname; *p; p++)
        if ( *p >= 'A' && *p <= 'Z' )
            *p += 'a' - 'A';

    configuration->sane_hostname = 0;
    switch (util_hostcheck(configuration->hostname)) {
        case HOSTCHECK_SANE:
            configuration->sane_hostname = 1;
        break;
        case HOSTCHECK_ERROR:
            ICECAST_LOG_ERROR("Can not check hostname \"%s\".", configuration->hostname);
        break;
        case HOSTCHECK_NOT_FQDN:
            ICECAST_LOG_WARN("Warning, <hostname> seems not to be set to a fully qualified fomain name (FQDN). This may cause problems, e.g. with YP directory listings.");
        break;
        case HOSTCHECK_IS_LOCALHOST:
            ICECAST_LOG_WARN("Warning, <hostname> not configured, using default value \"%s\". This will cause problems, e.g. with YP directory listings.", CONFIG_DEFAULT_HOSTNAME);
        break;
        case HOSTCHECK_IS_IPV4:
            ICECAST_LOG_WARN("Warning, <hostname> seems to be set to an IPv4 address. This may cause problems, e.g. with YP directory listings.");
        break;
        case HOSTCHECK_IS_IPV6:
            ICECAST_LOG_WARN("Warning, <hostname> seems to be set to an IPv6 address. This may cause problems, e.g. with YP directory listings.");
        break;
        case HOSTCHECK_BADCHAR:
            ICECAST_LOG_WARN("Warning, <hostname> configured to unusual characters. This may cause problems, e.g. with YP directory listings.");
        break;
    }
}

648 649 650 651
static void _parse_root(xmlDocPtr doc, xmlNodePtr node, 
        ice_config_t *configuration)
{
    char *tmp;
Philipp Schafft's avatar
Philipp Schafft committed
652
    char *source_password = NULL;
653

654 655 656 657
    configuration->listen_sock = calloc (1, sizeof (*configuration->listen_sock));
    configuration->listen_sock->port = 8000;
    configuration->listen_sock_count = 1;

658 659 660 661
    do {
        if (node == NULL) break;
        if (xmlIsBlankNode(node)) continue;

662
        if (xmlStrcmp (node->name, XMLSTR("location")) == 0) {
663
            if (configuration->location) xmlFree(configuration->location);
664
            configuration->location = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
665
        } else if (xmlStrcmp (node->name, XMLSTR("admin")) == 0) {
666
            if (configuration->admin) xmlFree(configuration->admin);
667
            configuration->admin = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
668
        } else if (xmlStrcmp (node->name, XMLSTR("server-id")) == 0) {
669 670
            xmlFree (configuration->server_id);
            configuration->server_id = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
671
            ICECAST_LOG_WARN("Warning, server version string override detected. This may lead to unexpected client software behavior.");
672
        } else if(xmlStrcmp (node->name, XMLSTR("authentication")) == 0) {
Philipp Schafft's avatar
Philipp Schafft committed
673
            _parse_authentication(doc, node->xmlChildrenNode, configuration, &source_password);
674
        } else if (xmlStrcmp (node->name, XMLSTR("source-password")) == 0) {
675
            /* TODO: This is the backwards-compatibility location */
676
            ICECAST_LOG_WARN("<source-password> defined outside <authentication>. This is deprecated and will be removed in version 2.5.");
Philipp Schafft's avatar
Philipp Schafft committed
677 678 679
            if (source_password)
                xmlFree(source_password);
            source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
680
        } else if (xmlStrcmp (node->name, XMLSTR("icelogin")) == 0) {
Philipp Schafft's avatar
Philipp Schafft committed
681
            ICECAST_LOG_ERROR("<icelogin> has been removed.");
682
        } else if (xmlStrcmp (node->name, XMLSTR("fileserve")) == 0) {
683
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
684
            configuration->fileserve = util_str_to_bool(tmp);
685
            if (tmp) xmlFree(tmp);
686
        } else if (xmlStrcmp (node->name, XMLSTR("relays-on-demand")) == 0) {
687
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
688
            configuration->on_demand = util_str_to_bool(tmp);
689
            if (tmp) xmlFree(tmp);
690
        } else if (xmlStrcmp (node->name, XMLSTR("hostname")) == 0) {
691
            if (configuration->hostname) xmlFree(configuration->hostname);
692
            configuration->hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
693
        } else if (xmlStrcmp (node->name, XMLSTR("mime-types")) == 0) {
694 695
            if (configuration->mimetypes_fn) xmlFree(configuration->mimetypes_fn);
            configuration->mimetypes_fn = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
696
        } else if (xmlStrcmp (node->name, XMLSTR("listen-socket")) == 0) {
697
            _parse_listen_socket(doc, node->xmlChildrenNode, configuration);
698
        } else if (xmlStrcmp (node->name, XMLSTR("port")) == 0) {
699
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
700 701 702 703 704 705 706
            if (tmp) {
                configuration->port = atoi(tmp);
                configuration->listen_sock->port = atoi(tmp);
                xmlFree(tmp);
            } else {
                ICECAST_LOG_WARN("<port> must not be empty.");
            }
707
        } else if (xmlStrcmp (node->name, XMLSTR("bind-address")) == 0) {
708 709 710
            if (configuration->listen_sock->bind_address) 
                xmlFree(configuration->listen_sock->bind_address);
            configuration->listen_sock->bind_address = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
711
        } else if (xmlStrcmp (node->name, XMLSTR("master-server")) == 0) {
712 713
            if (configuration->master_server) xmlFree(configuration->master_server);
            configuration->master_server = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
714
        } else if (xmlStrcmp (node->name, XMLSTR("master-username")) == 0) {
715 716
            if (configuration->master_username) xmlFree(configuration->master_username);
            configuration->master_username = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
717
        } else if (xmlStrcmp (node->name, XMLSTR("master-password")) == 0) {
718 719
            if (configuration->master_password) xmlFree(configuration->master_password);
            configuration->master_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
720
        } else if (xmlStrcmp (node->name, XMLSTR("master-server-port")) == 0) {
721 722
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->master_server_port = atoi(tmp);
723
            xmlFree (tmp);
724
        } else if (xmlStrcmp (node->name, XMLSTR("master-update-interval")) == 0) {
725 726
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->master_update_interval = atoi(tmp);
727
            xmlFree (tmp);
728
        } else if (xmlStrcmp (node->name, XMLSTR("shoutcast-mount")) == 0) {
729
            if (configuration->shoutcast_mount) xmlFree(configuration->shoutcast_mount);
730
            configuration->shoutcast_mount = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
731
        } else if (xmlStrcmp (node->name, XMLSTR("limits")) == 0) {
732
            _parse_limits(doc, node->xmlChildrenNode, configuration);
733
        } else if (xmlStrcmp (node->name, XMLSTR("http-headers")) == 0) {
734
            _parse_http_headers(doc, node->xmlChildrenNode, &(configuration->http_headers));
735
        } else if (xmlStrcmp (node->name, XMLSTR("relay")) == 0) {
736
            _parse_relay(doc, node->xmlChildrenNode, configuration);
737
        } else if (xmlStrcmp (node->name, XMLSTR("mount")) == 0) {
738
            _parse_mount(doc, node, configuration);
739
        } else if (xmlStrcmp (node->name, XMLSTR("directory")) == 0) {
740
            _parse_directory(doc, node->xmlChildrenNode, configuration);
741
        } else if (xmlStrcmp (node->name, XMLSTR("paths")) == 0) {
742
            _parse_paths(doc, node->xmlChildrenNode, configuration);
743
        } else if (xmlStrcmp (node->name, XMLSTR("logging")) == 0) {
744
            _parse_logging(doc, node->xmlChildrenNode, configuration);
745
        } else if (xmlStrcmp (node->name, XMLSTR("security")) == 0) {
746 747 748
            _parse_security(doc, node->xmlChildrenNode, configuration);
        }
    } while ((node = node->next));
749

Philipp Schafft's avatar
Philipp Schafft committed
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
    /* 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) {
            if (!mount->authstack)
                __append_old_style_auth(&mount->authstack, "legacy-global-source", AUTH_TYPE_STATIC, "source", source_password, NULL, "source,put,get", 0, "*");
        } else {
            ICECAST_LOG_ERROR("Can not find nor create default mount but global lagency source password set. Bad.");
        }
    }

773 774 775 776 777 778 779
    /* drop the first listening socket details if more than one is defined, as we only
     * have port or listen-socket not both */
    if (configuration->listen_sock_count > 1)
    {
        configuration->listen_sock = config_clear_listener (configuration->listen_sock);
        configuration->listen_sock_count--;
    }
780 781
    if (configuration->port == 0)
        configuration->port = 8000;
782 783 784 785 786

   /* 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).");

787
  __check_hostname(configuration);
788 789 790 791 792 793 794 795 796 797 798 799

  if (!configuration->location || strcmp(configuration->location, CONFIG_DEFAULT_LOCATION) == 0) {
      ICECAST_LOG_WARN("Warning, <location> not configured, using default value \"%s\".", CONFIG_DEFAULT_LOCATION);
      if (!configuration->location)
          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 default value \"%s\".", CONFIG_DEFAULT_ADMIN);
      if (!configuration->admin)
          configuration->admin = (char *)xmlCharStrdup (CONFIG_DEFAULT_ADMIN);
  }
800 801 802 803 804 805 806 807 808 809 810
}

static void _parse_limits(xmlDocPtr doc, xmlNodePtr node, 
        ice_config_t *configuration)
{
    char *tmp;

    do {
        if (node == NULL) break;
        if (xmlIsBlankNode(node)) continue;

811
        if (xmlStrcmp (node->name, XMLSTR("clients")) == 0) {
812 813 814
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->client_limit = atoi(tmp);
            if (tmp) xmlFree(tmp);
815
        } else if (xmlStrcmp (node->name, XMLSTR("sources")) == 0) {
816 817 818
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->source_limit = atoi(tmp);
            if (tmp) xmlFree(tmp);
819
        } else if (xmlStrcmp (node->name, XMLSTR("queue-size")) == 0) {
820 821 822
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->queue_size_limit = atoi(tmp);
            if (tmp) xmlFree(tmp);
823
        } else if (xmlStrcmp (node->name, XMLSTR("threadpool")) == 0) {
824
            ICECAST_LOG_WARN("<threadpool> deprecated and will be removed in version 2.5.");
825
        } else if (xmlStrcmp (node->name, XMLSTR("client-timeout")) == 0) {
826 827 828
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->client_timeout = atoi(tmp);
            if (tmp) xmlFree(tmp);
829
        } else if (xmlStrcmp (node->name, XMLSTR("header-timeout")) == 0) {
830 831 832
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->header_timeout = atoi(tmp);
            if (tmp) xmlFree(tmp);
833
        } else if (xmlStrcmp (node->name, XMLSTR("source-timeout")) == 0) {
834 835 836
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->source_timeout = atoi(tmp);
            if (tmp) xmlFree(tmp);
837
        } else if (xmlStrcmp (node->name, XMLSTR("burst-on-connect")) == 0) {
838
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Karl Heyes's avatar
Karl Heyes committed
839 840 841
            if (atoi(tmp) == 0)
                configuration->burst_size = 0;
            if (tmp) xmlFree(tmp);
842
        } else if (xmlStrcmp (node->name, XMLSTR("burst-size")) == 0) {
Karl Heyes's avatar
Karl Heyes committed
843 844
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->burst_size = atoi(tmp);
845
            if (tmp) xmlFree(tmp);
846 847 848 849
        }
    } while ((node = node->next));
}

Philipp Schafft's avatar
Philipp Schafft committed
850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
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;
     xmlNodePtr child;

     child = node->xmlChildrenNode;

     while (child) {
         if (xmlStrcmp(node->name, XMLSTR("option")) == 0) {
             name = (char *)xmlGetProp(node, XMLSTR("name"));
             value = (char *)xmlGetProp(node, XMLSTR("value"));
             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);
         }

         __append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL, NULL, "source,put", NULL, 0, NULL);
     } else if (strcmp(type, AUTH_TYPE_URL) == 0) {
         /* This block is super fun! Attention! Super fun ahead! Ladies and Gentlemans take care and watch your children! */
         /* 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) {
             if (xmlStrcmp(node->name, XMLSTR("option")) == 0) {
                 name = (char *)xmlGetProp(node, XMLSTR("name"));
                 value = (char *)xmlGetProp(node, XMLSTR("value"));

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

         /* TODO: FIXME: XXX: implement support for mount_add and mount_remove (using only username and password) here. */
         __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);
         if (listener_add)
             __append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL, NULL, "get,put,head", NULL, 0, NULL);
         if (stream_auth)
             __append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL, NULL, "source,put", NULL, 0, NULL);

         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 {
         ICECAST_LOG_ERROR("Unknown authentication type in legacy mode. Disable anonymous login listener and global login for source.");
         __append_old_style_auth(authstack, NULL, AUTH_TYPE_ANONYMOUS, NULL, NULL, NULL, NULL, 0, NULL);
     }
     xmlFree(type);
}

1002 1003 1004 1005 1006 1007 1008
static void _parse_mount(xmlDocPtr doc, xmlNodePtr node, 
        ice_config_t *configuration)
{
    char *tmp;
    mount_proxy *mount = calloc(1, sizeof(mount_proxy));
    mount_proxy *current = configuration->mounts;
    mount_proxy *last=NULL;
Philipp Schafft's avatar
Philipp Schafft committed
1009 1010 1011
    char *username = NULL;
    char *password = NULL;
    auth_stack_t *authstack = NULL;
1012
    
Karl Heyes's avatar
Karl Heyes committed
1013
    /* default <mount> settings */
1014
    mount->mounttype = MOUNT_TYPE_NORMAL;
1015
    mount->max_listeners = -1;
Karl Heyes's avatar
Karl Heyes committed
1016
    mount->burst_size = -1;
1017
    mount->mp3_meta_interval = -1;
1018
    mount->yp_public = -1;
1019 1020
    mount->next = NULL;

1021 1022 1023 1024 1025 1026 1027 1028 1029
    tmp = (char *)xmlGetProp(node, XMLSTR("type"));
    if (tmp) {
        if (strcmp(tmp, "normal") == 0) {
	    mount->mounttype = MOUNT_TYPE_NORMAL;
	}
	else if (strcmp(tmp, "default") == 0) {
	    mount->mounttype = MOUNT_TYPE_DEFAULT;
	}
	else {
1030
	    ICECAST_LOG_WARN("Unknown mountpoint type: %s", tmp);
1031 1032 1033
            config_clear_mount (mount);
            return;
	}
1034
	xmlFree(tmp);
1035 1036 1037 1038
    }

    node = node->xmlChildrenNode;

1039 1040 1041 1042
    do {
        if (node == NULL) break;
        if (xmlIsBlankNode(node)) continue;

1043 1044
        if (xmlStrcmp (node->name, XMLSTR("mount-name")) == 0) {
            mount->mountname = (char *)xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
1045
        }
1046
        else if (xmlStrcmp (node->name, XMLSTR("username")) == 0) {
Philipp Schafft's avatar
Philipp Schafft committed
1047
            username = (char *)xmlNodeListGetString(
1048 1049
                    doc, node->xmlChildrenNode, 1);
        }
1050
        else if (xmlStrcmp (node->name, XMLSTR("password")) == 0) {
Philipp Schafft's avatar
Philipp Schafft committed
1051
            password = (char *)xmlNodeListGetString(
1052 1053
                    doc, node->xmlChildrenNode, 1);
        }
1054
        else if (xmlStrcmp (node->name, XMLSTR("dump-file")) == 0) {
1055 1056
            mount->dumpfile = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
Karl Heyes's avatar
Karl Heyes committed
1057
        }
1058
        else if (xmlStrcmp (node->name, XMLSTR("intro")) == 0) {
Karl Heyes's avatar
Karl Heyes committed
1059 1060
            mount->intro_filename = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
1061
        }
1062
        else if (xmlStrcmp (node->name, XMLSTR("fallback-mount")) == 0) {
1063 1064 1065
            mount->fallback_mount = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
        }
1066
        else if (xmlStrcmp (node->name, XMLSTR("fallback-when-full")) == 0) {
1067
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
1068
            mount->fallback_when_full = util_str_to_bool(tmp);
1069 1070
            if(tmp) xmlFree(tmp);
        }
1071
        else if (xmlStrcmp (node->name, XMLSTR("max-listeners")) == 0) {
1072 1073 1074 1075
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            mount->max_listeners = atoi(tmp);
            if(tmp) xmlFree(tmp);
        }
1076
        else if (xmlStrcmp (node->name, XMLSTR("charset")) == 0) {
1077 1078 1079
            mount->charset = (char *)xmlNodeListGetString(doc,
                    node->xmlChildrenNode, 1);
        }
1080
        else if (xmlStrcmp (node->name, XMLSTR("mp3-metadata-interval")) == 0) {
1081 1082 1083 1084
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            mount->mp3_meta_interval = atoi(tmp);
            if(tmp) xmlFree(tmp);
        }
1085
        else if (xmlStrcmp (node->name, XMLSTR("fallback-override")) == 0) {
Michael Smith's avatar
Michael Smith committed
1086
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
1087
            mount->fallback_override = util_str_to_bool(tmp);
Michael Smith's avatar
Michael Smith committed
1088 1089
            if(tmp) xmlFree(tmp);
        }
1090
        else if (xmlStrcmp (node->name, XMLSTR("no-mount")) == 0) {
Michael Smith's avatar
Michael Smith committed
1091
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
1092
            mount->no_mount = util_str_to_bool(tmp);
Michael Smith's avatar
Michael Smith committed
1093 1094
            if(tmp) xmlFree(tmp);
        }
1095
        else if (xmlStrcmp (node->name, XMLSTR("no-yp")) == 0) {
1096
            ICECAST_LOG_WARN("<no-yp> defined. Please use <public>. This is deprecated and will be removed in version 2.5.");
1097
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
1098
            mount->yp_public = util_str_to_bool(tmp) == 0 ? -1 : 0;
1099 1100
            if(tmp) xmlFree(tmp);
        }
1101
        else if (xmlStrcmp (node->name, XMLSTR("hidden")) == 0) {
1102
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
1103
            mount->hidden = util_str_to_bool(tmp);
1104 1105
            if(tmp) xmlFree(tmp);
        }
1106
        else if (xmlStrcmp (node->name, XMLSTR("authentication")) == 0) {
Philipp Schafft's avatar
Philipp Schafft committed
1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123
            tmp = (char *)xmlGetProp(node, XMLSTR("type"));
            if (tmp) {
                xmlFree(tmp);
                _parse_mount_oldstyle_authentication(mount, node, &authstack);
            } else {
                xmlNodePtr child = node->xmlChildrenNode;

                do {
                    if (child == NULL) break;
                    if (xmlIsBlankNode(child)) continue;
                    if (xmlStrcmp (child->name, XMLSTR("role")) == 0) {
                        auth_t *auth = auth_get_authenticator(child);
                        auth_stack_push(&authstack, auth);
                        auth_release(auth);
                    }
               } while ((child = child->next));
            }
Michael Smith's avatar
Michael Smith committed
1124
        }
1125
        else if (xmlStrcmp (node->name, XMLSTR("on-connect")) == 0) {
1126 1127 1128
            mount->on_connect = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
        }
1129
        else if (xmlStrcmp (node->name, XMLSTR("on-disconnect")) == 0) {
1130 1131 1132
            mount->on_disconnect = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
        }
1133
        else if (xmlStrcmp (node->name, XMLSTR("max-listener-duration")) == 0) {
1134 1135 1136 1137
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            mount->max_listener_duration = atoi(tmp);
            if(tmp) xmlFree(tmp);
        }
1138
        else if (xmlStrcmp (node->name, XMLSTR("queue-size")) == 0) {
1139 1140 1141 1142
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            mount->queue_size_limit = atoi (tmp);
            if(tmp) xmlFree(tmp);
        }
1143
        else if (xmlStrcmp (node->name, XMLSTR("source-timeout")) == 0) {
1144 1145 1146 1147 1148 1149
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            if (tmp)
            {
                mount->source_timeout = atoi (tmp);
                xmlFree(tmp);
            }
1150
        } else if (xmlStrcmp (node->name, XMLSTR("burst-size")) == 0) {
Karl Heyes's avatar
Karl Heyes committed
1151 1152 1153
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            mount->burst_size = atoi(tmp);
            if (tmp) xmlFree(tmp);
1154
        } else if (xmlStrcmp (node->name, XMLSTR("cluster-password")) == 0) {
1155 1156
            mount->cluster_password = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
1157
        } else if (xmlStrcmp (node->name, XMLSTR("stream-name")) == 0) {
1158 1159
            mount->stream_name = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
1160
        } else if (xmlStrcmp (node->name, XMLSTR("stream-description")) == 0) {
1161 1162
            mount->stream_description = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
1163
        } else if (xmlStrcmp (node->name, XMLSTR("stream-url")) == 0) {
1164 1165
            mount->stream_url = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
1166
        } else if (xmlStrcmp (node->name, XMLSTR("genre")) == 0) {
1167 1168
            mount->stream_genre = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
1169
        } else if (xmlStrcmp (node->name, XMLSTR("bitrate")) == 0) {
1170 1171
            mount->bitrate = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
1172
        } else if (xmlStrcmp (node->name, XMLSTR("public")) == 0) {
1173
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
1174
            mount->yp_public = __parse_public(tmp);
1175
            if(tmp) xmlFree(tmp);
1176
        } else if (xmlStrcmp (node->name, XMLSTR("type")) == 0) {
1177 1178
            mount->type = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
1179
        } else if (xmlStrcmp (node->name, XMLSTR("subtype")) == 0) {
1180 1181
            mount->subtype = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
1182 1183
        } else if (xmlStrcmp (node->name, XMLSTR("http-headers")) == 0) {
            _parse_http_headers(doc, node->xmlChildrenNode, &(mount->http_headers));
1184
        }
1185
    } while ((node = node->next));
1186

Philipp Schafft's avatar
Philipp Schafft committed
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
    if (password) {
        auth_stack_t *old_style = NULL;
        __append_old_style_auth(&old_style, "legacy-mount-source", AUTH_TYPE_STATIC, username ? username : "source", password, NULL, "source,put,get", 0, "*");
        if (authstack) {
            auth_stack_append(old_style, authstack);
            auth_stack_release(authstack);
        }
        authstack = old_style;
    }

    if (username)
        xmlFree(username);
    if (password)
        xmlFree(password);

    if (mount->authstack)
        auth_stack_release(mount->authstack);
    auth_stack_addref(mount->authstack = authstack);

1206
    /* make sure we have at least the mountpoint name */
1207
    if (mount->mountname == NULL && mount->mounttype != MOUNT_TYPE_DEFAULT)
1208 1209 1210 1211
    {
        config_clear_mount (mount);
        return;
    }
1212 1213
    else if (mount->mountname != NULL && mount->mounttype == MOUNT_TYPE_DEFAULT)
    {
1214
    	ICECAST_LOG_WARN("Default mount %s has mount-name set. This is not supported. Behavior may not be consistent.", mount->mountname);
1215
    }
Philipp Schafft's avatar
Philipp Schafft committed
1216 1217 1218 1219 1220 1221 1222 1223 1224 1225

    while (authstack) {
        auth_t *auth = auth_stack_get(authstack);
        if (mount->mountname) {
            auth->mount = strdup((char *)mount->mountname);
        } else if (mount->mounttype == MOUNT_TYPE_DEFAULT ) {
            auth->mount = strdup("(default mount)");
        }
        auth_release(auth);
        auth_stack_next(&authstack);
1226
    }
Philipp Schafft's avatar
Philipp Schafft committed
1227

1228 1229 1230 1231 1232
    while(current) {
        last = current;
        current = current->next;
    }

1233 1234
    if (!mount->fallback_mount && (mount->fallback_when_full || mount->fallback_override))
    {
1235
        ICECAST_LOG_WARN("Config for mount %s contains fallback options but no fallback mount.", mount->mountname);
1236 1237
    }

1238 1239 1240 1241
    if(last)
        last->next = mount;
    else
        configuration->mounts = mount;
1242 1243
}

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