chiark / gitweb /
Limit rescan/recheck messages to one every 10 seconds (and at most one
[disorder] / server / server.c
index 60cb869a8972bf85fc55a717e90358b0eb63be17..09f5098a81c06b3060a692ff255f51a0a24cb4ff 100644 (file)
  * USA
  */
 
-#include <config.h>
-#include "types.h"
-
-#include <pwd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <gcrypt.h>
-#include <stddef.h>
-#include <time.h>
-#include <limits.h>
-#include <pcre.h>
-#include <netdb.h>
-#include <netinet/in.h>
-
-#include "event.h"
-#include "server.h"
-#include "syscalls.h"
-#include "queue.h"
-#include "server-queue.h"
-#include "play.h"
-#include "log.h"
-#include "mem.h"
-#include "state.h"
-#include "charset.h"
-#include "split.h"
-#include "configuration.h"
-#include "hex.h"
-#include "rights.h"
-#include "trackdb.h"
-#include "table.h"
-#include "kvp.h"
-#include "mixer.h"
-#include "sink.h"
-#include "authhash.h"
-#include "plugin.h"
-#include "printf.h"
-#include "trackname.h"
-#include "eventlog.h"
-#include "defs.h"
-#include "cache.h"
-#include "unicode.h"
-#include "cookies.h"
-#include "base64.h"
-#include "hash.h"
-#include "mime.h"
-#include "sendmail.h"
-#include "wstat.h"
+#include "disorder-server.h"
 
 #ifndef NONCE_SIZE
 # define NONCE_SIZE 16
@@ -400,16 +348,16 @@ static void start_fresh_rescan(void *ru) {
 static int c_rescan(struct conn *c,
                    char **vec,
                    int nvec) {
-  int wait = 0, fresh = 0, n;
+  int flag_wait = 0, flag_fresh = 0, n;
 
   /* Parse flags */
   for(n = 0; n < nvec; ++n) {
     if(!strcmp(vec[n], "wait"))
-      wait = 1;                                /* wait for rescan to complete */
+      flag_wait = 1;                   /* wait for rescan to complete */
 #if 0
     /* Currently disabled because untested (and hard to test). */
     else if(!strcmp(vec[n], "fresh"))
-      fresh = 1;                       /* don't piggyback underway rescan */
+      flag_fresh = 1;                  /* don't piggyback underway rescan */
 #endif
     else {
       sink_writes(ev_writer_sink(c->w), "550 unknown flag\n");
@@ -418,15 +366,15 @@ static int c_rescan(struct conn *c,
   }
   /* Report what was requested */
   info("S%x rescan by %s (%s %s)", c->tag, c->who,
-       wait ? "wait" : "",
-       fresh ? "fresh" : "");
+       flag_wait ? "wait" : "",
+       flag_fresh ? "fresh" : "");
   if(trackdb_rescan_underway()) {
-    if(fresh) {
+    if(flag_fresh) {
       /* We want a fresh rescan but there is already one underway.  Arrange a
        * callback when it completes and then set off a new one. */
-      c->rescan_wait = wait;
+      c->rescan_wait = flag_wait;
       trackdb_add_rescanned(start_fresh_rescan, c);
-      if(wait)
+      if(flag_wait)
        return 0;
       else {
        sink_writes(ev_writer_sink(c->w), "250 rescan queued\n");
@@ -434,7 +382,7 @@ static int c_rescan(struct conn *c,
       }
     } else {
       /* There's a rescan underway, and it's acceptable to piggyback on it */
-      if(wait) {
+      if(flag_wait) {
        /* We want to block until completion. */
        trackdb_add_rescanned(finished_rescan, c);
        return 0;
@@ -447,7 +395,7 @@ static int c_rescan(struct conn *c,
     }
   } else {
     /* No rescan is underway.  fresh is therefore irrelevant. */
-    if(wait) {
+    if(flag_wait) {
       /* We want to block until completion */
       trackdb_rescan(c->ev, 1/*check*/, finished_rescan, c);
       return 0;
@@ -1506,6 +1454,105 @@ static int c_reminder(struct conn *c,
   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;
+  const 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;
@@ -1566,6 +1613,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 },
+  { "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, },
@@ -1607,7 +1658,7 @@ static int command(struct conn *c, char *line) {
     sink_writes(ev_writer_sink(c->w), "500 do what?\n");
     return 1;
   }
-  if((n = TABLE_FIND(commands, struct command, name, vec[0])) < 0)
+  if((n = TABLE_FIND(commands, name, vec[0])) < 0)
     sink_writes(ev_writer_sink(c->w), "500 unknown command\n");
   else {
     if(commands[n].rights