From b3d5a657d2aed13701e6a1980e87c13033eab82d Mon Sep 17 00:00:00 2001
From: oddsock <oddsock@xiph.org>
Date: Tue, 7 Dec 2004 22:29:46 +0000
Subject: [PATCH] add a new web admin feature which allows the updating of
 metadata (now that we support metadata updates for most stream types).  This
 is an new admin feature.

add cluster-password to the config to allow for future clustering of relays on the xiph stream directory.

svn path=/icecast/trunk/icecast/; revision=8345
---
 admin/Makefile.am           |  2 +-
 admin/listclients.xsl       |  1 +
 admin/listmounts.xsl        |  1 +
 admin/manageauth.xsl        |  1 +
 admin/stats.xsl             |  1 +
 admin/updatemetadata.xsl    | 55 +++++++++++++++++++++++
 conf/icecast_minimal.xml.in |  4 +-
 src/admin.c                 | 89 ++++++++++++++++++++++++++++++++-----
 src/cfgfile.c               |  7 +++
 src/cfgfile.h               |  1 +
 src/yp.c                    | 22 +++++++++
 src/yp.h                    |  1 +
 12 files changed, 169 insertions(+), 16 deletions(-)
 create mode 100644 admin/updatemetadata.xsl

diff --git a/admin/Makefile.am b/admin/Makefile.am
index 3a298c24..0bc58b45 100644
--- a/admin/Makefile.am
+++ b/admin/Makefile.am
@@ -4,5 +4,5 @@ AUTOMAKE_OPTIONS = foreign
 
 admindir = $(pkgdatadir)/admin
 dist_admin_DATA = listclients.xsl listmounts.xsl moveclients.xsl response.xsl \
-	stats.xsl manageauth.xsl
+	stats.xsl manageauth.xsl updatemetadata.xsl
 
diff --git a/admin/listclients.xsl b/admin/listclients.xsl
index db7cd7ef..cfe01ef5 100644
--- a/admin/listclients.xsl
+++ b/admin/listclients.xsl
@@ -33,6 +33,7 @@
 	    <td align="center">
 		<a class="nav2" href="listclients.xsl?mount={@mount}">Show Listeners</a> | 
         	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> | 
+		<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a> |
         	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
 	    </td></tr>
 	</table>
diff --git a/admin/listmounts.xsl b/admin/listmounts.xsl
index 3cc367b8..a2679a29 100644
--- a/admin/listmounts.xsl
+++ b/admin/listmounts.xsl
@@ -35,6 +35,7 @@
 	    <td align="center">
 		<a class="nav2" href="listclients.xsl?mount={@mount}">Show Listeners</a> | 
         	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> | 
+		<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a> |
         	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
                 <xsl:if test="authenticator"> | <a class="nav2" href="manageauth.xsl?mount={@mount}">Manage Authentication</a></xsl:if>
 	    </td></tr>
diff --git a/admin/manageauth.xsl b/admin/manageauth.xsl
index e5f3af84..5e76609c 100644
--- a/admin/manageauth.xsl
+++ b/admin/manageauth.xsl
@@ -36,6 +36,7 @@
 	    <td align="center">
 		<a class="nav2" href="listclients.xsl?mount={@mount}">Show Listeners</a> | 
         	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move Listeners</a> | 
+		<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a> |
         	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
 	    </td></tr>
 	</table>
diff --git a/admin/stats.xsl b/admin/stats.xsl
index f3955e60..fecd461a 100644
--- a/admin/stats.xsl
+++ b/admin/stats.xsl
@@ -62,6 +62,7 @@
 	    <td align="center">
 		<a class="nav2" href="listclients.xsl?mount={@mount}">List Clients</a> | 
         	<a class="nav2" href="moveclients.xsl?mount={@mount}">Move MountPoints</a> | 
+        	<a class="nav2" href="updatemetadata.xsl?mount={@mount}">Update Metadata</a> |
         	<a class="nav2" href="killsource.xsl?mount={@mount}">Kill Source</a>
                 <xsl:if test="authenticator"> | <a class="nav2" href="manageauth.xsl?mount={@mount}">Manage Authentication</a></xsl:if>
 	    </td></tr>
diff --git a/admin/updatemetadata.xsl b/admin/updatemetadata.xsl
new file mode 100644
index 00000000..b37f3f1d
--- /dev/null
+++ b/admin/updatemetadata.xsl
@@ -0,0 +1,55 @@
+<xsl:stylesheet xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" version = "1.0" >
+<xsl:output omit-xml-declaration="no" method="xml" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" indent="yes" encoding="UTF-8" />
+<xsl:template match = "/icestats" >
+<html>
+<head>
+<title>Icecast Streaming Media Server</title>
+<link rel="stylesheet" type="text/css" href="/style.css" />
+</head>
+<body>
+	<center>
+	<table border="0" cellpadding="1" cellspacing="3">
+	<tr>        
+	    <td align="center">
+		<a class="nav" href="listmounts.xsl">List MountPoints</a> | 
+        	<a class="nav" href="moveclients.xsl">Move Listeners</a> | 
+        	<a class="nav" href="stats.xsl">Stats</a> | 
+        	<a class="nav" href="/status.xsl">Status Page</a>
+	    </td></tr>
+	</table>
+	</center>
+<h2>Update Metadata</h2>
+<div class="roundcont">
+<div class="roundtop">
+<img src="/corner_topleft.jpg" class="corner" style="display: none" />
+</div>
+<div class="newscontent">
+<xsl:for-each select="source">
+<h3>
+<xsl:if test="server_name"><xsl:value-of select="server_name" /> </xsl:if>
+(<xsl:value-of select="@mount" />)</h3>
+
+<form method="GET" action="/admin/metadata.xsl">
+<table border="0" cellpadding="4">
+<tr><td>Metadata : <input type="text" name="song"/></td></tr>
+<tr><td><input type="Submit" value="Update"/></td></tr>
+</table>
+<input type="hidden" name="mount" value="{@mount}"/>
+<input type="hidden" name="mode" value="updinfo"/>
+</form>
+
+<br />
+<br />
+</xsl:for-each>
+<xsl:text disable-output-escaping="yes">&amp;</xsl:text>nbsp;
+</div>
+<div class="roundbottom">
+<img src="/corner_bottomleft.jpg" class="corner" style="display: none" />
+</div>
+</div>
+<div class="poster">
+<img align="left" src="/icecast.png" />Support icecast development at <a class="nav" href="http://www.icecast.org">www.icecast.org</a></div>
+</body>
+</html>
+</xsl:template>
+</xsl:stylesheet>
diff --git a/conf/icecast_minimal.xml.in b/conf/icecast_minimal.xml.in
index 799040b5..dc584c08 100644
--- a/conf/icecast_minimal.xml.in
+++ b/conf/icecast_minimal.xml.in
@@ -25,13 +25,11 @@
         <logdir>@localstatedir@/log/@PACKAGE@</logdir>
         <webroot>@pkgdatadir@/web</webroot>
         <adminroot>@pkgdatadir@/admin</adminroot>
+        <alias source="/" dest="/status.xsl"/>
     </paths>
     <logging>
         <accesslog>access.log</accesslog>
         <errorlog>error.log</errorlog>
       	<loglevel>3</loglevel> <!-- 4 Debug, 3 Info, 2 Warn, 1 Error -->
     </logging>
-    <security>
-        <chroot>0</chroot>
-    </security>
 </icecast>
diff --git a/src/admin.c b/src/admin.c
index 6bb8ff40..884a9429 100644
--- a/src/admin.c
+++ b/src/admin.c
@@ -48,16 +48,19 @@
 
 /* Mount-specific commands */
 #define COMMAND_RAW_FALLBACK        1
-#define COMMAND_METADATA_UPDATE     2
+#define COMMAND_RAW_METADATA_UPDATE     2
 #define COMMAND_RAW_SHOW_LISTENERS  3
 #define COMMAND_RAW_MOVE_CLIENTS    4
 #define COMMAND_RAW_MANAGEAUTH      5
 #define COMMAND_SHOUTCAST_METADATA_UPDATE     6
+#define COMMAND_RAW_UPDATEMETADATA      7
 
 #define COMMAND_TRANSFORMED_FALLBACK        50
 #define COMMAND_TRANSFORMED_SHOW_LISTENERS  53
 #define COMMAND_TRANSFORMED_MOVE_CLIENTS    54
 #define COMMAND_TRANSFORMED_MANAGEAUTH      55
+#define COMMAND_TRANSFORMED_UPDATEMETADATA  56
+#define COMMAND_TRANSFORMED_METADATA_UPDATE 57
 
 /* Global commands */
 #define COMMAND_RAW_LIST_MOUNTS             101
@@ -80,7 +83,8 @@
 #define FALLBACK_RAW_REQUEST "fallbacks"
 #define FALLBACK_TRANSFORMED_REQUEST "fallbacks.xsl"
 #define SHOUTCAST_METADATA_REQUEST "admin.cgi"
-#define METADATA_REQUEST "metadata"
+#define METADATA_RAW_REQUEST "metadata"
+#define METADATA_TRANSFORMED_REQUEST "metadata.xsl"
 #define LISTCLIENTS_RAW_REQUEST "listclients"
 #define LISTCLIENTS_TRANSFORMED_REQUEST "listclients.xsl"
 #define STATS_RAW_REQUEST "stats"
@@ -99,6 +103,8 @@
 #define ADMIN_XSL_RESPONSE "response.xsl"
 #define MANAGEAUTH_RAW_REQUEST "manageauth"
 #define MANAGEAUTH_TRANSFORMED_REQUEST "manageauth.xsl"
+#define UPDATEMETADATA_RAW_REQUEST "updatemetadata"
+#define UPDATEMETADATA_TRANSFORMED_REQUEST "updatemetadata.xsl"
 #define DEFAULT_RAW_REQUEST ""
 #define DEFAULT_TRANSFORMED_REQUEST ""
 #define BUILDM3U_RAW_REQUEST "buildm3u"
@@ -112,8 +118,10 @@ int admin_get_command(char *command)
         return COMMAND_RAW_FALLBACK;
     else if(!strcmp(command, FALLBACK_TRANSFORMED_REQUEST))
         return COMMAND_TRANSFORMED_FALLBACK;
-    else if(!strcmp(command, METADATA_REQUEST))
-        return COMMAND_METADATA_UPDATE;
+    else if(!strcmp(command, METADATA_RAW_REQUEST))
+        return COMMAND_RAW_METADATA_UPDATE;
+    else if(!strcmp(command, METADATA_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_METADATA_UPDATE;
     else if(!strcmp(command, SHOUTCAST_METADATA_REQUEST))
         return COMMAND_SHOUTCAST_METADATA_UPDATE;
     else if(!strcmp(command, LISTCLIENTS_RAW_REQUEST))
@@ -150,6 +158,10 @@ int admin_get_command(char *command)
         return COMMAND_RAW_MANAGEAUTH;
     else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST))
         return COMMAND_TRANSFORMED_MANAGEAUTH;
+    else if(!strcmp(command, UPDATEMETADATA_RAW_REQUEST))
+        return COMMAND_RAW_UPDATEMETADATA;
+    else if(!strcmp(command, UPDATEMETADATA_TRANSFORMED_REQUEST))
+        return COMMAND_TRANSFORMED_UPDATEMETADATA;
     else if(!strcmp(command, BUILDM3U_RAW_REQUEST))
         return COMMAND_BUILDM3U;
     else if(!strcmp(command, DEFAULT_TRANSFORMED_REQUEST))
@@ -161,7 +173,7 @@ int admin_get_command(char *command)
 }
 
 static void command_fallback(client_t *client, source_t *source, int response);
-static void command_metadata(client_t *client, source_t *source);
+static void command_metadata(client_t *client, source_t *source, int response);
 static void command_shoutcast_metadata(client_t *client, source_t *source);
 static void command_show_listeners(client_t *client, source_t *source,
         int response);
@@ -177,6 +189,8 @@ static void command_buildm3u(client_t *client, source_t *source,
         int response);
 static void command_kill_source(client_t *client, source_t *source,
         int response);
+static void command_updatemetadata(client_t *client, source_t *source,
+        int response);
 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);
@@ -446,8 +460,11 @@ static void admin_handle_mount_request(client_t *client, source_t *source,
         case COMMAND_RAW_FALLBACK:
             command_fallback(client, source, RAW);
             break;
-        case COMMAND_METADATA_UPDATE:
-            command_metadata(client, source);
+        case COMMAND_RAW_METADATA_UPDATE:
+            command_metadata(client, source, RAW);
+            break;
+        case COMMAND_TRANSFORMED_METADATA_UPDATE:
+            command_metadata(client, source, TRANSFORMED);
             break;
         case COMMAND_SHOUTCAST_METADATA_UPDATE:
             command_shoutcast_metadata(client, source);
@@ -485,6 +502,12 @@ static void admin_handle_mount_request(client_t *client, source_t *source,
         case COMMAND_RAW_MANAGEAUTH:
             command_manageauth(client, source, RAW);
             break;
+        case COMMAND_TRANSFORMED_UPDATEMETADATA:
+            command_updatemetadata(client, source, TRANSFORMED);
+            break;
+        case COMMAND_RAW_UPDATEMETADATA:
+            command_updatemetadata(client, source, RAW);
+            break;
         case COMMAND_BUILDM3U:
             command_buildm3u(client, source, RAW);
             break;
@@ -822,11 +845,19 @@ static void command_fallback(client_t *client, source_t *source,
     html_success(client, "Fallback configured");
 }
 
-static void command_metadata(client_t *client, source_t *source)
+static void command_metadata(client_t *client, source_t *source,
+    int response)
 {
     char *action;
     char *song, *title, *artist;
     format_plugin_t *plugin;
+    xmlDocPtr doc;
+    xmlNodePtr node;
+    int ok = 1;
+
+    doc = xmlNewDoc("1.0");
+    node = xmlNewDocNode(doc, NULL, "iceresponse", NULL);
+    xmlDocSetRootElement(doc, node);
 
     DEBUG0("Got metadata update request");
 
@@ -837,7 +868,12 @@ static void command_metadata(client_t *client, source_t *source)
 
     if (strcmp (action, "updinfo") != 0)
     {
-        client_send_400 (client, "No such action");
+        xmlNewChild(node, NULL, "message", "No such action");
+        xmlNewChild(node, NULL, "return", "0");
+        admin_send_response(doc, client, response, 
+            ADMIN_XSL_RESPONSE);
+        xmlFreeDoc(doc);
+        client_destroy(client);
         return;
     }
 
@@ -860,13 +896,25 @@ static void command_metadata(client_t *client, source_t *source)
                         source->mount, artist, title);
             }
         }
-
-        html_success(client, "Metadata update successful");
     }
     else
     {
-        client_send_400 (client, "mountpoint will not accept URL updates");
+        xmlNewChild(node, NULL, "message", 
+            "Mountpoint will not accept URL updates");
+        xmlNewChild(node, NULL, "return", "1");
+        admin_send_response(doc, client, response, 
+            ADMIN_XSL_RESPONSE);
+        xmlFreeDoc(doc);
+        client_destroy(client);
+        return;
     }
+
+    xmlNewChild(node, NULL, "message", "Metadata update successful");
+    xmlNewChild(node, NULL, "return", "1");
+    admin_send_response(doc, client, response, 
+        ADMIN_XSL_RESPONSE);
+    xmlFreeDoc(doc);
+    client_destroy(client);
 }
 
 static void command_shoutcast_metadata(client_t *client, source_t *source)
@@ -960,3 +1008,20 @@ static void command_list_mounts(client_t *client, int response)
     return;
 }
 
+static void command_updatemetadata(client_t *client, source_t *source,
+    int response)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node, srcnode;
+
+    doc = xmlNewDoc("1.0");
+    node = xmlNewDocNode(doc, NULL, "icestats", NULL);
+    srcnode = xmlNewChild(node, NULL, "source", NULL);
+    xmlSetProp(srcnode, "mount", source->mount);
+    xmlDocSetRootElement(doc, node);
+
+    admin_send_response(doc, client, response, 
+        UPDATEMETADATA_TRANSFORMED_REQUEST);
+    xmlFreeDoc(doc);
+    client_destroy(client);
+}
diff --git a/src/cfgfile.c b/src/cfgfile.c
index 1da264df..9ac4c0ec 100644
--- a/src/cfgfile.c
+++ b/src/cfgfile.c
@@ -188,6 +188,9 @@ void config_clear(ice_config_t *c)
         xmlFree(mount->password);
         xmlFree(mount->dumpfile);
         xmlFree(mount->fallback_mount);
+        if (mount->cluster_password) {
+            xmlFree(mount->cluster_password);
+        }
 
         xmlFree(mount->auth_type);
         option = mount->auth_options;
@@ -610,6 +613,10 @@ static void _parse_mount(xmlDocPtr doc, xmlNodePtr node,
             tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
             mount->burst_size = atoi(tmp);
             if (tmp) xmlFree(tmp);
+        } else if (strcmp(node->name, "cluster-password") == 0) {
+            tmp = (char *)xmlNodeListGetString(doc, node->xmlChildrenNode, 1);
+            mount->cluster_password = (char *)xmlNodeListGetString(
+                    doc, node->xmlChildrenNode, 1);
         }
     } while ((node = node->next));
 }
diff --git a/src/cfgfile.h b/src/cfgfile.h
index f862ae22..1d8e8837 100644
--- a/src/cfgfile.h
+++ b/src/cfgfile.h
@@ -62,6 +62,7 @@ typedef struct _mount_proxy {
     unsigned int source_timeout;  /* source timeout in seconds */
 
     char *auth_type; /* Authentication type */
+    char *cluster_password;
     config_options_t *auth_options; /* Options for this type */
     struct _mount_proxy *next;
 } mount_proxy;
diff --git a/src/yp.c b/src/yp.c
index 648aac39..bf9ba87c 100644
--- a/src/yp.c
+++ b/src/yp.c
@@ -458,6 +458,7 @@ static ypdata_t *create_yp_entry (source_t *source)
         unsigned len = 512;
         int ret;
         char *url;
+        mount_proxy *mountproxy = NULL;
         ice_config_t *config;
 
         if (yp == NULL)
@@ -486,6 +487,18 @@ static ypdata_t *create_yp_entry (source_t *source)
             if (s) url = s;
             snprintf (url, ret, "http://%s:%d%s", config->hostname, config->port, source->mount);
         }
+
+        mountproxy = config->mounts;
+        while (mountproxy) {
+            if (strcmp (mountproxy->mountname, source->mount) == 0) {
+                if (mountproxy->cluster_password) {
+                   add_yp_info (yp, "cluster_password", 
+                       mountproxy->cluster_password, YP_CLUSTER_PASSWORD);
+                }
+                break;
+            }
+            mountproxy = mountproxy->next;
+        }
         config_release_config();
         yp->listen_url = util_url_escape (url);
         free (url);
@@ -813,6 +826,15 @@ static void add_yp_info (ypdata_t *yp, char *stat_name, void *info, int type)
                 stats_event (yp->mount, "yp_currently_playing", (char *)info);
             }
             break;
+        case YP_CLUSTER_PASSWORD:
+            escaped = util_url_escape(info);
+            if (escaped)
+            {
+                if (yp->cluster_password)
+                    free (yp->cluster_password);
+                yp->cluster_password = escaped;
+            }
+            break;
     }
 }
 
diff --git a/src/yp.h b/src/yp.h
index 25fddaf5..fb656ec7 100644
--- a/src/yp.h
+++ b/src/yp.h
@@ -22,6 +22,7 @@
 #define  YP_AUDIO_INFO 6
 #define  YP_SERVER_TYPE 7
 #define  YP_CURRENT_SONG 8
+#define  YP_CLUSTER_PASSWORD 9
 
 struct source_tag;