*** empty log message ***
[xiph/unicode.git] / icecast / src / admin.c
blobb903974502c9b740f297175be07169c727ed92de
1 /* Icecast
3 * This program is distributed under the GNU General Public License, version 2.
4 * A copy of this license is included with this source.
6 * Copyright 2000-2004, Jack Moffitt <jack@xiph.org,
7 * Michael Smith <msmith@xiph.org>,
8 * oddsock <oddsock@xiph.org>,
9 * Karl Heyes <karl@xiph.org>
10 * and others (see AUTHORS for details).
13 #ifdef HAVE_CONFIG_H
14 #include <config.h>
15 #endif
17 #include <string.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <time.h>
21 #include <libxml/xmlmemory.h>
22 #include <libxml/parser.h>
23 #include <libxml/tree.h>
25 #include "cfgfile.h"
26 #include "connection.h"
27 #include "refbuf.h"
28 #include "client.h"
29 #include "source.h"
30 #include "global.h"
31 #include "event.h"
32 #include "stats.h"
33 #include "os.h"
34 #include "xslt.h"
36 #include "format.h"
38 #include "logging.h"
39 #ifdef _WIN32
40 #define snprintf _snprintf
41 #endif
43 #define CATMODULE "admin"
45 #define COMMAND_ERROR (-1)
47 /* Mount-specific commands */
48 #define COMMAND_RAW_FALLBACK 1
49 #define COMMAND_METADATA_UPDATE 2
50 #define COMMAND_RAW_SHOW_LISTENERS 3
51 #define COMMAND_RAW_MOVE_CLIENTS 4
53 #define COMMAND_TRANSFORMED_FALLBACK 50
54 #define COMMAND_TRANSFORMED_SHOW_LISTENERS 53
55 #define COMMAND_TRANSFORMED_MOVE_CLIENTS 54
57 /* Global commands */
58 #define COMMAND_RAW_LIST_MOUNTS 101
59 #define COMMAND_RAW_STATS 102
60 #define COMMAND_RAW_LISTSTREAM 103
61 #define COMMAND_PLAINTEXT_LISTSTREAM 104
62 #define COMMAND_TRANSFORMED_LIST_MOUNTS 201
63 #define COMMAND_TRANSFORMED_STATS 202
64 #define COMMAND_TRANSFORMED_LISTSTREAM 203
66 /* Client management commands */
67 #define COMMAND_RAW_KILL_CLIENT 301
68 #define COMMAND_RAW_KILL_SOURCE 302
69 #define COMMAND_TRANSFORMED_KILL_CLIENT 401
70 #define COMMAND_TRANSFORMED_KILL_SOURCE 402
72 #define FALLBACK_RAW_REQUEST "fallbacks"
73 #define FALLBACK_TRANSFORMED_REQUEST "fallbacks.xsl"
74 #define METADATA_REQUEST "metadata"
75 #define LISTCLIENTS_RAW_REQUEST "listclients"
76 #define LISTCLIENTS_TRANSFORMED_REQUEST "listclients.xsl"
77 #define STATS_RAW_REQUEST "stats"
78 #define STATS_TRANSFORMED_REQUEST "stats.xsl"
79 #define LISTMOUNTS_RAW_REQUEST "listmounts"
80 #define LISTMOUNTS_TRANSFORMED_REQUEST "listmounts.xsl"
81 #define STREAMLIST_RAW_REQUEST "streamlist"
82 #define STREAMLIST_TRANSFORMED_REQUEST "streamlist.xsl"
83 #define STREAMLIST_PLAINTEXT_REQUEST "streamlist.txt"
84 #define MOVECLIENTS_RAW_REQUEST "moveclients"
85 #define MOVECLIENTS_TRANSFORMED_REQUEST "moveclients.xsl"
86 #define KILLCLIENT_RAW_REQUEST "killclient"
87 #define KILLCLIENT_TRANSFORMED_REQUEST "killclient.xsl"
88 #define KILLSOURCE_RAW_REQUEST "killsource"
89 #define KILLSOURCE_TRANSFORMED_REQUEST "killsource.xsl"
90 #define ADMIN_XSL_RESPONSE "response.xsl"
91 #define DEFAULT_RAW_REQUEST ""
92 #define DEFAULT_TRANSFORMED_REQUEST ""
94 #define RAW 1
95 #define TRANSFORMED 2
96 #define PLAINTEXT 3
97 int admin_get_command(char *command)
99 if(!strcmp(command, FALLBACK_RAW_REQUEST))
100 return COMMAND_RAW_FALLBACK;
101 else if(!strcmp(command, FALLBACK_TRANSFORMED_REQUEST))
102 return COMMAND_TRANSFORMED_FALLBACK;
103 else if(!strcmp(command, METADATA_REQUEST))
104 return COMMAND_METADATA_UPDATE;
105 else if(!strcmp(command, LISTCLIENTS_RAW_REQUEST))
106 return COMMAND_RAW_SHOW_LISTENERS;
107 else if(!strcmp(command, LISTCLIENTS_TRANSFORMED_REQUEST))
108 return COMMAND_TRANSFORMED_SHOW_LISTENERS;
109 else if(!strcmp(command, STATS_RAW_REQUEST))
110 return COMMAND_RAW_STATS;
111 else if(!strcmp(command, STATS_TRANSFORMED_REQUEST))
112 return COMMAND_TRANSFORMED_STATS;
113 else if(!strcmp(command, "stats.xml")) /* The old way */
114 return COMMAND_RAW_STATS;
115 else if(!strcmp(command, LISTMOUNTS_RAW_REQUEST))
116 return COMMAND_RAW_LIST_MOUNTS;
117 else if(!strcmp(command, LISTMOUNTS_TRANSFORMED_REQUEST))
118 return COMMAND_TRANSFORMED_LIST_MOUNTS;
119 else if(!strcmp(command, STREAMLIST_RAW_REQUEST))
120 return COMMAND_RAW_LISTSTREAM;
121 else if(!strcmp(command, STREAMLIST_PLAINTEXT_REQUEST))
122 return COMMAND_PLAINTEXT_LISTSTREAM;
123 else if(!strcmp(command, MOVECLIENTS_RAW_REQUEST))
124 return COMMAND_RAW_MOVE_CLIENTS;
125 else if(!strcmp(command, MOVECLIENTS_TRANSFORMED_REQUEST))
126 return COMMAND_TRANSFORMED_MOVE_CLIENTS;
127 else if(!strcmp(command, KILLCLIENT_RAW_REQUEST))
128 return COMMAND_RAW_KILL_CLIENT;
129 else if(!strcmp(command, KILLCLIENT_TRANSFORMED_REQUEST))
130 return COMMAND_TRANSFORMED_KILL_CLIENT;
131 else if(!strcmp(command, KILLSOURCE_RAW_REQUEST))
132 return COMMAND_RAW_KILL_SOURCE;
133 else if(!strcmp(command, KILLSOURCE_TRANSFORMED_REQUEST))
134 return COMMAND_TRANSFORMED_KILL_SOURCE;
135 else if(!strcmp(command, DEFAULT_TRANSFORMED_REQUEST))
136 return COMMAND_TRANSFORMED_STATS;
137 else if(!strcmp(command, DEFAULT_RAW_REQUEST))
138 return COMMAND_TRANSFORMED_STATS;
139 else
140 return COMMAND_ERROR;
143 static void command_fallback(client_t *client, source_t *source, int response);
144 static void command_metadata(client_t *client, source_t *source);
145 static void command_show_listeners(client_t *client, source_t *source,
146 int response);
147 static void command_move_clients(client_t *client, source_t *source,
148 int response);
149 static void command_stats(client_t *client, int response);
150 static void command_list_mounts(client_t *client, int response);
151 static void command_kill_client(client_t *client, source_t *source,
152 int response);
153 static void command_kill_source(client_t *client, source_t *source,
154 int response);
155 static void admin_handle_mount_request(client_t *client, source_t *source,
156 int command);
157 static void admin_handle_general_request(client_t *client, int command);
158 static void admin_send_response(xmlDocPtr doc, client_t *client,
159 int response, char *xslt_template);
160 static void html_write(client_t *client, char *fmt, ...);
162 xmlDocPtr admin_build_sourcelist(char *current_source)
164 avl_node *node;
165 source_t *source;
166 xmlNodePtr xmlnode, srcnode;
167 xmlDocPtr doc;
168 char buf[22];
169 time_t now = time(NULL);
171 doc = xmlNewDoc("1.0");
172 xmlnode = xmlNewDocNode(doc, NULL, "icestats", NULL);
173 xmlDocSetRootElement(doc, xmlnode);
175 if (current_source) {
176 xmlNewChild(xmlnode, NULL, "current_source", current_source);
179 node = avl_get_first(global.source_tree);
180 while(node) {
181 source = (source_t *)node->key;
182 if (source->running)
184 srcnode = xmlNewChild(xmlnode, NULL, "source", NULL);
185 xmlSetProp(srcnode, "mount", source->mount);
187 xmlNewChild(srcnode, NULL, "fallback",
188 (source->fallback_mount != NULL)?
189 source->fallback_mount:"");
190 snprintf(buf, sizeof(buf), "%ld", source->listeners);
191 xmlNewChild(srcnode, NULL, "listeners", buf);
192 snprintf(buf, sizeof(buf), "%ld", now - source->con->con_time);
193 xmlNewChild(srcnode, NULL, "Connected", buf);
194 xmlNewChild(srcnode, NULL, "Format",
195 source->format->format_description);
197 node = avl_get_next(node);
199 return(doc);
202 void admin_send_response(xmlDocPtr doc, client_t *client,
203 int response, char *xslt_template)
205 xmlChar *buff = NULL;
206 int len = 0;
207 ice_config_t *config;
208 char *fullpath_xslt_template;
209 int fullpath_xslt_template_len;
210 char *adminwebroot;
212 client->respcode = 200;
213 if (response == RAW) {
214 xmlDocDumpMemory(doc, &buff, &len);
215 html_write(client, "HTTP/1.0 200 OK\r\n"
216 "Content-Length: %d\r\n"
217 "Content-Type: text/xml\r\n"
218 "\r\n", len);
219 html_write(client, buff);
221 if (response == TRANSFORMED) {
222 config = config_get_config();
223 adminwebroot = config->adminroot_dir;
224 config_release_config();
225 fullpath_xslt_template_len = strlen(adminwebroot) +
226 strlen(xslt_template) + 2;
227 fullpath_xslt_template = malloc(fullpath_xslt_template_len);
228 memset(fullpath_xslt_template, '\000', fullpath_xslt_template_len);
229 snprintf(fullpath_xslt_template, fullpath_xslt_template_len, "%s%s%s",
230 adminwebroot, PATH_SEPARATOR, xslt_template);
231 html_write(client, "HTTP/1.0 200 OK\r\n"
232 "Content-Type: text/html\r\n"
233 "\r\n");
234 DEBUG1("Sending XSLT (%s)", fullpath_xslt_template);
235 xslt_transform(doc, fullpath_xslt_template, client);
236 free(fullpath_xslt_template);
238 if (buff) {
239 xmlFree(buff);
242 void admin_handle_request(client_t *client, char *uri)
244 char *mount, *command_string;
245 int command;
247 if(strncmp("/admin/", uri, 7)) {
248 ERROR0("Internal error: admin request isn't");
249 client_send_401(client);
250 return;
253 command_string = uri + 7;
255 DEBUG1("Got command (%s)", command_string);
256 command = admin_get_command(command_string);
258 if(command < 0) {
259 ERROR1("Error parsing command string or unrecognised command: %s",
260 command_string);
261 client_send_400(client, "Unrecognised command");
262 return;
265 mount = httpp_get_query_param(client->parser, "mount");
267 if(mount != NULL) {
268 source_t *source;
270 /* This is a mount request, handle it as such */
271 if(!connection_check_admin_pass(client->parser)) {
272 if(!connection_check_source_pass(client->parser, mount)) {
273 INFO1("Bad or missing password on mount modification admin "
274 "request (command: %s)", command_string);
275 client_send_401(client);
276 return;
280 avl_tree_rlock(global.source_tree);
281 source = source_find_mount_raw(mount);
283 if (source == NULL)
285 WARN2("Admin command %s on non-existent source %s",
286 command_string, mount);
287 avl_tree_unlock(global.source_tree);
288 client_send_400(client, "Source does not exist");
290 else
292 if (source->running == 0)
294 INFO2("Received admin command %s on unavailable mount \"%s\"",
295 command_string, mount);
296 avl_tree_unlock (global.source_tree);
297 client_send_400 (client, "Source is not available");
298 return;
300 INFO2("Received admin command %s on mount \"%s\"",
301 command_string, mount);
302 admin_handle_mount_request(client, source, command);
303 avl_tree_unlock(global.source_tree);
306 else {
308 if (command == COMMAND_PLAINTEXT_LISTSTREAM) {
309 /* this request is used by a slave relay to retrieve
310 mounts from the master, so handle this request
311 validating against the relay password */
312 if(!connection_check_relay_pass(client->parser)) {
313 INFO1("Bad or missing password on admin command "
314 "request (command: %s)", command_string);
315 client_send_401(client);
316 return;
319 else {
320 if(!connection_check_admin_pass(client->parser)) {
321 INFO1("Bad or missing password on admin command "
322 "request (command: %s)", command_string);
323 client_send_401(client);
324 return;
328 admin_handle_general_request(client, command);
332 static void admin_handle_general_request(client_t *client, int command)
334 switch(command) {
335 case COMMAND_RAW_STATS:
336 command_stats(client, RAW);
337 break;
338 case COMMAND_RAW_LIST_MOUNTS:
339 command_list_mounts(client, RAW);
340 break;
341 case COMMAND_RAW_LISTSTREAM:
342 command_list_mounts(client, RAW);
343 break;
344 case COMMAND_PLAINTEXT_LISTSTREAM:
345 command_list_mounts(client, PLAINTEXT);
346 break;
347 case COMMAND_TRANSFORMED_STATS:
348 command_stats(client, TRANSFORMED);
349 break;
350 case COMMAND_TRANSFORMED_LIST_MOUNTS:
351 command_list_mounts(client, TRANSFORMED);
352 break;
353 case COMMAND_TRANSFORMED_LISTSTREAM:
354 command_list_mounts(client, TRANSFORMED);
355 break;
356 case COMMAND_TRANSFORMED_MOVE_CLIENTS:
357 command_list_mounts(client, TRANSFORMED);
358 break;
359 default:
360 WARN0("General admin request not recognised");
361 client_send_400(client, "Unknown admin request");
362 return;
366 static void admin_handle_mount_request(client_t *client, source_t *source,
367 int command)
369 switch(command) {
370 case COMMAND_RAW_FALLBACK:
371 command_fallback(client, source, RAW);
372 break;
373 case COMMAND_METADATA_UPDATE:
374 command_metadata(client, source);
375 break;
376 case COMMAND_RAW_SHOW_LISTENERS:
377 command_show_listeners(client, source, RAW);
378 break;
379 case COMMAND_RAW_MOVE_CLIENTS:
380 command_move_clients(client, source, RAW);
381 break;
382 case COMMAND_RAW_KILL_CLIENT:
383 command_kill_client(client, source, RAW);
384 break;
385 case COMMAND_RAW_KILL_SOURCE:
386 command_kill_source(client, source, RAW);
387 break;
388 case COMMAND_TRANSFORMED_FALLBACK:
389 command_fallback(client, source, RAW);
390 break;
391 case COMMAND_TRANSFORMED_SHOW_LISTENERS:
392 command_show_listeners(client, source, TRANSFORMED);
393 break;
394 case COMMAND_TRANSFORMED_MOVE_CLIENTS:
395 command_move_clients(client, source, TRANSFORMED);
396 break;
397 case COMMAND_TRANSFORMED_KILL_CLIENT:
398 command_kill_client(client, source, TRANSFORMED);
399 break;
400 case COMMAND_TRANSFORMED_KILL_SOURCE:
401 command_kill_source(client, source, TRANSFORMED);
402 break;
403 default:
404 WARN0("Mount request not recognised");
405 client_send_400(client, "Mount request unknown");
406 break;
410 #define COMMAND_REQUIRE(client,name,var) \
411 do { \
412 (var) = httpp_get_query_param((client)->parser, (name)); \
413 if((var) == NULL) { \
414 client_send_400((client), "Missing parameter"); \
415 return; \
417 } while(0);
418 #define COMMAND_OPTIONAL(client,name,var) \
419 (var) = httpp_get_query_param((client)->parser, (name))
421 static void html_success(client_t *client, char *message)
423 int bytes;
425 client->respcode = 200;
426 bytes = sock_write(client->con->sock,
427 "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"
428 "<html><head><title>Admin request successful</title></head>"
429 "<body><p>%s</p></body></html>", message);
430 if(bytes > 0) client->con->sent_bytes = bytes;
431 client_destroy(client);
434 static void html_write(client_t *client, char *fmt, ...)
436 int bytes;
437 va_list ap;
439 va_start(ap, fmt);
440 bytes = sock_write_fmt(client->con->sock, fmt, ap);
441 va_end(ap);
442 if(bytes > 0) client->con->sent_bytes = bytes;
445 static void command_move_clients(client_t *client, source_t *source,
446 int response)
448 char *dest_source;
449 source_t *dest;
450 xmlDocPtr doc;
451 xmlNodePtr node;
452 char buf[255];
453 int parameters_passed = 0;
455 DEBUG0("Doing optional check");
456 if((COMMAND_OPTIONAL(client, "destination", dest_source))) {
457 parameters_passed = 1;
459 DEBUG1("Done optional check (%d)", parameters_passed);
460 if (!parameters_passed) {
461 doc = admin_build_sourcelist(source->mount);
462 admin_send_response(doc, client, response,
463 MOVECLIENTS_TRANSFORMED_REQUEST);
464 xmlFreeDoc(doc);
465 client_destroy(client);
466 return;
469 dest = source_find_mount (dest_source);
471 if (dest == NULL)
473 client_send_400 (client, "No such destination");
474 return;
477 if (strcmp (dest->mount, source->mount) == 0)
479 client_send_400 (client, "supplied mountpoints are identical");
480 return;
483 if (dest->running == 0)
485 client_send_400 (client, "Destination not running");
486 return;
489 doc = xmlNewDoc("1.0");
490 node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
491 xmlDocSetRootElement(doc, node);
493 source_move_clients (source, dest);
495 memset(buf, '\000', sizeof(buf));
496 snprintf (buf, sizeof(buf), "Clients moved from %s to %s",
497 source->mount, dest_source);
498 xmlNewChild(node, NULL, "message", buf);
499 xmlNewChild(node, NULL, "return", "1");
501 admin_send_response(doc, client, response,
502 ADMIN_XSL_RESPONSE);
503 xmlFreeDoc(doc);
504 client_destroy(client);
507 static void command_show_listeners(client_t *client, source_t *source,
508 int response)
510 xmlDocPtr doc;
511 xmlNodePtr node, srcnode, listenernode;
512 avl_node *client_node;
513 client_t *current;
514 char buf[22];
515 char *userAgent = NULL;
516 time_t now = time(NULL);
518 doc = xmlNewDoc("1.0");
519 node = xmlNewDocNode(doc, NULL, "icestats", NULL);
520 srcnode = xmlNewChild(node, NULL, "source", NULL);
521 xmlSetProp(srcnode, "mount", source->mount);
522 xmlDocSetRootElement(doc, node);
524 memset(buf, '\000', sizeof(buf));
525 snprintf(buf, sizeof(buf)-1, "%ld", source->listeners);
526 xmlNewChild(srcnode, NULL, "Listeners", buf);
528 avl_tree_rlock(source->client_tree);
530 client_node = avl_get_first(source->client_tree);
531 while(client_node) {
532 current = (client_t *)client_node->key;
533 listenernode = xmlNewChild(srcnode, NULL, "listener", NULL);
534 xmlNewChild(listenernode, NULL, "IP", current->con->ip);
535 userAgent = httpp_getvar(current->parser, "user-agent");
536 if (userAgent) {
537 xmlNewChild(listenernode, NULL, "UserAgent", userAgent);
539 else {
540 xmlNewChild(listenernode, NULL, "UserAgent", "Unknown");
542 memset(buf, '\000', sizeof(buf));
543 snprintf(buf, sizeof(buf)-1, "%ld", now - current->con->con_time);
544 xmlNewChild(listenernode, NULL, "Connected", buf);
545 memset(buf, '\000', sizeof(buf));
546 snprintf(buf, sizeof(buf)-1, "%lu", current->con->id);
547 xmlNewChild(listenernode, NULL, "ID", buf);
548 client_node = avl_get_next(client_node);
551 avl_tree_unlock(source->client_tree);
552 admin_send_response(doc, client, response,
553 LISTCLIENTS_TRANSFORMED_REQUEST);
554 xmlFreeDoc(doc);
555 client_destroy(client);
558 static void command_kill_source(client_t *client, source_t *source,
559 int response)
561 xmlDocPtr doc;
562 xmlNodePtr node;
564 doc = xmlNewDoc("1.0");
565 node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
566 xmlNewChild(node, NULL, "message", "Source Removed");
567 xmlNewChild(node, NULL, "return", "1");
568 xmlDocSetRootElement(doc, node);
570 source->running = 0;
572 admin_send_response(doc, client, response,
573 ADMIN_XSL_RESPONSE);
574 xmlFreeDoc(doc);
575 client_destroy(client);
578 static void command_kill_client(client_t *client, source_t *source,
579 int response)
581 char *idtext;
582 int id;
583 client_t *listener;
584 xmlDocPtr doc;
585 xmlNodePtr node;
586 char buf[50] = "";
588 COMMAND_REQUIRE(client, "id", idtext);
590 id = atoi(idtext);
592 listener = source_find_client(source, id);
594 doc = xmlNewDoc("1.0");
595 node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
596 xmlDocSetRootElement(doc, node);
597 DEBUG1("Response is %d", response);
599 if(listener != NULL) {
600 INFO1("Admin request: client %d removed", id);
602 /* This tags it for removal on the next iteration of the main source
603 * loop
605 listener->con->error = 1;
606 memset(buf, '\000', sizeof(buf));
607 snprintf(buf, sizeof(buf)-1, "Client %d removed", id);
608 xmlNewChild(node, NULL, "message", buf);
609 xmlNewChild(node, NULL, "return", "1");
611 else {
612 memset(buf, '\000', sizeof(buf));
613 snprintf(buf, sizeof(buf)-1, "Client %d not found", id);
614 xmlNewChild(node, NULL, "message", buf);
615 xmlNewChild(node, NULL, "return", "0");
617 admin_send_response(doc, client, response,
618 ADMIN_XSL_RESPONSE);
619 xmlFreeDoc(doc);
620 client_destroy(client);
623 static void command_fallback(client_t *client, source_t *source,
624 int response)
626 char *fallback;
627 char *old;
629 DEBUG0("Got fallback request");
631 COMMAND_REQUIRE(client, "fallback", fallback);
633 old = source->fallback_mount;
634 source->fallback_mount = strdup(fallback);
635 free(old);
637 html_success(client, "Fallback configured");
640 static void command_metadata(client_t *client, source_t *source)
642 char *action;
643 char *value;
644 format_plugin_t *format;
645 #ifdef USE_YP
646 int i;
647 time_t current_time;
648 #endif
650 DEBUG0("Got metadata update request");
652 COMMAND_REQUIRE(client, "mode", action);
653 COMMAND_REQUIRE(client, "song", value);
655 format = source->format;
656 if (format->type != FORMAT_TYPE_MP3)
658 client_send_400 (client, "Not mp3, cannot update metadata");
659 return;
662 if (strcmp (action, "updinfo") != 0)
664 client_send_400 (client, "No such action");
665 return;
667 if (format->set_tag)
669 if (value)
670 format->set_tag (format, "title", value);
673 DEBUG2("Metadata on mountpoint %s changed to \"%s\"",
674 source->mount, value);
675 stats_event(source->mount, "title", value);
677 #ifdef USE_YP
678 /* If we get an update on the mountpoint, force a
679 yp touch */
680 current_time = time(NULL);
681 for (i=0; i<source->num_yp_directories; i++) {
682 source->ypdata[i]->yp_last_touch = current_time -
683 source->ypdata[i]->yp_touch_interval + 2;
685 #endif
687 html_success(client, "Metadata update successful");
690 static void command_stats(client_t *client, int response) {
691 xmlDocPtr doc;
693 DEBUG0("Stats request, sending xml stats");
695 stats_get_xml(&doc);
696 admin_send_response(doc, client, response, STATS_TRANSFORMED_REQUEST);
697 xmlFreeDoc(doc);
698 client_destroy(client);
699 return;
702 static void command_list_mounts(client_t *client, int response) {
703 xmlDocPtr doc;
704 avl_node *node;
705 source_t *source;
707 DEBUG0("List mounts request");
710 if (response == PLAINTEXT)
712 node = avl_get_first(global.source_tree);
713 html_write(client,
714 "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
715 while(node) {
716 source = (source_t *)node->key;
717 html_write(client, "%s\n", source->mount);
718 node = avl_get_next(node);
721 else {
723 doc = admin_build_sourcelist(NULL);
725 admin_send_response(doc, client, response,
726 LISTMOUNTS_TRANSFORMED_REQUEST);
727 xmlFreeDoc(doc);
729 client_destroy(client);
731 return;