cfgfile.c 62.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/* Icecast
 *
 * This program is distributed under the GNU General Public License, version 2.
 * A copy of this license is included with this source.
 *
 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org, 
 *                      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 29 30 31 32 33
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#include "thread/thread.h"
#include "cfgfile.h"
#include "refbuf.h"
#include "client.h"
#include "logging.h" 
34
#include "util.h" 
35 36 37 38 39 40

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

#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"
70
#define CONFIG_DEFAULT_NULL_FILE "/dev/null"
71
#define MIMETYPESFILE "/etc/mime.types"
72 73 74 75 76
#else
#define CONFIG_DEFAULT_BASE_DIR ".\\"
#define CONFIG_DEFAULT_LOG_DIR ".\\logs"
#define CONFIG_DEFAULT_WEBROOT_DIR ".\\webroot"
#define CONFIG_DEFAULT_ADMINROOT_DIR ".\\admin"
77
#define CONFIG_DEFAULT_NULL_FILE "nul:"
78
#define MIMETYPESFILE ".\\mime.types"
79 80 81 82 83 84 85 86 87 88 89 90 91 92
#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, 
        ice_config_t *c);
93 94
static void _parse_http_headers(xmlDocPtr doc, xmlNodePtr node,
        ice_config_http_header_t **http_headers);
95 96 97 98 99 100
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);

101 102 103
static void merge_mounts(mount_proxy * dst, mount_proxy * src);
static inline void _merge_mounts_all(ice_config_t *c);

104
static void create_locks(void) {
105
    thread_mutex_create(&_locks.relay_lock);
106
    thread_rwlock_create(&_locks.config_lock);
107 108
}

109
static void release_locks(void) {
110
    thread_mutex_destroy(&_locks.relay_lock);
111
    thread_rwlock_destroy(&_locks.config_lock);
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
}

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

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
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);
}

158 159 160 161 162 163 164 165 166 167 168 169
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);
 }
}

170
static inline ice_config_http_header_t * config_copy_http_header(ice_config_http_header_t *header) {
171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
    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 */

187 188 189 190
        cur->type   = header->type;
        cur->name   = (char *)xmlCharStrdup(header->name);
        cur->value  = (char *)xmlCharStrdup(header->value);
        cur->status = header->status;
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209

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

210 211 212 213
static void config_clear_mount (mount_proxy *mount)
{
    config_options_t *option;

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
    if (mount->mountname)       xmlFree (mount->mountname);
    if (mount->username)        xmlFree (mount->username);
    if (mount->password)        xmlFree (mount->password);
    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);

    if (mount->auth_type)       xmlFree (mount->auth_type);
232 233 234 235
    option = mount->auth_options;
    while (option)
    {
        config_options_t *nextopt = option->next;
236 237
        if (option->name)   xmlFree (option->name);
        if (option->value)  xmlFree (option->value);
238 239 240 241
        free (option);
        option = nextopt;
    }
    auth_release (mount->auth);
242
    config_clear_http_header(mount->http_headers);
243 244 245
    free (mount);
}

246 247 248 249 250 251 252
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);
253
        if (listener->shoutcast_mount)  xmlFree (listener->shoutcast_mount);
254 255 256 257
        free (listener);
    }
    return next;
}
258

259 260 261 262 263 264
void config_clear(ice_config_t *c)
{
    ice_config_dir_t *dirnode, *nextdirnode;
    relay_server *relay, *nextrelay;
    mount_proxy *mount, *nextmount;
    aliases *alias, *nextalias;
265
#ifdef USE_YP
266
    int i;
267
#endif
268

269
    free(c->config_filename);
270

Karl Heyes's avatar
Karl Heyes committed
271
    xmlFree (c->server_id);
272 273 274
    if (c->location) xmlFree(c->location);
    if (c->admin) xmlFree(c->admin);
    if (c->source_password) xmlFree(c->source_password);
275 276 277 278
    if (c->admin_username)
        xmlFree(c->admin_username);
    if (c->admin_password)
        xmlFree(c->admin_password);
279 280 281 282
    if (c->relay_username)
        xmlFree(c->relay_username);
    if (c->relay_password)
        xmlFree(c->relay_password);
283 284 285 286 287
    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
288
    if (c->cert_file) xmlFree(c->cert_file);
289
    if (c->cipher_list) xmlFree(c->cipher_list);
Karl Heyes's avatar
Karl Heyes committed
290
    if (c->pidfile)
291
        xmlFree(c->pidfile);
292 293
    if (c->banfile) xmlFree(c->banfile);
    if (c->allowfile) xmlFree(c->allowfile);
294 295 296 297
    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);
298
    if (c->master_server) xmlFree(c->master_server);
299
    if (c->master_username) xmlFree(c->master_username);
300 301 302
    if (c->master_password) xmlFree(c->master_password);
    if (c->user) xmlFree(c->user);
    if (c->group) xmlFree(c->group);
303
    if (c->mimetypes_fn) xmlFree (c->mimetypes_fn);
304

305 306 307
    while ((c->listen_sock = config_clear_listener (c->listen_sock)))
        ;

308 309 310 311 312 313
    thread_mutex_lock(&(_locks.relay_lock));
    relay = c->relay;
    while(relay) {
        nextrelay = relay->next;
        xmlFree(relay->server);
        xmlFree(relay->mount);
Karl Heyes's avatar
Karl Heyes committed
314
        xmlFree(relay->localmount);
315 316 317 318 319 320 321 322
        free(relay);
        relay = nextrelay;
    }
    thread_mutex_unlock(&(_locks.relay_lock));

    mount = c->mounts;
    while(mount) {
        nextmount = mount->next;
323
        config_clear_mount (mount);
324 325 326 327 328 329 330 331 332
        mount = nextmount;
    }

    alias = c->aliases;
    while(alias) {
        nextalias = alias->next;
        xmlFree(alias->source);
        xmlFree(alias->destination);
        xmlFree(alias->bind_address);
333
        xmlFree(alias->vhost);
334 335 336 337 338 339 340 341 342 343 344
        free(alias);
        alias = nextalias;
    }

    dirnode = c->dir_list;
    while(dirnode) {
        nextdirnode = dirnode->next;
        xmlFree(dirnode->host);
        free(dirnode);
        dirnode = nextdirnode;
    }
345
#ifdef USE_YP
Karl Heyes's avatar
Karl Heyes committed
346
    i = 0;
347
    while (i < c->num_yp_directories)
Karl Heyes's avatar
Karl Heyes committed
348 349 350 351 352
    {
        xmlFree (c->yp_url[i]);
        i++;
    }
#endif
353

354 355
    config_clear_http_header(c->http_headers);

356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
    memset(c, 0, sizeof(ice_config_t));
}

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

383
    if (xmlStrcmp (node->name, XMLSTR("icecast")) != 0) {
384 385 386 387 388 389
        xmlFreeDoc(doc);
        return CONFIG_EBADROOT;
    }

    config_init_configuration(configuration);

390
    configuration->config_filename = strdup (filename);
391 392 393 394 395

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

    xmlFreeDoc(doc);

396 397
    _merge_mounts_all(configuration);

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
    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)
{
413
    thread_rwlock_unlock(&(_locks.config_lock));
414 415 416 417
}

ice_config_t *config_get_config(void)
{
418 419 420 421 422 423 424
    thread_rwlock_rlock(&(_locks.config_lock));
    return &_current_configuration;
}

ice_config_t *config_grab_config(void)
{
    thread_rwlock_wlock(&(_locks.config_lock));
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
    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)
{
440
    configuration->location = (char *)xmlCharStrdup (CONFIG_DEFAULT_LOCATION);
441
    configuration->server_id = (char *)xmlCharStrdup (ICECAST_VERSION_STRING);
442
    configuration->admin = (char *)xmlCharStrdup (CONFIG_DEFAULT_ADMIN);
443 444 445 446 447 448
    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;
449
    configuration->source_password = NULL;
450
    configuration->shoutcast_mount = (char *)xmlCharStrdup (CONFIG_DEFAULT_SHOUTCAST_MOUNT);
451 452 453
    configuration->ice_login = CONFIG_DEFAULT_ICE_LOGIN;
    configuration->fileserve = CONFIG_DEFAULT_FILESERVE;
    configuration->touch_interval = CONFIG_DEFAULT_TOUCH_FREQ;
454
    configuration->on_demand = 0;
455
    configuration->dir_list = NULL;
456 457
    configuration->hostname = (char *)xmlCharStrdup (CONFIG_DEFAULT_HOSTNAME);
    configuration->mimetypes_fn = (char *)xmlCharStrdup (MIMETYPESFILE);
458 459 460
    configuration->master_server = NULL;
    configuration->master_server_port = 0;
    configuration->master_update_interval = CONFIG_MASTER_UPDATE_INTERVAL;
461
    configuration->master_username = (char *)xmlCharStrdup (CONFIG_DEFAULT_MASTER_USERNAME);
462
    configuration->master_password = NULL;
463 464
    configuration->base_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_BASE_DIR);
    configuration->log_dir = (char *)xmlCharStrdup (CONFIG_DEFAULT_LOG_DIR);
465
    configuration->cipher_list = (char *)xmlCharStrdup (CONFIG_DEFAULT_CIPHER_LIST);
466
    configuration->null_device = (char *)xmlCharStrdup (CONFIG_DEFAULT_NULL_FILE);
467 468 469 470 471
    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);
472 473 474
    configuration->loglevel = CONFIG_DEFAULT_LOG_LEVEL;
    configuration->chroot = CONFIG_DEFAULT_CHROOT;
    configuration->chuid = CONFIG_DEFAULT_CHUID;
475 476
    configuration->user = NULL;
    configuration->group = NULL;
477
    configuration->num_yp_directories = 0;
478
    configuration->relay_username = (char *)xmlCharStrdup (CONFIG_DEFAULT_MASTER_USERNAME);
479
    configuration->relay_password = NULL;
Karl Heyes's avatar
Karl Heyes committed
480
    /* default to a typical prebuffer size used by clients */
481
    configuration->burst_size = CONFIG_DEFAULT_BURST_SIZE;
482 483
}

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
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;
    }
}

522 523 524 525 526
static void _parse_root(xmlDocPtr doc, xmlNodePtr node, 
        ice_config_t *configuration)
{
    char *tmp;

527 528 529 530
    configuration->listen_sock = calloc (1, sizeof (*configuration->listen_sock));
    configuration->listen_sock->port = 8000;
    configuration->listen_sock_count = 1;

531 532 533 534
    do {
        if (node == NULL) break;
        if (xmlIsBlankNode(node)) continue;

535
        if (xmlStrcmp (node->name, XMLSTR("location")) == 0) {
536
            if (configuration->location) xmlFree(configuration->location);
537
            configuration->location = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
538
        } else if (xmlStrcmp (node->name, XMLSTR("admin")) == 0) {
539
            if (configuration->admin) xmlFree(configuration->admin);
540
            configuration->admin = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
541
        } else if (xmlStrcmp (node->name, XMLSTR("server-id")) == 0) {
542 543
            xmlFree (configuration->server_id);
            configuration->server_id = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
544
            ICECAST_LOG_WARN("Warning, server version string override detected. This may lead to unexpected client software behavior.");
545
        } else if(xmlStrcmp (node->name, XMLSTR("authentication")) == 0) {
546
            _parse_authentication(doc, node->xmlChildrenNode, configuration);
547
        } else if (xmlStrcmp (node->name, XMLSTR("source-password")) == 0) {
548
            /* TODO: This is the backwards-compatibility location */
549
            ICECAST_LOG_WARN("<source-password> defined outside <authentication>. This is deprecated and will be removed in version 2.5.");
550 551
            if (configuration->source_password) xmlFree(configuration->source_password);
            configuration->source_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
552
        } else if (xmlStrcmp (node->name, XMLSTR("icelogin")) == 0) {
553
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
554
            configuration->ice_login = util_str_to_bool(tmp);
555
            if (tmp) xmlFree(tmp);
556
        } else if (xmlStrcmp (node->name, XMLSTR("fileserve")) == 0) {
557
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
558
            configuration->fileserve = util_str_to_bool(tmp);
559
            if (tmp) xmlFree(tmp);
560
        } else if (xmlStrcmp (node->name, XMLSTR("relays-on-demand")) == 0) {
561
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
562
            configuration->on_demand = util_str_to_bool(tmp);
563
            if (tmp) xmlFree(tmp);
564
        } else if (xmlStrcmp (node->name, XMLSTR("hostname")) == 0) {
565
            if (configuration->hostname) xmlFree(configuration->hostname);
566
            configuration->hostname = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
567
        } else if (xmlStrcmp (node->name, XMLSTR("mime-types")) == 0) {
568 569
            if (configuration->mimetypes_fn) xmlFree(configuration->mimetypes_fn);
            configuration->mimetypes_fn = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
570
        } else if (xmlStrcmp (node->name, XMLSTR("listen-socket")) == 0) {
571
            _parse_listen_socket(doc, node->xmlChildrenNode, configuration);
572
        } else if (xmlStrcmp (node->name, XMLSTR("port")) == 0) {
573
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
574 575 576 577 578 579 580
            if (tmp) {
                configuration->port = atoi(tmp);
                configuration->listen_sock->port = atoi(tmp);
                xmlFree(tmp);
            } else {
                ICECAST_LOG_WARN("<port> must not be empty.");
            }
581
        } else if (xmlStrcmp (node->name, XMLSTR("bind-address")) == 0) {
582 583 584
            if (configuration->listen_sock->bind_address) 
                xmlFree(configuration->listen_sock->bind_address);
            configuration->listen_sock->bind_address = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
585
        } else if (xmlStrcmp (node->name, XMLSTR("master-server")) == 0) {
586 587
            if (configuration->master_server) xmlFree(configuration->master_server);
            configuration->master_server = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
588
        } else if (xmlStrcmp (node->name, XMLSTR("master-username")) == 0) {
589 590
            if (configuration->master_username) xmlFree(configuration->master_username);
            configuration->master_username = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
591
        } else if (xmlStrcmp (node->name, XMLSTR("master-password")) == 0) {
592 593
            if (configuration->master_password) xmlFree(configuration->master_password);
            configuration->master_password = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
594
        } else if (xmlStrcmp (node->name, XMLSTR("master-server-port")) == 0) {
595 596
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->master_server_port = atoi(tmp);
597
            xmlFree (tmp);
598
        } else if (xmlStrcmp (node->name, XMLSTR("master-update-interval")) == 0) {
599 600
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->master_update_interval = atoi(tmp);
601
            xmlFree (tmp);
602
        } else if (xmlStrcmp (node->name, XMLSTR("shoutcast-mount")) == 0) {
603
            if (configuration->shoutcast_mount) xmlFree(configuration->shoutcast_mount);
604
            configuration->shoutcast_mount = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
605
        } else if (xmlStrcmp (node->name, XMLSTR("limits")) == 0) {
606
            _parse_limits(doc, node->xmlChildrenNode, configuration);
607
        } else if (xmlStrcmp (node->name, XMLSTR("http-headers")) == 0) {
608
            _parse_http_headers(doc, node->xmlChildrenNode, &(configuration->http_headers));
609
        } else if (xmlStrcmp (node->name, XMLSTR("relay")) == 0) {
610
            _parse_relay(doc, node->xmlChildrenNode, configuration);
611
        } else if (xmlStrcmp (node->name, XMLSTR("mount")) == 0) {
612
            _parse_mount(doc, node, configuration);
613
        } else if (xmlStrcmp (node->name, XMLSTR("directory")) == 0) {
614
            _parse_directory(doc, node->xmlChildrenNode, configuration);
615
        } else if (xmlStrcmp (node->name, XMLSTR("paths")) == 0) {
616
            _parse_paths(doc, node->xmlChildrenNode, configuration);
617
        } else if (xmlStrcmp (node->name, XMLSTR("logging")) == 0) {
618
            _parse_logging(doc, node->xmlChildrenNode, configuration);
619
        } else if (xmlStrcmp (node->name, XMLSTR("security")) == 0) {
620 621 622
            _parse_security(doc, node->xmlChildrenNode, configuration);
        }
    } while ((node = node->next));
623 624 625 626 627 628 629 630

    /* 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--;
    }
631 632
    if (configuration->port == 0)
        configuration->port = 8000;
633 634 635 636 637

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

638
  __check_hostname(configuration);
639 640 641 642 643 644 645 646 647 648 649 650

  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);
  }
651 652 653 654 655 656 657 658 659 660 661
}

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

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

662
        if (xmlStrcmp (node->name, XMLSTR("clients")) == 0) {
663 664 665
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->client_limit = atoi(tmp);
            if (tmp) xmlFree(tmp);
666
        } else if (xmlStrcmp (node->name, XMLSTR("sources")) == 0) {
667 668 669
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->source_limit = atoi(tmp);
            if (tmp) xmlFree(tmp);
670
        } else if (xmlStrcmp (node->name, XMLSTR("queue-size")) == 0) {
671 672 673
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->queue_size_limit = atoi(tmp);
            if (tmp) xmlFree(tmp);
674
        } else if (xmlStrcmp (node->name, XMLSTR("threadpool")) == 0) {
675
            ICECAST_LOG_WARN("<threadpool> deprecated and will be removed in version 2.5.");
676
        } else if (xmlStrcmp (node->name, XMLSTR("client-timeout")) == 0) {
677 678 679
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->client_timeout = atoi(tmp);
            if (tmp) xmlFree(tmp);
680
        } else if (xmlStrcmp (node->name, XMLSTR("header-timeout")) == 0) {
681 682 683
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->header_timeout = atoi(tmp);
            if (tmp) xmlFree(tmp);
684
        } else if (xmlStrcmp (node->name, XMLSTR("source-timeout")) == 0) {
685 686 687
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->source_timeout = atoi(tmp);
            if (tmp) xmlFree(tmp);
688
        } else if (xmlStrcmp (node->name, XMLSTR("burst-on-connect")) == 0) {
689
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
Karl Heyes's avatar
Karl Heyes committed
690 691 692
            if (atoi(tmp) == 0)
                configuration->burst_size = 0;
            if (tmp) xmlFree(tmp);
693
        } else if (xmlStrcmp (node->name, XMLSTR("burst-size")) == 0) {
Karl Heyes's avatar
Karl Heyes committed
694 695
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            configuration->burst_size = atoi(tmp);
696
            if (tmp) xmlFree(tmp);
697 698 699 700 701 702 703 704 705 706 707 708
        }
    } while ((node = node->next));
}

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;
    
Karl Heyes's avatar
Karl Heyes committed
709
    /* default <mount> settings */
710
    mount->mounttype = MOUNT_TYPE_NORMAL;
711
    mount->max_listeners = -1;
Karl Heyes's avatar
Karl Heyes committed
712
    mount->burst_size = -1;
713
    mount->mp3_meta_interval = -1;
714
    mount->yp_public = -1;
715 716
    mount->next = NULL;

717 718 719 720 721 722 723 724 725
    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 {
726
	    ICECAST_LOG_WARN("Unknown mountpoint type: %s", tmp);
727 728 729
            config_clear_mount (mount);
            return;
	}
730
	xmlFree(tmp);
731 732 733 734
    }

    node = node->xmlChildrenNode;

735 736 737 738
    do {
        if (node == NULL) break;
        if (xmlIsBlankNode(node)) continue;

739 740
        if (xmlStrcmp (node->name, XMLSTR("mount-name")) == 0) {
            mount->mountname = (char *)xmlNodeListGetString (doc, node->xmlChildrenNode, 1);
741
        }
742
        else if (xmlStrcmp (node->name, XMLSTR("username")) == 0) {
743 744 745
            mount->username = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
        }
746
        else if (xmlStrcmp (node->name, XMLSTR("password")) == 0) {
747 748 749
            mount->password = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
        }
750
        else if (xmlStrcmp (node->name, XMLSTR("dump-file")) == 0) {
751 752
            mount->dumpfile = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
Karl Heyes's avatar
Karl Heyes committed
753
        }
754
        else if (xmlStrcmp (node->name, XMLSTR("intro")) == 0) {
Karl Heyes's avatar
Karl Heyes committed
755 756
            mount->intro_filename = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
757
        }
758
        else if (xmlStrcmp (node->name, XMLSTR("fallback-mount")) == 0) {
759 760 761
            mount->fallback_mount = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
        }
762
        else if (xmlStrcmp (node->name, XMLSTR("fallback-when-full")) == 0) {
763
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
764
            mount->fallback_when_full = util_str_to_bool(tmp);
765 766
            if(tmp) xmlFree(tmp);
        }
767
        else if (xmlStrcmp (node->name, XMLSTR("max-listeners")) == 0) {
768 769 770 771
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            mount->max_listeners = atoi(tmp);
            if(tmp) xmlFree(tmp);
        }
772
        else if (xmlStrcmp (node->name, XMLSTR("charset")) == 0) {
773 774 775
            mount->charset = (char *)xmlNodeListGetString(doc,
                    node->xmlChildrenNode, 1);
        }
776
        else if (xmlStrcmp (node->name, XMLSTR("mp3-metadata-interval")) == 0) {
777 778 779 780
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            mount->mp3_meta_interval = atoi(tmp);
            if(tmp) xmlFree(tmp);
        }
781
        else if (xmlStrcmp (node->name, XMLSTR("fallback-override")) == 0) {
Michael Smith's avatar
Michael Smith committed
782
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
783
            mount->fallback_override = util_str_to_bool(tmp);
Michael Smith's avatar
Michael Smith committed
784 785
            if(tmp) xmlFree(tmp);
        }
786
        else if (xmlStrcmp (node->name, XMLSTR("no-mount")) == 0) {
Michael Smith's avatar
Michael Smith committed
787
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
788
            mount->no_mount = util_str_to_bool(tmp);
Michael Smith's avatar
Michael Smith committed
789 790
            if(tmp) xmlFree(tmp);
        }
791
        else if (xmlStrcmp (node->name, XMLSTR("no-yp")) == 0) {
792
            ICECAST_LOG_WARN("<no-yp> defined. Please use <public>. This is deprecated and will be removed in version 2.5.");
Karl Heyes's avatar
Karl Heyes committed
793
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
794
            mount->yp_public = util_str_to_bool(tmp) == 0 ? -1 : 0;
Karl Heyes's avatar
Karl Heyes committed
795 796
            if(tmp) xmlFree(tmp);
        }
797
        else if (xmlStrcmp (node->name, XMLSTR("hidden")) == 0) {
798
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
799
            mount->hidden = util_str_to_bool(tmp);
800 801
            if(tmp) xmlFree(tmp);
        }
802
        else if (xmlStrcmp (node->name, XMLSTR("authentication")) == 0) {
803
            mount->auth = auth_get_authenticator (node);
Michael Smith's avatar
Michael Smith committed
804
        }
805
        else if (xmlStrcmp (node->name, XMLSTR("on-connect")) == 0) {
806 807 808
            mount->on_connect = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
        }
809
        else if (xmlStrcmp (node->name, XMLSTR("on-disconnect")) == 0) {
810 811 812
            mount->on_disconnect = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
        }
813
        else if (xmlStrcmp (node->name, XMLSTR("max-listener-duration")) == 0) {
814 815 816 817
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            mount->max_listener_duration = atoi(tmp);
            if(tmp) xmlFree(tmp);
        }
818
        else if (xmlStrcmp (node->name, XMLSTR("queue-size")) == 0) {
819 820 821 822
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            mount->queue_size_limit = atoi (tmp);
            if(tmp) xmlFree(tmp);
        }
823
        else if (xmlStrcmp (node->name, XMLSTR("source-timeout")) == 0) {
824 825 826 827 828 829
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            if (tmp)
            {
                mount->source_timeout = atoi (tmp);
                xmlFree(tmp);
            }
830
        } else if (xmlStrcmp (node->name, XMLSTR("burst-size")) == 0) {
Karl Heyes's avatar
Karl Heyes committed
831 832 833
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
            mount->burst_size = atoi(tmp);
            if (tmp) xmlFree(tmp);
834
        } else if (xmlStrcmp (node->name, XMLSTR("cluster-password")) == 0) {
835 836
            mount->cluster_password = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
837
        } else if (xmlStrcmp (node->name, XMLSTR("stream-name")) == 0) {
838 839
            mount->stream_name = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
840
        } else if (xmlStrcmp (node->name, XMLSTR("stream-description")) == 0) {
841 842
            mount->stream_description = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
843
        } else if (xmlStrcmp (node->name, XMLSTR("stream-url")) == 0) {
844 845
            mount->stream_url = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
846
        } else if (xmlStrcmp (node->name, XMLSTR("genre")) == 0) {
847 848
            mount->stream_genre = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
849
        } else if (xmlStrcmp (node->name, XMLSTR("bitrate")) == 0) {
850 851
            mount->bitrate = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
852
        } else if (xmlStrcmp (node->name, XMLSTR("public")) == 0) {
853
            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
854
            mount->yp_public = __parse_public(tmp);
855
            if(tmp) xmlFree(tmp);
856
        } else if (xmlStrcmp (node->name, XMLSTR("type")) == 0) {
857 858
            mount->type = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
859
        } else if (xmlStrcmp (node->name, XMLSTR("subtype")) == 0) {
860 861
            mount->subtype = (char *)xmlNodeListGetString(
                    doc, node->xmlChildrenNode, 1);
862 863
        } else if (xmlStrcmp (node->name, XMLSTR("http-headers")) == 0) {
            _parse_http_headers(doc, node->xmlChildrenNode, &(mount->http_headers));
864
        }
865
    } while ((node = node->next));
866 867

    /* make sure we have at least the mountpoint name */
868
    if (mount->mountname == NULL && mount->mounttype != MOUNT_TYPE_DEFAULT)
869 870 871 872
    {
        config_clear_mount (mount);
        return;
    }
873 874
    else if (mount->mountname != NULL && mount->mounttype == MOUNT_TYPE_DEFAULT)
    {
875
    	ICECAST_LOG_WARN("Default mount %s has mount-name set. This is not supported. Behavior may not be consistent.", mount->mountname);
876
    }
877
    if (mount->auth && mount->mountname) {
878
        mount->auth->mount = strdup ((char *)mount->mountname);
879 880 881
    } else if (mount->auth && mount->mounttype == MOUNT_TYPE_DEFAULT ) {
        mount->auth->mount = strdup ("(default mount)");
    }
882 883 884 885 886
    while(current) {
        last = current;
        current = current->next;
    }

887 888
    if (!mount->fallback_mount && (mount->fallback_when_full || mount->fallback_override))
    {
889
        ICECAST_LOG_WARN("Config for mount %s contains fallback options but no fallback mount.", mount->mountname);
890 891
    }

892 893 894 895
    if(last)
        last->next = mount;
    else
        configuration->mounts = mount;
896 897
}

898
static void _parse_http_headers(xmlDocPtr doc, xmlNodePtr node, ice_config_http_header_t **http_headers) {
899 900 901 902
    ice_config_http_header_t *header;
    ice_config_http_header_t *next;
    char *name = NULL;
    char *value = NULL;
903 904 905
    char *tmp;
    int status;
    http_header_type type;
906 907 908 909

    do {
        if (node == NULL) break;
        if (xmlIsBlankNode(node)) continue;
910
        if (xmlStrcmp (node->name, XMLSTR("header")) != 0) continue;
911 912 913
        if (!(name = (char *)xmlGetProp(node, XMLSTR("name")))) break;
        if (!(value = (char *)xmlGetProp(node, XMLSTR("value")))) break;

914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
        type = HTTP_HEADER_TYPE_STATIC; /* default */
        if ((tmp = (char *)xmlGetProp(node, XMLSTR("type")))) {
            if (strcmp(tmp, "static") == 0) {
                type = HTTP_HEADER_TYPE_STATIC;
            } else {
                ICECAST_LOG_WARN("Unknown type %s for HTTP Header %s", tmp, name);
                xmlFree(tmp);
                break;
            }
            xmlFree(tmp);
        }

        status = 0; /* default: any */
        if ((tmp = (char *)xmlGetProp(node, XMLSTR("status")))) {
            status = atoi(tmp);
            xmlFree(tmp);
        }

932 933
        header = calloc(1, sizeof(ice_config_http_header_t));
        if (!header) break;
934
        header->type = type;
935 936
        header->name = name;
        header->value = value;
937
        header->status = status;
938 939 940
        name = NULL;
        value = NULL;

941 942
        if (!*http_headers) {
            *http_headers = header;
943 944
            continue;
        }
945
        next = *http_headers;
946 947 948 949 950 951 952 953 954
        while (next->next) next = next->next;
        next->next = header;
    } while ((node = node->next));
    /* in case we used break we may need to clean those up */
    if (name)
	xmlFree(name);
    if (value)
	xmlFree(value);
}
955

956 957 958 959 960 961 962
static void _parse_relay(xmlDocPtr doc, xmlNodePtr node,
        ice_config_t *configuration)
{
    char *tmp;
    relay_server *relay = calloc(1, sizeof(relay_server));
    relay_server *current = configuration->relay;
    relay_server *last=NULL;