auth_url.c 16.3 KB
Newer Older
Karl Heyes's avatar
Karl Heyes committed
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>,
Karl Heyes's avatar
Karl Heyes committed
7 8 9 10
 *                      Michael Smith <msmith@xiph.org>,
 *                      oddsock <oddsock@xiph.org>,
 *                      Karl Heyes <karl@xiph.org>
 *                      and others (see AUTHORS for details).
Philipp Schafft's avatar
Philipp Schafft committed
11
 * Copyright 2011-2014, Philipp "ph3-der-loewe" Schafft <lion@lion.leolix.org>,
Karl Heyes's avatar
Karl Heyes committed
12 13
 */

14
/*
Karl Heyes's avatar
Karl Heyes committed
15 16 17 18 19 20
 * Client authentication via URL functions
 *
 * authenticate user via a URL, this is done via libcurl so https can also
 * be handled. The request will have POST information about the request in
 * the form of
 *
21
 * action=listener_add&client=1&server=host&port=8000&mount=/live&user=fred&pass=mypass&ip=127.0.0.1&agent=""
Karl Heyes's avatar
Karl Heyes committed
22 23 24 25 26 27
 *
 * For a user to be accecpted the following HTTP header needs
 * to be returned (the actual string can be specified in the xml file)
 *
 * icecast-auth-user: 1
 *
28 29 30 31 32 33
 * A listening client may also be configured as only to stay connected for a
 * certain length of time. eg The auth server may only allow a 15 minute
 * playback by sending back.
 *
 * icecast-auth-timelimit: 900
 *
Karl Heyes's avatar
Karl Heyes committed
34 35 36
 * On client disconnection another request can be sent to a URL with the POST
 * information of
 *
37
 * action=listener_remove&server=host&port=8000&client=1&mount=/live&user=fred&pass=mypass&duration=3600
Karl Heyes's avatar
Karl Heyes committed
38 39 40 41 42 43
 *
 * client refers to the icecast client identification number. mount refers
 * to the mountpoint (beginning with / and may contain query parameters eg ?&
 * encoded) and duration is the amount of time in seconds. user and pass
 * setting can be blank
 *
44 45 46 47 48 49 50 51
 * On source client connection, a request can be made to trigger a URL request
 * to verify the details externally. Post info is
 *
 * action=stream_auth&mount=/stream&ip=IP&server=SERVER&port=8000&user=fred&pass=pass
 *
 * As admin requests can come in for a stream (eg metadata update) these requests
 * can be issued while stream is active. For these &admin=1 is added to the POST
 * details.
Karl Heyes's avatar
Karl Heyes committed
52 53 54 55 56 57 58 59 60 61 62
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#ifndef _WIN32
63 64
#   include <sys/wait.h>
#   include <strings.h>
Karl Heyes's avatar
Karl Heyes committed
65
#else
66 67
#   define snprintf _snprintf
#   define strncasecmp strnicmp
Karl Heyes's avatar
Karl Heyes committed
68 69
#endif

70
#include "curl.h"
Karl Heyes's avatar
Karl Heyes committed
71 72 73 74
#include "auth.h"
#include "source.h"
#include "client.h"
#include "cfgfile.h"
Marvin Scholz's avatar
Marvin Scholz committed
75
#include "common/httpp/httpp.h"
Karl Heyes's avatar
Karl Heyes committed
76 77 78 79 80

#include "logging.h"
#define CATMODULE "auth_url"

typedef struct {
Marvin Scholz's avatar
Marvin Scholz committed
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    char       *pass_headers; // headers passed from client to addurl.
    char       *prefix_headers; // prefix for passed headers.
    char       *addurl;
    char       *removeurl;
    char       *addaction;
    char       *removeaction;
    char       *username;
    char       *password;
    char       *auth_header;
    int         auth_header_len;
    char       *timelimit_header;
    int         timelimit_header_len;
    char       *userpwd;
    CURL       *handle;
    char        errormsg[CURL_ERROR_SIZE];
Philipp Schafft's avatar
Philipp Schafft committed
96
    auth_result result;
Karl Heyes's avatar
Karl Heyes committed
97 98 99 100
} auth_url;


static void auth_url_clear(auth_t *self)
101 102
{
    auth_url *url;
103

104
    ICECAST_LOG_INFO("Doing auth URL cleanup");
105
    url = self->state;
106
    self->state = NULL;
107
    icecast_curl_free(url->handle);
Philipp Schafft's avatar
Philipp Schafft committed
108 109 110 111 112 113 114 115 116 117 118 119
    free(url->username);
    free(url->password);
    free(url->pass_headers);
    free(url->prefix_headers);
    free(url->removeurl);
    free(url->addurl);
    free(url->addaction);
    free(url->removeaction);
    free(url->auth_header);
    free(url->timelimit_header);
    free(url->userpwd);
    free(url);
Karl Heyes's avatar
Karl Heyes committed
120 121
}

Marvin Scholz's avatar
Marvin Scholz committed
122 123 124 125
static size_t handle_returned_header(void      *ptr,
                                     size_t    size,
                                     size_t    nmemb,
                                     void      *stream)
Karl Heyes's avatar
Karl Heyes committed
126 127 128 129 130
{
    auth_client *auth_user = stream;
    unsigned bytes = size * nmemb;
    client_t *client = auth_user->client;

Marvin Scholz's avatar
Marvin Scholz committed
131
    if (client) {
Karl Heyes's avatar
Karl Heyes committed
132 133
        auth_t *auth = client->auth;
        auth_url *url = auth->state;
Marvin Scholz's avatar
Marvin Scholz committed
134
        if (strncasecmp(ptr, url->auth_header, url->auth_header_len) == 0)
Philipp Schafft's avatar
Philipp Schafft committed
135
            url->result = AUTH_OK;
Marvin Scholz's avatar
Marvin Scholz committed
136 137
        if (strncasecmp(ptr, url->timelimit_header,
                url->timelimit_header_len) == 0) {
138 139 140 141
            unsigned int limit = 0;
            sscanf ((char *)ptr+url->timelimit_header_len, "%u\r\n", &limit);
            client->con->discon_time = time(NULL) + limit;
        }
Marvin Scholz's avatar
Marvin Scholz committed
142
        if (strncasecmp (ptr, "icecast-auth-message: ", 22) == 0) {
Karl Heyes's avatar
Karl Heyes committed
143
            char *eol;
Marvin Scholz's avatar
Marvin Scholz committed
144 145
            snprintf(url->errormsg, sizeof(url->errormsg), "%s", (char*)ptr+22);
            eol = strchr(url->errormsg, '\r');
Karl Heyes's avatar
Karl Heyes committed
146
            if (eol == NULL)
Marvin Scholz's avatar
Marvin Scholz committed
147
                eol = strchr(url->errormsg, '\n');
Karl Heyes's avatar
Karl Heyes committed
148 149 150 151 152 153 154 155
            if (eol)
                *eol = '\0';
        }
    }

    return (int)bytes;
}

Marvin Scholz's avatar
Marvin Scholz committed
156
static auth_result url_remove_client(auth_client *auth_user)
Karl Heyes's avatar
Karl Heyes committed
157
{
Marvin Scholz's avatar
Marvin Scholz committed
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
    client_t       *client      = auth_user->client;
    auth_t         *auth        = client->auth;
    auth_url       *url         = auth->state;
    time_t          duration    = time(NULL) - client->con->con_time;
    char           *username,
                   *password,
                   *mount,
                   *server;
    const char     *mountreq;
    ice_config_t   *config;
    int             port;
    char           *userpwd     = NULL,
                    post[4096];
    const char     *agent;
    char           *user_agent,
                   *ipaddr;
Karl Heyes's avatar
Karl Heyes committed
174

175 176
    if (url->removeurl == NULL)
        return AUTH_OK;
177

Marvin Scholz's avatar
Marvin Scholz committed
178 179
    config = config_get_config();
    server = util_url_escape(config->hostname);
Karl Heyes's avatar
Karl Heyes committed
180
    port = config->port;
Marvin Scholz's avatar
Marvin Scholz committed
181
    config_release_config();
Karl Heyes's avatar
Karl Heyes committed
182

Marvin Scholz's avatar
Marvin Scholz committed
183 184 185 186 187 188
    agent = httpp_getvar(client->parser, "user-agent");
    if (agent) {
        user_agent = util_url_escape(agent);
    } else {
        user_agent = strdup("-");
    }
189

Marvin Scholz's avatar
Marvin Scholz committed
190 191 192 193 194
    if (client->username) {
        username = util_url_escape(client->username);
    } else {
        username = strdup("");
    }
Karl Heyes's avatar
Karl Heyes committed
195

Marvin Scholz's avatar
Marvin Scholz committed
196 197 198 199 200
    if (client->password) {
        password = util_url_escape(client->password);
    } else {
        password = strdup("");
    }
Karl Heyes's avatar
Karl Heyes committed
201 202

    /* get the full uri (with query params if available) */
Marvin Scholz's avatar
Marvin Scholz committed
203
    mountreq = httpp_getvar(client->parser, HTTPP_VAR_RAWURI);
204
    if (mountreq == NULL)
Marvin Scholz's avatar
Marvin Scholz committed
205 206 207
        mountreq = httpp_getvar(client->parser, HTTPP_VAR_URI);
    mount = util_url_escape(mountreq);
    ipaddr = util_url_escape(client->con->ip);
Karl Heyes's avatar
Karl Heyes committed
208

Marvin Scholz's avatar
Marvin Scholz committed
209
    snprintf(post, sizeof (post),
Philipp Schafft's avatar
Philipp Schafft committed
210
            "action=%s&server=%s&port=%d&client=%lu&mount=%s"
211
            "&user=%s&pass=%s&duration=%lu&ip=%s&agent=%s",
Philipp Schafft's avatar
Philipp Schafft committed
212
            url->removeaction, /* already escaped */
Karl Heyes's avatar
Karl Heyes committed
213
            server, port, client->con->id, mount, username,
214
            password, (long unsigned)duration, ipaddr, user_agent);
Marvin Scholz's avatar
Marvin Scholz committed
215 216 217 218 219 220 221 222 223 224 225 226

    free(server);
    free(mount);
    free(username);
    free(password);
    free(ipaddr);
    free(user_agent);

    if (strchr (url->removeurl, '@') == NULL) {
        if (url->userpwd) {
            curl_easy_setopt(url->handle, CURLOPT_USERPWD, url->userpwd);
        } else {
227
            /* auth'd requests may not have a user/pass, but may use query args */
Marvin Scholz's avatar
Marvin Scholz committed
228 229 230 231 232 233 234 235 236
            if (client->username && client->password) {
                size_t len = strlen(client->username) +
                    strlen(client->password) + 2;
                userpwd = malloc(len);
                snprintf(userpwd, len, "%s:%s",
                    client->username, client->password);
                curl_easy_setopt(url->handle, CURLOPT_USERPWD, userpwd);
            } else {
                curl_easy_setopt(url->handle, CURLOPT_USERPWD, "");
237 238
            }
        }
Marvin Scholz's avatar
Marvin Scholz committed
239
    } else {
240
        /* url has user/pass but libcurl may need to clear any existing settings */
Marvin Scholz's avatar
Marvin Scholz committed
241
        curl_easy_setopt(url->handle, CURLOPT_USERPWD, "");
242
    }
Marvin Scholz's avatar
Marvin Scholz committed
243 244 245
    curl_easy_setopt(url->handle, CURLOPT_URL, url->removeurl);
    curl_easy_setopt(url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt(url->handle, CURLOPT_WRITEHEADER, auth_user);
Karl Heyes's avatar
Karl Heyes committed
246 247

    if (curl_easy_perform (url->handle))
Marvin Scholz's avatar
Marvin Scholz committed
248 249
        ICECAST_LOG_WARN("auth to server %s failed with %s",
            url->removeurl, url->errormsg);
Karl Heyes's avatar
Karl Heyes committed
250

Marvin Scholz's avatar
Marvin Scholz committed
251
    free(userpwd);
252

Karl Heyes's avatar
Karl Heyes committed
253 254 255 256
    return AUTH_OK;
}


Marvin Scholz's avatar
Marvin Scholz committed
257
static auth_result url_add_client(auth_client *auth_user)
Karl Heyes's avatar
Karl Heyes committed
258
{
Marvin Scholz's avatar
Marvin Scholz committed
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
    client_t       *client      = auth_user->client;
    auth_t         *auth        = client->auth;
    auth_url       *url         = auth->state;
    int             res         = 0,
                    port;
    const char     *agent;
    char           *user_agent,
                   *username,
                   *password;
    const char     *mountreq;
    char           *mount,
                   *ipaddr,
                   *server;
    ice_config_t   *config;
    char           *userpwd    = NULL, post [4096];
    ssize_t         post_offset;
    char           *pass_headers,
                   *cur_header,
                   *next_header;
    const char     *header_val;
    char           *header_valesc;
Karl Heyes's avatar
Karl Heyes committed
280 281 282 283

    if (url->addurl == NULL)
        return AUTH_OK;

Marvin Scholz's avatar
Marvin Scholz committed
284 285
    config = config_get_config();
    server = util_url_escape(config->hostname);
Karl Heyes's avatar
Karl Heyes committed
286
    port = config->port;
Marvin Scholz's avatar
Marvin Scholz committed
287
    config_release_config();
288

Marvin Scholz's avatar
Marvin Scholz committed
289 290 291 292 293 294
    agent = httpp_getvar(client->parser, "user-agent");
    if (agent) {
        user_agent = util_url_escape(agent);
    } else {
        user_agent = strdup("-");
    }
295

Marvin Scholz's avatar
Marvin Scholz committed
296 297 298 299 300
    if (client->username) {
        username = util_url_escape(client->username);
    } else {
        username = strdup("");
    }
301

Marvin Scholz's avatar
Marvin Scholz committed
302 303 304 305 306
    if (client->password) {
        password = util_url_escape(client->password);
    } else {
        password = strdup("");
    }
Karl Heyes's avatar
Karl Heyes committed
307 308

    /* get the full uri (with query params if available) */
Marvin Scholz's avatar
Marvin Scholz committed
309
    mountreq = httpp_getvar(client->parser, HTTPP_VAR_RAWURI);
310
    if (mountreq == NULL)
Marvin Scholz's avatar
Marvin Scholz committed
311 312 313
        mountreq = httpp_getvar(client->parser, HTTPP_VAR_URI);
    mount = util_url_escape(mountreq);
    ipaddr = util_url_escape(client->con->ip);
Karl Heyes's avatar
Karl Heyes committed
314

Marvin Scholz's avatar
Marvin Scholz committed
315
    post_offset = snprintf(post, sizeof (post),
Philipp Schafft's avatar
Philipp Schafft committed
316
            "action=%s&server=%s&port=%d&client=%lu&mount=%s"
Karl Heyes's avatar
Karl Heyes committed
317
            "&user=%s&pass=%s&ip=%s&agent=%s",
Philipp Schafft's avatar
Philipp Schafft committed
318
            url->addaction, /* already escaped */
Karl Heyes's avatar
Karl Heyes committed
319 320
            server, port, client->con->id, mount, username,
            password, ipaddr, user_agent);
Marvin Scholz's avatar
Marvin Scholz committed
321 322 323 324 325 326 327

    free(server);
    free(mount);
    free(user_agent);
    free(username);
    free(password);
    free(ipaddr);
Karl Heyes's avatar
Karl Heyes committed
328

329 330
    pass_headers = NULL;
    if (url->pass_headers)
Marvin Scholz's avatar
Marvin Scholz committed
331 332
        pass_headers = strdup(url->pass_headers);
    if (pass_headers) {
333
        cur_header = pass_headers;
Marvin Scholz's avatar
Marvin Scholz committed
334 335 336
        while (cur_header) {
            next_header = strstr(cur_header, ",");
            if (next_header) {
337
                *next_header=0;
338
                next_header++;
339
            }
340 341

            header_val = httpp_getvar (client->parser, cur_header);
Marvin Scholz's avatar
Marvin Scholz committed
342
            if (header_val) {
343
                header_valesc = util_url_escape (header_val);
Marvin Scholz's avatar
Marvin Scholz committed
344 345 346 347 348 349
                post_offset += snprintf(post + post_offset,
                                        sizeof(post) - post_offset,
                                        "&%s%s=%s",
                                        url->prefix_headers ? url->prefix_headers : "",
                                        cur_header, header_valesc);
                free(header_valesc);
350 351
            }

352
            cur_header = next_header;
353 354 355
        }
    }

Marvin Scholz's avatar
Marvin Scholz committed
356 357 358 359
    if (strchr(url->addurl, '@') == NULL) {
        if (url->userpwd) {
            curl_easy_setopt(url->handle, CURLOPT_USERPWD, url->userpwd);
        } else {
360
            /* auth'd requests may not have a user/pass, but may use query args */
Marvin Scholz's avatar
Marvin Scholz committed
361 362
            if (client->username && client->password) {
                size_t len = strlen(client->username) + strlen(client->password) + 2;
363
                userpwd = malloc (len);
Marvin Scholz's avatar
Marvin Scholz committed
364 365 366 367
                snprintf(userpwd, len, "%s:%s",
                    client->username, client->password);
                curl_easy_setopt(url->handle, CURLOPT_USERPWD, userpwd);
            } else {
368
                curl_easy_setopt (url->handle, CURLOPT_USERPWD, "");
Marvin Scholz's avatar
Marvin Scholz committed
369
            }
370
        }
Marvin Scholz's avatar
Marvin Scholz committed
371
    } else {
372
        /* url has user/pass but libcurl may need to clear any existing settings */
Marvin Scholz's avatar
Marvin Scholz committed
373
        curl_easy_setopt(url->handle, CURLOPT_USERPWD, "");
374
    }
Marvin Scholz's avatar
Marvin Scholz committed
375 376 377
    curl_easy_setopt(url->handle, CURLOPT_URL, url->addurl);
    curl_easy_setopt(url->handle, CURLOPT_POSTFIELDS, post);
    curl_easy_setopt(url->handle, CURLOPT_WRITEHEADER, auth_user);
Karl Heyes's avatar
Karl Heyes committed
378 379
    url->errormsg[0] = '\0';

Philipp Schafft's avatar
Philipp Schafft committed
380
    url->result = AUTH_FAILED;
Marvin Scholz's avatar
Marvin Scholz committed
381
    res = curl_easy_perform(url->handle);
Karl Heyes's avatar
Karl Heyes committed
382

Marvin Scholz's avatar
Marvin Scholz committed
383
    free(userpwd);
384

Marvin Scholz's avatar
Marvin Scholz committed
385 386 387
    if (res) {
        ICECAST_LOG_WARN("auth to server %s failed with %s",
            url->addurl, url->errormsg);
Karl Heyes's avatar
Karl Heyes committed
388 389 390
        return AUTH_FAILED;
    }
    /* we received a response, lets see what it is */
Philipp Schafft's avatar
Philipp Schafft committed
391
    if (url->result == AUTH_FAILED) {
Marvin Scholz's avatar
Marvin Scholz committed
392 393
        ICECAST_LOG_INFO("client auth (%s) failed with \"%s\"",
            url->addurl, url->errormsg);
394
    }
Philipp Schafft's avatar
Philipp Schafft committed
395
    return url->result;
396 397
}

Marvin Scholz's avatar
Marvin Scholz committed
398 399 400
static auth_result auth_url_adduser(auth_t      *auth,
                                    const char  *username,
                                    const char  *password)
Karl Heyes's avatar
Karl Heyes committed
401 402 403 404
{
    return AUTH_FAILED;
}

Marvin Scholz's avatar
Marvin Scholz committed
405
static auth_result auth_url_deleteuser(auth_t *auth, const char *username)
Karl Heyes's avatar
Karl Heyes committed
406 407 408 409
{
    return AUTH_FAILED;
}

Marvin Scholz's avatar
Marvin Scholz committed
410
static auth_result auth_url_listuser(auth_t *auth, xmlNodePtr srcnode)
Karl Heyes's avatar
Karl Heyes committed
411 412 413 414
{
    return AUTH_FAILED;
}

415
int auth_get_url_auth(auth_t *authenticator, config_options_t *options)
Karl Heyes's avatar
Karl Heyes committed
416
{
Marvin Scholz's avatar
Marvin Scholz committed
417 418 419
    auth_url    *url_info;
    const char  *addaction      = "listener_add";
    const char  *removeaction   = "listener_remove";
Karl Heyes's avatar
Karl Heyes committed
420

Marvin Scholz's avatar
Marvin Scholz committed
421 422 423 424
    authenticator->free         = auth_url_clear;
    authenticator->adduser      = auth_url_adduser;
    authenticator->deleteuser   = auth_url_deleteuser;
    authenticator->listuser     = auth_url_listuser;
Karl Heyes's avatar
Karl Heyes committed
425

Marvin Scholz's avatar
Marvin Scholz committed
426 427
    url_info                    = calloc(1, sizeof(auth_url));
    authenticator->state        = url_info;
428 429

    /* default headers */
Marvin Scholz's avatar
Marvin Scholz committed
430 431
    url_info->auth_header       = strdup("icecast-auth-user: 1\r\n");
    url_info->timelimit_header  = strdup("icecast-auth-timelimit:");
Karl Heyes's avatar
Karl Heyes committed
432

433
    /* force auth thread to call function. this makes sure the auth_t is attached to client */
Philipp Schafft's avatar
Philipp Schafft committed
434
    authenticator->authenticate_client = url_add_client;
435

Karl Heyes's avatar
Karl Heyes committed
436
    while(options) {
Philipp Schafft's avatar
Philipp Schafft committed
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
        if(strcmp(options->name, "username") == 0) {
            free(url_info->username);
            url_info->username = strdup(options->value);
        } else if(strcmp(options->name, "password") == 0) {
            free(url_info->password);
            url_info->password = strdup(options->value);
        } else if(strcmp(options->name, "headers") == 0) {
            free(url_info->pass_headers);
            url_info->pass_headers = strdup(options->value);
        } else if(strcmp(options->name, "header_prefix") == 0) {
            free(url_info->prefix_headers);
            url_info->prefix_headers = strdup(options->value);
        } else if(strcmp(options->name, "client_add") == 0) {
            free(url_info->addurl);
            url_info->addurl = strdup(options->value);
        } else if(strcmp(options->name, "client_remove") == 0) {
            authenticator->release_client = url_remove_client;
            free(url_info->removeurl);
            url_info->removeurl = strdup(options->value);
        } else if(strcmp(options->name, "action_add") == 0) {
            addaction = options->value;
        } else if(strcmp(options->name, "action_remove") == 0) {
            removeaction = options->value;
        } else if(strcmp(options->name, "auth_header") == 0) {
            free(url_info->auth_header);
            url_info->auth_header = strdup(options->value);
        } else if (strcmp(options->name, "timelimit_header") == 0) {
            free(url_info->timelimit_header);
            url_info->timelimit_header = strdup(options->value);
        } else {
            ICECAST_LOG_ERROR("Unknown option: %s", options->name);
468
        }
Karl Heyes's avatar
Karl Heyes committed
469 470
        options = options->next;
    }
Philipp Schafft's avatar
Philipp Schafft committed
471 472 473 474

    url_info->addaction = util_url_escape(addaction);
    url_info->removeaction = util_url_escape(removeaction);

475
    url_info->handle = icecast_curl_new(NULL, &url_info->errormsg[0]);
Marvin Scholz's avatar
Marvin Scholz committed
476 477
    if (url_info->handle == NULL) {
        auth_url_clear(authenticator);
Karl Heyes's avatar
Karl Heyes committed
478 479
        return -1;
    }
Philipp Schafft's avatar
Philipp Schafft committed
480

Karl Heyes's avatar
Karl Heyes committed
481 482
    if (url_info->auth_header)
        url_info->auth_header_len = strlen (url_info->auth_header);
483 484
    if (url_info->timelimit_header)
        url_info->timelimit_header_len = strlen (url_info->timelimit_header);
Karl Heyes's avatar
Karl Heyes committed
485

Marvin Scholz's avatar
Marvin Scholz committed
486
    curl_easy_setopt(url_info->handle, CURLOPT_HEADERFUNCTION, handle_returned_header);
Karl Heyes's avatar
Karl Heyes committed
487

Marvin Scholz's avatar
Marvin Scholz committed
488 489 490 491 492
    if (url_info->username && url_info->password) {
        int len = strlen(url_info->username) + strlen(url_info->password) + 2;
        url_info->userpwd = malloc(len);
        snprintf(url_info->userpwd, len, "%s:%s",
            url_info->username, url_info->password);
493 494
    }

495
    ICECAST_LOG_INFO("URL based authentication setup");
Karl Heyes's avatar
Karl Heyes committed
496 497
    return 0;
}