admin.c 26.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
/* 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).
 */

13
14
15
16
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

17
18
#include <string.h>
#include <stdlib.h>
19
20
#include <stdarg.h>
#include <time.h>
21
22
23
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
24

25
#include "cfgfile.h"
26
27
28
29
30
31
32
#include "connection.h"
#include "refbuf.h"
#include "client.h"
#include "source.h"
#include "global.h"
#include "event.h"
#include "stats.h"
33
#include "os.h"
34
#include "xslt.h"
35
36

#include "format.h"
37
#include "format_mp3.h"
38
39

#include "logging.h"
40
#include "auth.h"
Ed "oddsock" Zaleski's avatar
Ed "oddsock" Zaleski committed
41
42
43
#ifdef _WIN32
#define snprintf _snprintf
#endif
44
45
46

#define CATMODULE "admin"

47
48
49
#define COMMAND_ERROR             (-1)

/* Mount-specific commands */
50
#define COMMAND_RAW_FALLBACK        1
51
#define COMMAND_METADATA_UPDATE     2
52
53
#define COMMAND_RAW_SHOW_LISTENERS  3
#define COMMAND_RAW_MOVE_CLIENTS    4
54
#define COMMAND_RAW_MANAGEAUTH      5
55
56
57
58

#define COMMAND_TRANSFORMED_FALLBACK        50
#define COMMAND_TRANSFORMED_SHOW_LISTENERS  53
#define COMMAND_TRANSFORMED_MOVE_CLIENTS    54
59
#define COMMAND_TRANSFORMED_MANAGEAUTH      55
60
61

/* Global commands */
Karl Heyes's avatar
Karl Heyes committed
62
63
64
65
66
67
68
#define COMMAND_RAW_LIST_MOUNTS             101
#define COMMAND_RAW_STATS                   102
#define COMMAND_RAW_LISTSTREAM              103
#define COMMAND_PLAINTEXT_LISTSTREAM        104
#define COMMAND_TRANSFORMED_LIST_MOUNTS     201
#define COMMAND_TRANSFORMED_STATS           202
#define COMMAND_TRANSFORMED_LISTSTREAM      203
69

70
/* Client management commands */
Karl Heyes's avatar
Karl Heyes committed
71
72
73
74
#define COMMAND_RAW_KILL_CLIENT             301
#define COMMAND_RAW_KILL_SOURCE             302
#define COMMAND_TRANSFORMED_KILL_CLIENT     401
#define COMMAND_TRANSFORMED_KILL_SOURCE     402
75
76
77
78
79
80
81
82
83
84
85
86

#define FALLBACK_RAW_REQUEST "fallbacks"
#define FALLBACK_TRANSFORMED_REQUEST "fallbacks.xsl"
#define METADATA_REQUEST "metadata"
#define LISTCLIENTS_RAW_REQUEST "listclients"
#define LISTCLIENTS_TRANSFORMED_REQUEST "listclients.xsl"
#define STATS_RAW_REQUEST "stats"
#define STATS_TRANSFORMED_REQUEST "stats.xsl"
#define LISTMOUNTS_RAW_REQUEST "listmounts"
#define LISTMOUNTS_TRANSFORMED_REQUEST "listmounts.xsl"
#define STREAMLIST_RAW_REQUEST "streamlist"
#define STREAMLIST_TRANSFORMED_REQUEST "streamlist.xsl"
87
#define STREAMLIST_PLAINTEXT_REQUEST "streamlist.txt"
88
89
90
91
92
93
94
#define MOVECLIENTS_RAW_REQUEST "moveclients"
#define MOVECLIENTS_TRANSFORMED_REQUEST "moveclients.xsl"
#define KILLCLIENT_RAW_REQUEST "killclient"
#define KILLCLIENT_TRANSFORMED_REQUEST "killclient.xsl"
#define KILLSOURCE_RAW_REQUEST "killsource"
#define KILLSOURCE_TRANSFORMED_REQUEST "killsource.xsl"
#define ADMIN_XSL_RESPONSE "response.xsl"
95
96
#define MANAGEAUTH_RAW_REQUEST "manageauth"
#define MANAGEAUTH_TRANSFORMED_REQUEST "manageauth.xsl"
97
98
99
100
101
#define DEFAULT_RAW_REQUEST ""
#define DEFAULT_TRANSFORMED_REQUEST ""

#define RAW         1
#define TRANSFORMED 2
102
#define PLAINTEXT   3
103
104
int admin_get_command(char *command)
{
105
106
107
108
109
    if(!strcmp(command, FALLBACK_RAW_REQUEST))
        return COMMAND_RAW_FALLBACK;
    else if(!strcmp(command, FALLBACK_TRANSFORMED_REQUEST))
        return COMMAND_TRANSFORMED_FALLBACK;
    else if(!strcmp(command, METADATA_REQUEST))
110
        return COMMAND_METADATA_UPDATE;
111
112
113
114
115
    else if(!strcmp(command, LISTCLIENTS_RAW_REQUEST))
        return COMMAND_RAW_SHOW_LISTENERS;
    else if(!strcmp(command, LISTCLIENTS_TRANSFORMED_REQUEST))
        return COMMAND_TRANSFORMED_SHOW_LISTENERS;
    else if(!strcmp(command, STATS_RAW_REQUEST))
116
        return COMMAND_RAW_STATS;
117
118
    else if(!strcmp(command, STATS_TRANSFORMED_REQUEST))
        return COMMAND_TRANSFORMED_STATS;
119
120
    else if(!strcmp(command, "stats.xml")) /* The old way */
        return COMMAND_RAW_STATS;
121
122
123
124
125
    else if(!strcmp(command, LISTMOUNTS_RAW_REQUEST))
        return COMMAND_RAW_LIST_MOUNTS;
    else if(!strcmp(command, LISTMOUNTS_TRANSFORMED_REQUEST))
        return COMMAND_TRANSFORMED_LIST_MOUNTS;
    else if(!strcmp(command, STREAMLIST_RAW_REQUEST))
126
        return COMMAND_RAW_LISTSTREAM;
127
128
    else if(!strcmp(command, STREAMLIST_PLAINTEXT_REQUEST))
        return COMMAND_PLAINTEXT_LISTSTREAM;
129
130
131
132
133
134
135
136
137
138
139
140
    else if(!strcmp(command, MOVECLIENTS_RAW_REQUEST))
        return COMMAND_RAW_MOVE_CLIENTS;
    else if(!strcmp(command, MOVECLIENTS_TRANSFORMED_REQUEST))
        return COMMAND_TRANSFORMED_MOVE_CLIENTS;
    else if(!strcmp(command, KILLCLIENT_RAW_REQUEST))
        return COMMAND_RAW_KILL_CLIENT;
    else if(!strcmp(command, KILLCLIENT_TRANSFORMED_REQUEST))
        return COMMAND_TRANSFORMED_KILL_CLIENT;
    else if(!strcmp(command, KILLSOURCE_RAW_REQUEST))
        return COMMAND_RAW_KILL_SOURCE;
    else if(!strcmp(command, KILLSOURCE_TRANSFORMED_REQUEST))
        return COMMAND_TRANSFORMED_KILL_SOURCE;
141
142
143
144
    else if(!strcmp(command, MANAGEAUTH_RAW_REQUEST))
        return COMMAND_RAW_MANAGEAUTH;
    else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST))
        return COMMAND_TRANSFORMED_MANAGEAUTH;
145
146
147
148
    else if(!strcmp(command, DEFAULT_TRANSFORMED_REQUEST))
        return COMMAND_TRANSFORMED_STATS;
    else if(!strcmp(command, DEFAULT_RAW_REQUEST))
        return COMMAND_TRANSFORMED_STATS;
149
150
151
152
    else
        return COMMAND_ERROR;
}

153
static void command_fallback(client_t *client, source_t *source, int response);
154
static void command_metadata(client_t *client, source_t *source);
155
156
157
158
159
160
161
162
static void command_show_listeners(client_t *client, source_t *source,
        int response);
static void command_move_clients(client_t *client, source_t *source,
        int response);
static void command_stats(client_t *client, int response);
static void command_list_mounts(client_t *client, int response);
static void command_kill_client(client_t *client, source_t *source,
        int response);
163
164
static void command_manageauth(client_t *client, source_t *source,
        int response);
165
166
static void command_kill_source(client_t *client, source_t *source,
        int response);
167
168
169
static void admin_handle_mount_request(client_t *client, source_t *source,
        int command);
static void admin_handle_general_request(client_t *client, int command);
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
static void admin_send_response(xmlDocPtr doc, client_t *client, 
        int response, char *xslt_template);
static void html_write(client_t *client, char *fmt, ...);

xmlDocPtr admin_build_sourcelist(char *current_source)
{
    avl_node *node;
    source_t *source;
    xmlNodePtr xmlnode, srcnode;
    xmlDocPtr doc;
    char buf[22];
    time_t now = time(NULL);

    doc = xmlNewDoc("1.0");
    xmlnode = xmlNewDocNode(doc, NULL, "icestats", NULL);
    xmlDocSetRootElement(doc, xmlnode);
186

187
188
189
190
191
192
193
    if (current_source) {
        xmlNewChild(xmlnode, NULL, "current_source", current_source);
    }

    node = avl_get_first(global.source_tree);
    while(node) {
        source = (source_t *)node->key;
194
195
196
197
        if (source->running)
        {
            srcnode = xmlNewChild(xmlnode, NULL, "source", NULL);
            xmlSetProp(srcnode, "mount", source->mount);
198

199
            xmlNewChild(srcnode, NULL, "fallback", 
200
201
                    (source->fallback_mount != NULL)?
                    source->fallback_mount:"");
202
203
            snprintf(buf, sizeof(buf), "%ld", source->listeners);
            xmlNewChild(srcnode, NULL, "listeners", buf);
Karl Heyes's avatar
Karl Heyes committed
204
205
            snprintf(buf, sizeof(buf), "%lu",
                    (unsigned long)(now - source->con->con_time));
206
207
208
            xmlNewChild(srcnode, NULL, "Connected", buf);
            xmlNewChild(srcnode, NULL, "Format", 
                    source->format->format_description);
209
210
211
212
            if (source->authenticator) {
                xmlNewChild(srcnode, NULL, "authenticator", 
                    source->authenticator->type);
            }
213
        }
214
215
216
217
218
219
220
221
        node = avl_get_next(node);
    }
    return(doc);
}

void admin_send_response(xmlDocPtr doc, client_t *client, 
        int response, char *xslt_template)
{
222
    xmlChar *buff = NULL;
223
224
225
226
227
228
229
230
    int len = 0;
    ice_config_t *config;
    char *fullpath_xslt_template;
    int fullpath_xslt_template_len;
    char *adminwebroot;

    client->respcode = 200;
    if (response == RAW) {
231
        xmlDocDumpMemory(doc, &buff, &len);
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
        html_write(client, "HTTP/1.0 200 OK\r\n"
               "Content-Length: %d\r\n"
               "Content-Type: text/xml\r\n"
               "\r\n", len);
        html_write(client, buff);
    }
    if (response == TRANSFORMED) {
        config = config_get_config();
        adminwebroot = config->adminroot_dir;
        config_release_config();
        fullpath_xslt_template_len = strlen(adminwebroot) + 
            strlen(xslt_template) + 2;
        fullpath_xslt_template = malloc(fullpath_xslt_template_len);
        memset(fullpath_xslt_template, '\000', fullpath_xslt_template_len);
        snprintf(fullpath_xslt_template, fullpath_xslt_template_len, "%s%s%s",
            adminwebroot, PATH_SEPARATOR, xslt_template);
        html_write(client, "HTTP/1.0 200 OK\r\n"
               "Content-Type: text/html\r\n"
               "\r\n");
        DEBUG1("Sending XSLT (%s)", fullpath_xslt_template);
        xslt_transform(doc, fullpath_xslt_template, client);
        free(fullpath_xslt_template);
    }
    if (buff) {
        xmlFree(buff);
    }
}
259
260
261
262
263
264
265
266
267
268
269
270
271
void admin_handle_request(client_t *client, char *uri)
{
    char *mount, *command_string;
    int command;

    if(strncmp("/admin/", uri, 7)) {
        ERROR0("Internal error: admin request isn't");
        client_send_401(client);
        return;
    }

    command_string = uri + 7;

272
    DEBUG1("Got command (%s)", command_string);
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
    command = admin_get_command(command_string);

    if(command < 0) {
        ERROR1("Error parsing command string or unrecognised command: %s",
                command_string);
        client_send_400(client, "Unrecognised command");
        return;
    }

    mount = httpp_get_query_param(client->parser, "mount");

    if(mount != NULL) {
        source_t *source;

        /* This is a mount request, handle it as such */
288
289
        if(!connection_check_admin_pass(client->parser)) {
            if(!connection_check_source_pass(client->parser, mount)) {
290
                INFO1("Bad or missing password on mount modification admin "
291
292
293
294
                      "request (command: %s)", command_string);
                client_send_401(client);
                return;
            }
295
296
297
        }
        
        avl_tree_rlock(global.source_tree);
Michael Smith's avatar
Michael Smith committed
298
        source = source_find_mount_raw(mount);
299

300
301
        if (source == NULL)
        {
302
303
            WARN2("Admin command %s on non-existent source %s", 
                    command_string, mount);
304
            avl_tree_unlock(global.source_tree);
305
306
            client_send_400(client, "Source does not exist");
        }
307
308
        else
        {
309
310
311
312
313
314
315
316
            if (source->running == 0)
            {
                INFO2("Received admin command %s on unavailable mount \"%s\"",
                        command_string, mount);
                avl_tree_unlock (global.source_tree);
                client_send_400 (client, "Source is not available");
                return;
            }
317
318
319
            INFO2("Received admin command %s on mount \"%s\"", 
                    command_string, mount);
            admin_handle_mount_request(client, source, command);
320
            avl_tree_unlock(global.source_tree);
321
        }
322
323
324
    }
    else {

325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
        if (command == COMMAND_PLAINTEXT_LISTSTREAM) {
        /* this request is used by a slave relay to retrieve
           mounts from the master, so handle this request
           validating against the relay password */
            if(!connection_check_relay_pass(client->parser)) {
                INFO1("Bad or missing password on admin command "
                      "request (command: %s)", command_string);
                client_send_401(client);
                return;
            }
        }
        else {
            if(!connection_check_admin_pass(client->parser)) {
                INFO1("Bad or missing password on admin command "
                      "request (command: %s)", command_string);
                client_send_401(client);
                return;
            }
343
344
345
346
347
348
349
350
351
352
        }
        
        admin_handle_general_request(client, command);
    }
}

static void admin_handle_general_request(client_t *client, int command)
{
    switch(command) {
        case COMMAND_RAW_STATS:
353
            command_stats(client, RAW);
354
            break;
355
356
        case COMMAND_RAW_LIST_MOUNTS:
            command_list_mounts(client, RAW);
357
358
            break;
        case COMMAND_RAW_LISTSTREAM:
359
360
            command_list_mounts(client, RAW);
            break;
361
362
363
        case COMMAND_PLAINTEXT_LISTSTREAM:
            command_list_mounts(client, PLAINTEXT);
            break;
364
365
366
367
368
369
370
371
372
373
374
        case COMMAND_TRANSFORMED_STATS:
            command_stats(client, TRANSFORMED);
            break;
        case COMMAND_TRANSFORMED_LIST_MOUNTS:
            command_list_mounts(client, TRANSFORMED);
            break;
        case COMMAND_TRANSFORMED_LISTSTREAM:
            command_list_mounts(client, TRANSFORMED);
            break;
        case COMMAND_TRANSFORMED_MOVE_CLIENTS:
            command_list_mounts(client, TRANSFORMED);
375
            break;
376
377
378
379
380
381
382
383
384
385
386
        default:
            WARN0("General admin request not recognised");
            client_send_400(client, "Unknown admin request");
            return;
    }
}

static void admin_handle_mount_request(client_t *client, source_t *source, 
        int command)
{
    switch(command) {
387
388
        case COMMAND_RAW_FALLBACK:
            command_fallback(client, source, RAW);
389
390
391
392
            break;
        case COMMAND_METADATA_UPDATE:
            command_metadata(client, source);
            break;
393
394
395
396
397
398
399
400
        case COMMAND_RAW_SHOW_LISTENERS:
            command_show_listeners(client, source, RAW);
            break;
        case COMMAND_RAW_MOVE_CLIENTS:
            command_move_clients(client, source, RAW);
            break;
        case COMMAND_RAW_KILL_CLIENT:
            command_kill_client(client, source, RAW);
401
            break;
402
403
        case COMMAND_RAW_KILL_SOURCE:
            command_kill_source(client, source, RAW);
404
            break;
405
406
        case COMMAND_TRANSFORMED_FALLBACK:
            command_fallback(client, source, RAW);
407
            break;
408
409
410
411
412
413
414
415
416
417
418
        case COMMAND_TRANSFORMED_SHOW_LISTENERS:
            command_show_listeners(client, source, TRANSFORMED);
            break;
        case COMMAND_TRANSFORMED_MOVE_CLIENTS:
            command_move_clients(client, source, TRANSFORMED);
            break;
        case COMMAND_TRANSFORMED_KILL_CLIENT:
            command_kill_client(client, source, TRANSFORMED);
            break;
        case COMMAND_TRANSFORMED_KILL_SOURCE:
            command_kill_source(client, source, TRANSFORMED);
419
            break;
420
421
422
423
424
425
        case COMMAND_TRANSFORMED_MANAGEAUTH:
            command_manageauth(client, source, TRANSFORMED);
            break;
        case COMMAND_RAW_MANAGEAUTH:
            command_manageauth(client, source, RAW);
            break;
426
427
428
        default:
            WARN0("Mount request not recognised");
            client_send_400(client, "Mount request unknown");
429
            break;
430
431
432
433
434
435
436
437
438
439
440
    }
}

#define COMMAND_REQUIRE(client,name,var) \
    do { \
        (var) = httpp_get_query_param((client)->parser, (name)); \
        if((var) == NULL) { \
            client_send_400((client), "Missing parameter"); \
            return; \
        } \
    } while(0);
441
442
#define COMMAND_OPTIONAL(client,name,var) \
    (var) = httpp_get_query_param((client)->parser, (name))
443

444
static void html_success(client_t *client, char *message)
445
446
447
448
449
450
451
452
453
454
455
456
{
    int bytes;

    client->respcode = 200;
    bytes = sock_write(client->con->sock, 
            "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" 
            "<html><head><title>Admin request successful</title></head>"
            "<body><p>%s</p></body></html>", message);
    if(bytes > 0) client->con->sent_bytes = bytes;
    client_destroy(client);
}

457
458
459
460
461
462
463
464
465
466
467
static void html_write(client_t *client, char *fmt, ...)
{
    int bytes;
    va_list ap;

    va_start(ap, fmt);
    bytes = sock_write_fmt(client->con->sock, fmt, ap);
    va_end(ap);
    if(bytes > 0) client->con->sent_bytes = bytes;
}

468
469
static void command_move_clients(client_t *client, source_t *source,
    int response)
470
471
472
{
    char *dest_source;
    source_t *dest;
473
474
475
476
477
478
    xmlDocPtr doc;
    xmlNodePtr node;
    char buf[255];
    int parameters_passed = 0;

    DEBUG0("Doing optional check");
479
    if((COMMAND_OPTIONAL(client, "destination", dest_source))) {
480
481
482
483
484
485
486
487
488
489
490
        parameters_passed = 1;
    }
    DEBUG1("Done optional check (%d)", parameters_passed);
    if (!parameters_passed) {
        doc = admin_build_sourcelist(source->mount);
        admin_send_response(doc, client, response, 
             MOVECLIENTS_TRANSFORMED_REQUEST);
        xmlFreeDoc(doc);
        client_destroy(client);
        return;
    }
491

492
493
494
495
496
497
498
499
    dest = source_find_mount (dest_source);

    if (dest == NULL)
    {
        client_send_400 (client, "No such destination");
        return;
    }

Karl Heyes's avatar
Karl Heyes committed
500
    if (strcmp (dest->mount, source->mount) == 0)
501
502
503
504
505
    {
        client_send_400 (client, "supplied mountpoints are identical");
        return;
    }

506
507
508
    if (dest->running == 0)
    {
        client_send_400 (client, "Destination not running");
509
510
511
        return;
    }

512
513
514
515
    doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
    xmlDocSetRootElement(doc, node);

516
    source_move_clients (source, dest);
517

518
    memset(buf, '\000', sizeof(buf));
519
520
    snprintf (buf, sizeof(buf), "Clients moved from %s to %s",
            source->mount, dest_source);
521
522
523
524
525
526
527
    xmlNewChild(node, NULL, "message", buf);
    xmlNewChild(node, NULL, "return", "1");

    admin_send_response(doc, client, response, 
        ADMIN_XSL_RESPONSE);
    xmlFreeDoc(doc);
    client_destroy(client);
528
529
}

530
531
static void command_show_listeners(client_t *client, source_t *source,
    int response)
532
{
533
534
    xmlDocPtr doc;
    xmlNodePtr node, srcnode, listenernode;
535
536
    avl_node *client_node;
    client_t *current;
537
538
    char buf[22];
    char *userAgent = NULL;
539
540
    time_t now = time(NULL);

541
542
543
544
545
    doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(doc, NULL, "icestats", NULL);
    srcnode = xmlNewChild(node, NULL, "source", NULL);
    xmlSetProp(srcnode, "mount", source->mount);
    xmlDocSetRootElement(doc, node);
546

547
    memset(buf, '\000', sizeof(buf));
548
    snprintf(buf, sizeof(buf)-1, "%ld", source->listeners);
549
    xmlNewChild(srcnode, NULL, "Listeners", buf);
550
551
552
553
554
555

    avl_tree_rlock(source->client_tree);

    client_node = avl_get_first(source->client_tree);
    while(client_node) {
        current = (client_t *)client_node->key;
556
557
558
559
560
561
562
563
564
565
        listenernode = xmlNewChild(srcnode, NULL, "listener", NULL);
        xmlNewChild(listenernode, NULL, "IP", current->con->ip);
        userAgent = httpp_getvar(current->parser, "user-agent");
        if (userAgent) {
            xmlNewChild(listenernode, NULL, "UserAgent", userAgent);
        }
        else {
            xmlNewChild(listenernode, NULL, "UserAgent", "Unknown");
        }
        memset(buf, '\000', sizeof(buf));
566
        snprintf(buf, sizeof(buf)-1, "%ld", now - current->con->con_time);
567
568
        xmlNewChild(listenernode, NULL, "Connected", buf);
        memset(buf, '\000', sizeof(buf));
569
        snprintf(buf, sizeof(buf)-1, "%lu", current->con->id);
570
        xmlNewChild(listenernode, NULL, "ID", buf);
571
572
573
        if (current->username) {
            xmlNewChild(listenernode, NULL, "username", current->username);
        }
574
575
576
577
        client_node = avl_get_next(client_node);
    }

    avl_tree_unlock(source->client_tree);
578
579
580
    admin_send_response(doc, client, response, 
        LISTCLIENTS_TRANSFORMED_REQUEST);
    xmlFreeDoc(doc);
581
582
583
    client_destroy(client);
}

584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
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
static void command_manageauth(client_t *client, source_t *source,
    int response)
{
    xmlDocPtr doc;
    xmlNodePtr node, srcnode, msgnode;
    char *action = NULL;
    char *username = NULL;
    char *password = NULL;
    char *message = NULL;
    int ret = AUTH_OK;

    if((COMMAND_OPTIONAL(client, "action", action))) {
        if (!strcmp(action, "add")) {
            COMMAND_REQUIRE(client, "username", username);
            COMMAND_REQUIRE(client, "password", password);
            ret = auth_adduser(source, username, password);
            if (ret == AUTH_FAILED) {
                message = strdup("User add failed - check the icecast error log");
            }
            if (ret == AUTH_USERADDED) {
                message = strdup("User added");
            }
            if (ret == AUTH_USEREXISTS) {
                message = strdup("User already exists - not added");
            }
        }
        if (!strcmp(action, "delete")) {
            COMMAND_REQUIRE(client, "username", username);
            ret = auth_deleteuser(source, username);
            if (ret == AUTH_FAILED) {
                message = strdup("User delete failed - check the icecast error log");
            }
            if (ret == AUTH_USERDELETED) {
                message = strdup("User deleted");
            }
        }
    }

    doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(doc, NULL, "icestats", NULL);
    srcnode = xmlNewChild(node, NULL, "source", NULL);
    xmlSetProp(srcnode, "mount", source->mount);

    if (message) {
        msgnode = xmlNewChild(node, NULL, "iceresponse", NULL);
        xmlNewChild(msgnode, NULL, "message", message);
    }

    xmlDocSetRootElement(doc, node);

    auth_get_userlist(source, srcnode);

    admin_send_response(doc, client, response, 
        MANAGEAUTH_TRANSFORMED_REQUEST);
    if (message) {
        free(message);
    }
    xmlFreeDoc(doc);
    client_destroy(client);
}

645
646
static void command_kill_source(client_t *client, source_t *source,
    int response)
647
{
648
649
650
651
652
653
654
655
656
    xmlDocPtr doc;
    xmlNodePtr node;

    doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
    xmlNewChild(node, NULL, "message", "Source Removed");
    xmlNewChild(node, NULL, "return", "1");
    xmlDocSetRootElement(doc, node);

657
658
    source->running = 0;

659
660
661
662
    admin_send_response(doc, client, response, 
        ADMIN_XSL_RESPONSE);
    xmlFreeDoc(doc);
    client_destroy(client);
663
664
}

665
666
static void command_kill_client(client_t *client, source_t *source,
    int response)
667
668
669
670
{
    char *idtext;
    int id;
    client_t *listener;
671
672
673
    xmlDocPtr doc;
    xmlNodePtr node;
    char buf[50] = "";
674
675
676
677
678
679
680

    COMMAND_REQUIRE(client, "id", idtext);

    id = atoi(idtext);

    listener = source_find_client(source, id);

681
682
683
684
685
    doc = xmlNewDoc("1.0");
    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
    xmlDocSetRootElement(doc, node);
    DEBUG1("Response is %d", response);

686
687
688
689
690
691
692
    if(listener != NULL) {
        INFO1("Admin request: client %d removed", id);

        /* This tags it for removal on the next iteration of the main source
         * loop
         */
        listener->con->error = 1;
693
694
695
696
        memset(buf, '\000', sizeof(buf));
        snprintf(buf, sizeof(buf)-1, "Client %d removed", id);
        xmlNewChild(node, NULL, "message", buf);
        xmlNewChild(node, NULL, "return", "1");
697
698
    }
    else {
699
700
701
702
        memset(buf, '\000', sizeof(buf));
        snprintf(buf, sizeof(buf)-1, "Client %d not found", id);
        xmlNewChild(node, NULL, "message", buf);
        xmlNewChild(node, NULL, "return", "0");
703
    }
704
705
706
707
    admin_send_response(doc, client, response, 
        ADMIN_XSL_RESPONSE);
    xmlFreeDoc(doc);
    client_destroy(client);
708
709
}

710
711
static void command_fallback(client_t *client, source_t *source,
    int response)
712
713
714
715
716
717
718
719
720
721
722
723
{
    char *fallback;
    char *old;

    DEBUG0("Got fallback request");

    COMMAND_REQUIRE(client, "fallback", fallback);

    old = source->fallback_mount;
    source->fallback_mount = strdup(fallback);
    free(old);

724
    html_success(client, "Fallback configured");
725
726
727
728
729
730
}

static void command_metadata(client_t *client, source_t *source)
{
    char *action;
    char *value;
731
    mp3_state *state;
732
733
734
735
#ifdef USE_YP
    int i;
    time_t current_time;
#endif
736
737
738
739
740
741

    DEBUG0("Got metadata update request");

    COMMAND_REQUIRE(client, "mode", action);
    COMMAND_REQUIRE(client, "song", value);

742
    if (source->format->type != FORMAT_TYPE_MP3)
Karl Heyes's avatar
Karl Heyes committed
743
744
    {
        client_send_400 (client, "Not mp3, cannot update metadata");
745
746
747
        return;
    }

Karl Heyes's avatar
Karl Heyes committed
748
749
750
    if (strcmp (action, "updinfo") != 0)
    {
        client_send_400 (client, "No such action");
751
752
        return;
    }
753
754
755
756
757
758
759
760

    state = source->format->_state;

    thread_mutex_lock(&(state->lock));
    free(state->metadata);
    state->metadata = strdup(value);
    state->metadata_age++;
    thread_mutex_unlock(&(state->lock));
761

762
763
    DEBUG2("Metadata on mountpoint %s changed to \"%s\"", 
        source->mount, value);
764
    stats_event(source->mount, "title", value);
Karl Heyes's avatar
Karl Heyes committed
765

766
767
768
769
770
771
772
773
774
775
#ifdef USE_YP
    /* If we get an update on the mountpoint, force a
       yp touch */
    current_time = time(NULL);
    for (i=0; i<source->num_yp_directories; i++) {
        source->ypdata[i]->yp_last_touch = current_time - 
            source->ypdata[i]->yp_touch_interval + 2;
    }
#endif

776
    html_success(client, "Metadata update successful");
777
778
}

779
780
781
static void command_stats(client_t *client, int response) {
    xmlDocPtr doc;

782
783
    DEBUG0("Stats request, sending xml stats");

784
785
786
    stats_get_xml(&doc);
    admin_send_response(doc, client, response, STATS_TRANSFORMED_REQUEST);
    xmlFreeDoc(doc);
787
788
789
790
    client_destroy(client);
    return;
}

Karl Heyes's avatar
Karl Heyes committed
791
792
static void command_list_mounts(client_t *client, int response)
{
793
794
    avl_node *node;
    source_t *source;
795
796
797

    DEBUG0("List mounts request");

Karl Heyes's avatar
Karl Heyes committed
798
    avl_tree_rlock (global.source_tree);
799
800
    if (response == PLAINTEXT)
    {
Karl Heyes's avatar
Karl Heyes committed
801
802
803
804
805
        char buffer [4096], *buf = buffer;
        unsigned remaining = sizeof (buffer);
        int ret = sprintf (buffer,
                "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");

806
        node = avl_get_first(global.source_tree);
Karl Heyes's avatar
Karl Heyes committed
807
808
809
810
        while (node && ret > 0 && ret < remaining)
        {
            remaining -= ret;
            buf += ret;
811
            source = (source_t *)node->key;
Karl Heyes's avatar
Karl Heyes committed
812
            ret = snprintf (buf, remaining, "%s\n", source->mount);
813
814
            node = avl_get_next(node);
        }
Karl Heyes's avatar
Karl Heyes committed
815
816
817
818
819
820
821
822
        avl_tree_unlock (global.source_tree);
        /* handle last line */
        if (ret > 0 && ret < remaining)
        {
            remaining -= ret;
            buf += ret;
        }
        sock_write_bytes (client->con->sock, buffer, sizeof (buffer)-remaining);
823
    }
Karl Heyes's avatar
Karl Heyes committed
824
825
826
827
    else
    {
        xmlDocPtr doc = admin_build_sourcelist(NULL);
        avl_tree_unlock (global.source_tree);
828
829
830
831
832

        admin_send_response(doc, client, response, 
            LISTMOUNTS_TRANSFORMED_REQUEST);
        xmlFreeDoc(doc);
    }
833
    client_destroy(client);
834

835
836
837
    return;
}