chiark / gitweb /
Server and Python interface now support schedule-* commands. Tests to
authorRichard Kettlewell <rjk@greenend.org.uk>
Sat, 24 May 2008 10:44:08 +0000 (11:44 +0100)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sat, 24 May 2008 10:44:08 +0000 (11:44 +0100)
follow.

doc/disorder_protocol.5.in
python/disorder.py.in
server/server.c

index 0847050..f4c5b2f 100644 (file)
@@ -294,6 +294,61 @@ Requires one of the \fBscratch mine\fR, \fBscratch random\fR or
 \fBscratch any\fR rights depending on how the track came to be
 added to the queue.
 .TP
 \fBscratch any\fR rights depending on how the track came to be
 added to the queue.
 .TP
+.B schedule-add \fIWHEN\fR \fIPRIORITY\fR \fIACTION\fR ...
+Schedule an event for the future.
+.IP
+.I WHEN
+is the time when it should happen, as \fBtime_t\fR value.
+It must refer to a time in the future.
+.IP
+.I PRIORITY
+is the event priority.
+This can be \fBnormal\fR, in which case the event will be run at startup if its
+time has past, or \fBjunk\fR in which case it will be discarded if it is found
+to be in the past at startup.
+The meaning of other values is not defined.
+.IP
+.I ACTION
+is the action to perform.
+The choice of action determines the meaning of the remaining arguments.
+Possible actions are:
+.RS
+.TP
+.B play
+Play a track.
+The next argument is the track name.
+Requires the \fBplay\fR right.
+.TP
+.B set-global
+Set a global preference.
+The next argument is the preference name and the final argument is the value to
+set it to (omit it to unset it).
+Requires the \fBglobal prefs\fR right.
+.RE
+.IP
+You need the right at the point you create the event.
+It is not possible to create scheduled events in expectation of a future change
+in rights.
+.TP
+.B schedule-del \fIEVENT\fR
+Deletes a scheduled event.
+Users can always delete their own scheduled events; with the \fBadmin\fR
+right you can delete any event.
+.TP
+.B schedule-get \fIEVENT\fR
+Sends the details of scheduled event \fIEVENT\fR in a response body.
+Each line is a pair of strings quoted in the usual way, the first being the key
+ane the second the value.
+No particular order is used.
+.IP
+Scheduled events are considered public information.
+Right \fBread\fR is sufficient to see details of all events.
+.TP
+.B schedule-list
+Sends the event IDs of all scheduled events in a response body, in no
+particular order.
+Use \fBschedule-get\fR to get the details of each event.
+.TP
 .B search \fITERMS\fR
 Search for tracks matching the search terms.
 The results are put in a response body, one to a line.
 .B search \fITERMS\fR
 Search for tracks matching the search terms.
 The results are put in a response body, one to a line.
index ef33106..867b901 100644 (file)
@@ -883,6 +883,30 @@ class client:
     """Confirm a user registration"""
     res, details = self._simple("confirm", confirmation)
 
     """Confirm a user registration"""
     res, details = self._simple("confirm", confirmation)
 
+  def schedule_list(self):
+    """Get a list of scheduled events """
+    self._simple("schedule-list")
+    return self._body()
+
+  def schedule_del(self, event):
+    """Delete a scheduled event"""
+    self._simple("schedule-del", event)
+
+  def schedule_get(self, event):
+    """Get the details for an event as a dict (returns None if event not found)"""
+    res, details = self._simple("schedule-get", event)
+    if res == 555:
+      return None
+    d = {}
+    for line in self._body():
+      bits = _split(line)
+      d[bits[0]] = bits[1]
+    return d
+
+  def schedule_add(self, when, priority, action, *rest):
+    """Add a scheduled event"""
+    self._simple("schedule-add", when, priorty, action, *rest)
+
   ########################################################################
   # I/O infrastructure
 
   ########################################################################
   # I/O infrastructure
 
index 60cb869..e8e6a68 100644 (file)
@@ -71,6 +71,7 @@
 #include "mime.h"
 #include "sendmail.h"
 #include "wstat.h"
 #include "mime.h"
 #include "sendmail.h"
 #include "wstat.h"
+#include "schedule.h"
 
 #ifndef NONCE_SIZE
 # define NONCE_SIZE 16
 
 #ifndef NONCE_SIZE
 # define NONCE_SIZE 16
@@ -1506,6 +1507,105 @@ static int c_reminder(struct conn *c,
   return 0;
 }
 
   return 0;
 }
 
+static int c_schedule_list(struct conn *c,
+                          char attribute((unused)) **vec,
+                          int attribute((unused)) nvec) {
+  char **ids = schedule_list(0);
+  sink_writes(ev_writer_sink(c->w), "253 ID list follows\n");
+  while(*ids)
+    sink_printf(ev_writer_sink(c->w), "%s\n", *ids++);
+  sink_writes(ev_writer_sink(c->w), ".\n");
+  return 1;                            /* completed */
+}
+
+static int c_schedule_get(struct conn *c,
+                         char **vec,
+                         int attribute((unused)) nvec) {
+  struct kvp *actiondata = schedule_get(vec[0]), *k;
+
+  if(!actiondata) {
+    sink_writes(ev_writer_sink(c->w), "555 No such event\n");
+    return 1;                          /* completed */
+  }
+  /* Scheduled events are public information.  Anyone with RIGHT_READ can see
+   * them. */
+  sink_writes(ev_writer_sink(c->w), "253 Event information follows\n");
+  for(k = actiondata; k; k = k->next)
+    sink_printf(ev_writer_sink(c->w), " %s %s\n",
+               quoteutf8(k->name),  quoteutf8(k->value));
+  sink_writes(ev_writer_sink(c->w), ".\n");
+  return 1;                            /* completed */
+}
+
+static int c_schedule_del(struct conn *c,
+                         char **vec,
+                         int attribute((unused)) nvec) {
+  struct kvp *actiondata = schedule_get(vec[0]);
+
+  if(!actiondata) {
+    sink_writes(ev_writer_sink(c->w), "555 No such event\n");
+    return 1;                          /* completed */
+  }
+  /* If you have admin rights you can delete anything.  If you don't then you
+   * can only delete your own scheduled events. */
+  if(!(c->rights & RIGHT_ADMIN)) {
+    const char *who = kvp_get(actiondata, "who");
+
+    if(!who || !c->who || strcmp(who, c->who)) {
+      sink_writes(ev_writer_sink(c->w), "551 Not authorized\n");
+      return 1;                                /* completed */
+    }
+  }
+  if(schedule_del(vec[0]))
+    sink_writes(ev_writer_sink(c->w), "550 Could not delete scheduled event\n");
+  else
+    sink_writes(ev_writer_sink(c->w), "250 Deleted\n");
+  return 1;                            /* completed */
+}
+
+static int c_schedule_add(struct conn *c,
+                         char **vec,
+                         int nvec) {
+  struct kvp *actiondata = 0;
+  char *id;
+
+  /* Standard fields */
+  kvp_set(&actiondata, "who", c->who);
+  kvp_set(&actiondata, "when", vec[0]);
+  kvp_set(&actiondata, "priority", vec[1]);
+  kvp_set(&actiondata, "action", vec[2]);
+  /* Action-dependent fields */
+  if(!strcmp(vec[2], "play")) {
+    if(nvec != 4) {
+      sink_writes(ev_writer_sink(c->w), "550 Wrong number of arguments\n");
+      return 1;
+    }
+    if(!trackdb_exists(vec[3])) {
+      sink_writes(ev_writer_sink(c->w), "550 Track is not in database\n");
+      return 1;
+    }
+    kvp_set(&actiondata, "track", vec[3]);
+  } else if(!strcmp(vec[2], "set-global")) {
+    if(nvec < 4 || nvec > 5) {
+      sink_writes(ev_writer_sink(c->w), "550 Wrong number of arguments\n");
+      return 1;
+    }
+    kvp_set(&actiondata, "key", vec[3]);
+    if(nvec > 4)
+      kvp_set(&actiondata, "value", vec[4]);
+  } else {
+    sink_writes(ev_writer_sink(c->w), "550 Unknown action\n");
+    return 1;
+  }
+  /* schedule_add() checks user rights */
+  id = schedule_add(c->ev, actiondata);
+  if(!id)
+    sink_writes(ev_writer_sink(c->w), "550 Cannot add scheduled event\n");
+  else
+    sink_printf(ev_writer_sink(c->w), "252 %s\n", id);
+  return 1;
+}
+
 static const struct command {
   /** @brief Command name */
   const char *name;
 static const struct command {
   /** @brief Command name */
   const char *name;
@@ -1566,6 +1666,10 @@ static const struct command {
   { "resume",         0, 0,       c_resume,         RIGHT_PAUSE },
   { "revoke",         0, 0,       c_revoke,         RIGHT_READ },
   { "rtp-address",    0, 0,       c_rtp_address,    0 },
   { "resume",         0, 0,       c_resume,         RIGHT_PAUSE },
   { "revoke",         0, 0,       c_revoke,         RIGHT_READ },
   { "rtp-address",    0, 0,       c_rtp_address,    0 },
+  { "schedule-add",   3, INT_MAX, c_schedule_add,   RIGHT_READ },
+  { "schedule-del",   1, 1,       c_schedule_del,   RIGHT_READ },
+  { "schedule-get",   1, 1,       c_schedule_get,   RIGHT_READ },
+  { "schedule-list",  0, 0,       c_schedule_list,  RIGHT_READ },
   { "scratch",        0, 1,       c_scratch,        RIGHT_SCRATCH__MASK },
   { "search",         1, 1,       c_search,         RIGHT_READ },
   { "set",            3, 3,       c_set,            RIGHT_PREFS, },
   { "scratch",        0, 1,       c_scratch,        RIGHT_SCRATCH__MASK },
   { "search",         1, 1,       c_search,         RIGHT_READ },
   { "set",            3, 3,       c_set,            RIGHT_PREFS, },