chiark / gitweb /
Switch to GPL v3
[disorder] / clients / disorder.c
index caeac1821c84356988e6505ae3dae1a5f34dba09..b07ffa1f2b66edc655d1cf4413ce98b34a7bec71 100644 (file)
@@ -1,39 +1,32 @@
 /*
  * This file is part of DisOrder.
- * Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell
+ * Copyright (C) 2004-2008 Richard Kettlewell
  *
- * This program is free software; you can redistribute it and/or modify
+ * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <config.h>
-#include "types.h"
+#include "common.h"
 
 #include <getopt.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
-#include <stdio.h>
 #include <errno.h>
-#include <stdlib.h>
-#include <string.h>
 #include <locale.h>
 #include <time.h>
 #include <stddef.h>
 #include <unistd.h>
-#include <assert.h>
 #include <pcre.h>
 #include <ctype.h>
 
 #include "kvp.h"
 #include "split.h"
 #include "sink.h"
-#include "plugin.h"
 #include "mem.h"
 #include "defs.h"
 #include "authorize.h"
 #include "vector.h"
 #include "version.h"
+#include "dateparse.h"
 
 static disorder_client *client;
 
@@ -468,6 +461,110 @@ static void cf_setup_guest(char **argv) {
     exit(EXIT_FAILURE);
 }
 
+struct scheduled_event {
+  time_t when;
+  struct kvp *actiondata;
+  char *id;
+};
+
+static int compare_event(const void *av, const void *bv) {
+  struct scheduled_event *a = (void *)av, *b = (void *)bv;
+
+  /* Primary sort key is the trigger time */
+  if(a->when < b->when)
+    return -1;
+  else if(a->when > b->when)
+    return 1;
+  /* For events that go off at the same time just sort by ID */
+  return strcmp(a->id, b->id);
+}
+
+static void cf_schedule_list(char attribute((unused)) **argv) {
+  char **ids;
+  int nids, n;
+  struct scheduled_event *events;
+  char tb[128];
+  const char *action, *key, *value, *priority;
+  int prichar;
+
+  /* Get all known events */
+  if(disorder_schedule_list(getclient(), &ids, &nids))
+    exit(EXIT_FAILURE);
+  events = xcalloc(nids, sizeof *events);
+  for(n = 0; n < nids; ++n) {
+    events[n].id = ids[n];
+    if(disorder_schedule_get(getclient(), ids[n], &events[n].actiondata))
+      exit(EXIT_FAILURE);
+    events[n].when = atoll(kvp_get(events[n].actiondata, "when"));
+  }
+  /* Sort by trigger time */
+  qsort(events, nids, sizeof *events, compare_event);
+  /* Display them */
+  for(n = 0; n < nids; ++n) {
+    strftime(tb, sizeof tb, "%Y-%m-%d %H:%M:%S %Z", localtime(&events[n].when));
+    action = kvp_get(events[n].actiondata, "action");
+    priority = kvp_get(events[n].actiondata, "priority");
+    if(!strcmp(priority, "junk"))
+      prichar = 'J';
+    else if(!strcmp(priority, "normal"))
+      prichar = 'N';
+    else
+      prichar = '?';
+    xprintf("%11s %-25s %c %-8s %s",
+           events[n].id, tb, prichar, kvp_get(events[n].actiondata, "who"),
+           action);
+    if(!strcmp(action, "play"))
+      xprintf(" %s",
+             nullcheck(utf82mb(kvp_get(events[n].actiondata, "track"))));
+    else if(!strcmp(action, "set-global")) {
+      key = kvp_get(events[n].actiondata, "key");
+      value = kvp_get(events[n].actiondata, "value");
+      if(value)
+       xprintf(" %s=%s",
+               nullcheck(utf82mb(key)),
+               nullcheck(utf82mb(value)));
+      else
+       xprintf(" %s unset",
+               nullcheck(utf82mb(key)));
+    }
+    xprintf("\n");
+  }
+}
+
+static void cf_schedule_del(char **argv) {
+  if(disorder_schedule_del(getclient(), argv[0]))
+    exit(EXIT_FAILURE);
+}
+
+static void cf_schedule_play(char **argv) {
+  if(disorder_schedule_add(getclient(),
+                          dateparse(argv[0]),
+                          argv[1],
+                          "play",
+                          argv[2]))
+    exit(EXIT_FAILURE);
+}
+
+static void cf_schedule_set_global(char **argv) {
+  if(disorder_schedule_add(getclient(),
+                          dateparse(argv[0]),
+                          argv[1],
+                          "set-global",
+                          argv[2],
+                          argv[3]))
+    exit(EXIT_FAILURE);
+}
+
+static void cf_schedule_unset_global(char **argv) {
+  if(disorder_schedule_add(getclient(),
+                          dateparse(argv[0]),
+                          argv[1],
+                          "set-global",
+                          argv[2],
+                          (char *)0))
+    exit(EXIT_FAILURE);
+}
+
 static const struct command {
   const char *name;
   int min, max;
@@ -475,22 +572,22 @@ static const struct command {
   int (*isarg)(const char *);
   const char *argstr, *desc;
 } commands[] = {
-  { "adduser",        2, 3, cf_adduser, isarg_rights, "USER PASSWORD [RIGHTS]",
+  { "adduser",        2, 3, cf_adduser, isarg_rights, "USERNAME PASSWORD [RIGHTS]",
                       "Create a new user" },
   { "allfiles",       1, 2, cf_allfiles, isarg_regexp, "DIR [~REGEXP]",
                       "List all files and directories in DIR" },
-  { "authorize",      1, 2, cf_authorize, isarg_rights, "USER [RIGHTS]",
-                      "Authorize USER to connect to the server" },
-  { "deluser",        1, 1, cf_deluser, 0, "USER",
-                      "Delete a user" },
+  { "authorize",      1, 2, cf_authorize, isarg_rights, "USERNAME [RIGHTS]",
+                      "Authorize user USERNAME to connect" },
+  { "deluser",        1, 1, cf_deluser, 0, "USERNAME",
+                      "Delete user USERNAME" },
   { "dirs",           1, 2, cf_dirs, isarg_regexp, "DIR [~REGEXP]",
                       "List directories in DIR" },
   { "disable",        0, 0, cf_disable, 0, "",
                       "Disable play" },
   { "disable-random", 0, 0, cf_random_disable, 0, "",
                       "Disable random play" },
-  { "edituser",       3, 3, cf_edituser, 0, "USER PROPERTY VALUE",
-                      "Set a property of a user" },
+  { "edituser",       3, 3, cf_edituser, 0, "USERNAME PROPERTY VALUE",
+                      "Set a property of user USERNAME" },
   { "enable",         0, 0, cf_enable, 0, "",
                       "Enable play" },
   { "enable-random",  0, 0, cf_random_enable, 0, "",
@@ -542,6 +639,16 @@ static const struct command {
                       "Resume after a pause" },
   { "rtp-address",    0, 0, cf_rtp_address, 0, "",
                       "Report server's broadcast address" },
+  { "schedule-del",   1, 1, cf_schedule_del, 0, "EVENT",
+                      "Delete a scheduled event" },
+  { "schedule-list",  0, 0, cf_schedule_list, 0, "",
+                      "List scheduled events" },
+  { "schedule-play",  3, 3, cf_schedule_play, 0, "WHEN PRI TRACK",
+                      "Play TRACK later" },
+  { "schedule-set-global", 4, 4, cf_schedule_set_global, 0, "WHEN PRI NAME VAL",
+                      "Set a global preference later" },
+  { "schedule-unset-global", 3, 3, cf_schedule_unset_global, 0, "WHEN PRI NAME",
+                      "Unset a global preference later" },
   { "scratch",        0, 0, cf_scratch, 0, "",
                       "Scratch the currently playing track" },
   { "scratch-id",     1, 1, cf_scratch, 0, "ID",
@@ -566,8 +673,8 @@ static const struct command {
                       "Unset a preference" },
   { "unset-global",   1, 1, cf_unset_global, 0, "NAME",
                       "Unset a global preference" },
-  { "userinfo",       2, 2, cf_userinfo, 0, "USER PROPERTY",
-                      "Get a property of as user" },
+  { "userinfo",       2, 2, cf_userinfo, 0, "USERNAME PROPERTY",
+                      "Get a property of a user" },
   { "users",          0, 0, cf_users, 0, "",
                       "List all users" },
   { "version",        0, 0, cf_version, 0, "",
@@ -612,6 +719,7 @@ int main(int argc, char **argv) {
   pcre_malloc = xmalloc;
   pcre_free = xfree;
   if(!setlocale(LC_CTYPE, "")) fatal(errno, "error calling setlocale");
+  if(!setlocale(LC_TIME, "")) fatal(errno, "error calling setlocale");
   while((n = getopt_long(argc, argv, "+hVc:dHlNu:p:", options, 0)) >= 0) {
     switch(n) {
     case 'h': help();
@@ -639,7 +747,7 @@ int main(int argc, char **argv) {
   optind = 1;                          /* for subsequent getopt calls */
   /* accumulate command args */
   while(n < argc) {
-    if((i = TABLE_FIND(commands, struct command, name, argv[n])) < 0)
+    if((i = TABLE_FIND(commands, name, argv[n])) < 0)
       fatal(0, "unknown command '%s'", argv[n]);
     if(n + commands[i].min >= argc)
       fatal(0, "missing arguments to '%s'", argv[n]);