\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.
"""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
#include "mime.h"
#include "sendmail.h"
#include "wstat.h"
+#include "schedule.h"
#ifndef NONCE_SIZE
# define NONCE_SIZE 16
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;
{ "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, },