From f9c693e602231e77eee94580addf60eff095b2de Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Mon, 19 Jan 2009 19:09:49 +0100
Subject: [PATCH] command: added "sticker" command

The "sticker" command allows clients to query or manipulate the
sticker database.  This patch implements the sub-commands "get" and
"set"; more will follow soon (enumeration), as well as extended
"lsinfo" / "playlistinfo" versions.
---
 doc/protocol.xml | 67 ++++++++++++++++++++++++++++++++++++++++++++
 src/command.c    | 72 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 139 insertions(+)

diff --git a/doc/protocol.xml b/doc/protocol.xml
index ff5d4838f..36ee3c35d 100644
--- a/doc/protocol.xml
+++ b/doc/protocol.xml
@@ -1155,6 +1155,73 @@ OK
       </variablelist>
     </section>
 
+    <section>
+      <title>Stickers</title>
+
+      <para>
+        "Stickers" are pieces of information attached to existing MPD
+        objects (e.g. song files, directories, albums).  Clients can
+        create arbitrary name/value pairs.  MPD itself does not assume
+        any special meaning in them.
+      </para>
+
+      <para>
+        The goal is to allow clients to share additional (possibly
+        dynamic) information about songs, which is neither stored on
+        the client (not available to other clients), nor stored in the
+        song files (MPD has no write access).
+      </para>
+
+      <para>
+        Client developers should create a standard for common sticker
+        names, to ensure interoperability.
+      </para>
+
+      <para>
+        Objects which may have stickers are addressed by their object
+        type ("song" for song objects) and their URI (the path within
+        the database for songs).
+      </para>
+
+      <variablelist>
+        <varlistentry id="command_sticker_get">
+          <term>
+            <cmdsynopsis>
+              <command>sticker</command>
+              <arg choice="plain">get</arg>
+              <arg choice="req"><replaceable>TYPE</replaceable></arg>
+              <arg choice="req"><replaceable>URI</replaceable></arg>
+              <arg choice="req"><replaceable>NAME</replaceable></arg>
+            </cmdsynopsis>
+          </term>
+          <listitem>
+            <para>
+              Reads a sticker value for the specified object.
+            </para>
+          </listitem>
+        </varlistentry>
+        <varlistentry id="command_sticker_set">
+          <term>
+            <cmdsynopsis>
+              <command>sticker</command>
+              <arg choice="plain">set</arg>
+              <arg choice="req"><replaceable>TYPE</replaceable></arg>
+              <arg choice="req"><replaceable>URI</replaceable></arg>
+              <arg choice="req"><replaceable>NAME</replaceable></arg>
+              <arg choice="req"><replaceable>VALUE</replaceable></arg>
+            </cmdsynopsis>
+          </term>
+          <listitem>
+            <para>
+              Adds a sticker value to the specified object.  If a
+              sticker item with that name already exists, it is
+              replaced.
+            </para>
+          </listitem>
+        </varlistentry>
+      </variablelist>
+    </section>
+
     <section>
       <title>Connection settings</title>
 
diff --git a/src/command.c b/src/command.c
index dbc7bdb99..6e8dc9fc0 100644
--- a/src/command.c
+++ b/src/command.c
@@ -40,6 +40,11 @@
 #include "idle.h"
 #include "config.h"
 
+#ifdef ENABLE_SQLITE
+#include "sticker.h"
+#include "song_sticker.h"
+#endif
+
 #include <assert.h>
 #include <time.h>
 #include <stdlib.h>
@@ -1403,6 +1408,70 @@ handle_idle(struct client *client,
 	return 1;
 }
 
+#ifdef ENABLE_SQLITE
+static enum command_return
+handle_sticker_song(struct client *client, int argc, char *argv[])
+{
+	struct song *song = db_get_song(argv[3]);
+
+	if (song == NULL) {
+		command_error(client, ACK_ERROR_NO_EXIST,
+			      "no such song");
+		return COMMAND_RETURN_ERROR;
+	}
+
+	if (argc == 5 && strcmp(argv[1], "get") == 0) {
+		char *value;
+
+		value = sticker_song_get_value(song, argv[4]);
+		if (value == NULL) {
+			command_error(client, ACK_ERROR_NO_EXIST,
+				      "no such sticker");
+			return COMMAND_RETURN_ERROR;
+		}
+
+		client_printf(client, "sticker:%s=%s\n", argv[4], value);
+		g_free(value);
+
+		return COMMAND_RETURN_OK;
+	} else if (argc == 6 && strcmp(argv[1], "set") == 0) {
+		bool ret;
+
+		ret = sticker_song_set_value(song, argv[4], argv[5]);
+		if (!ret) {
+			command_error(client, ACK_ERROR_SYSTEM,
+				      "failed to set sticker vqalue");
+			return COMMAND_RETURN_ERROR;
+		}
+
+		return COMMAND_RETURN_OK;
+	} else {
+		command_error(client, ACK_ERROR_ARG, "bad request");
+		return COMMAND_RETURN_ERROR;
+	}
+}
+
+static enum command_return
+handle_sticker(struct client *client, int argc, char *argv[])
+{
+	assert(argc >= 4);
+
+	if (!sticker_enabled()) {
+		command_error(client, ACK_ERROR_UNKNOWN,
+			      "sticker database is disabled");
+		return COMMAND_RETURN_ERROR;
+	}
+
+	if (strcmp(argv[2], "song") == 0)
+		return handle_sticker_song(client, argc, argv);
+	else {
+		command_error(client, ACK_ERROR_ARG,
+			      "unknown sticker domain");
+		return COMMAND_RETURN_ERROR;
+	}
+}
+#endif
+
 /**
  * The command registry.
  *
@@ -1467,6 +1536,9 @@ static const struct command commands[] = {
 	{ "shuffle", PERMISSION_CONTROL, 0, 0, handle_shuffle },
 	{ "stats", PERMISSION_READ, 0, 0, handle_stats },
 	{ "status", PERMISSION_READ, 0, 0, handle_status },
+#ifdef ENABLE_SQLITE
+	{ "sticker", PERMISSION_ADMIN, 3, -1, handle_sticker },
+#endif
 	{ "stop", PERMISSION_CONTROL, 0, 0, handle_stop },
 	{ "swap", PERMISSION_CONTROL, 2, 2, handle_swap },
 	{ "swapid", PERMISSION_CONTROL, 2, 2, handle_swapid },