chiark / gitweb /
protogen: support multiple return values.
[disorder] / cgi / actions.c
index a139eaf681f68af7fea8a883b694e6b7a22eb892..584da76c040a88c0ea8988c17366c7ae86201acd 100644 (file)
@@ -2,28 +2,28 @@
  * This file is part of DisOrder.
  * 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/>.
  */
-/** @file server/actions.c
+/** @file cgi/actions.c
  * @brief DisOrder web actions
  *
  * Actions are anything that the web interface does beyond passive template
  * expansion and inspection of state recieved from the server.  This means
  * playing tracks, editing prefs etc but also setting extra headers e.g. to
  * auto-refresh the playing list.
+ *
+ * See @ref lib/macros-builtin.c for docstring syntax.
  */
 
 #include "disorder-cgi.h"
@@ -44,17 +44,17 @@ static void redirect(const char *url) {
   if(printf("Location: %s\n"
             "%s\n"
             "\n", url, dcgi_cookie_header()) < 0)
-    fatal(errno, "error writing to stdout");
+    disorder_fatal(errno, "error writing to stdout");
 }
 
-/*! playing
+/*$ playing
  *
  * Expands \fIplaying.tmpl\fR as if there was no special 'playing' action, but
  * adds a Refresh: field to the HTTP header.  The maximum refresh interval is
  * defined by \fBrefresh\fR (see \fBdisorder_config\fR(5)) but may be less if
  * the end of the track is near.
  */
-/*! manage
+/*$ manage
  *
  * Expands \fIplaying.tmpl\fR (NB not \fImanage.tmpl\fR) as if there was no
  * special 'playing' action, and adds a Refresh: field to the HTTP header.  The
@@ -75,19 +75,19 @@ static void act_playing(void) {
      && length
      && dcgi_playing->sofar >= 0) {
     /* Try to put the next refresh at the start of the next track. */
-    time(&now);
+    xtime(&now);
     fin = now + length - dcgi_playing->sofar + config->gap;
     if(now + refresh > fin)
       refresh = fin - now;
   }
-  if(dcgi_queue && dcgi_queue->state == playing_isscratch) {
+  if(dcgi_queue && dcgi_queue->origin == origin_scratch) {
     /* next track is a scratch, don't leave more than the inter-track gap */
     if(refresh > config->gap)
       refresh = config->gap;
   }
   if(!dcgi_playing
      && ((dcgi_queue
-          && dcgi_queue->state != playing_random)
+          && dcgi_queue->origin != origin_random)
          || dcgi_random_enabled)
      && dcgi_enabled) {
     /* no track playing but playing is enabled and there is something coming
@@ -95,17 +95,21 @@ static void act_playing(void) {
     if(refresh > config->gap)
       refresh = config->gap;
   }
+  /* Bound the refresh interval below as a back-stop against the above
+   * calculations coming up with a stupid answer */
+  if(refresh < config->refresh_min)
+    refresh = config->refresh_min;
   if((action = cgi_get("action")))
     url = cgi_makeurl(config->url, "action", action, (char *)0);
   else
     url = config->url;
   if(printf("Refresh: %ld;url=%s\n",
             refresh, url) < 0)
-    fatal(errno, "error writing to stdout");
+    disorder_fatal(errno, "error writing to stdout");
   dcgi_expand("playing", 1);
 }
 
-/*! disable
+/*$ disable
  *
  * Disables play.
  */
@@ -115,7 +119,7 @@ static void act_disable(void) {
   redirect(0);
 }
 
-/*! enable
+/*$ enable
  *
  * Enables play.
  */
@@ -125,7 +129,7 @@ static void act_enable(void) {
   redirect(0);
 }
 
-/*! random-disable
+/*$ random-disable
  *
  * Disables random play.
  */
@@ -135,7 +139,7 @@ static void act_random_disable(void) {
   redirect(0);
 }
 
-/*! random-enable
+/*$ random-enable
  *
  * Enables random play.
  */
@@ -145,7 +149,7 @@ static void act_random_enable(void) {
   redirect(0);
 }
 
-/*! pause
+/*$ pause
  *
  * Pauses the current track (if there is one and it's not paused already).
  */
@@ -155,7 +159,7 @@ static void act_pause(void) {
   redirect(0);
 }
 
-/*! resume
+/*$ resume
  *
  * Resumes the current track (if there is one and it's paused).
  */
@@ -165,7 +169,7 @@ static void act_resume(void) {
   redirect(0);
 }
 
-/*! remove
+/*$ remove
  *
  * Removes the track given by the \fBid\fR argument.  If this is the currently
  * playing track then it is scratched.
@@ -176,32 +180,27 @@ static void act_remove(void) {
 
   if(dcgi_client) {
     if(!(id = cgi_get("id")))
-      error(0, "missing 'id' argument");
+      disorder_error(0, "missing 'id' argument");
     else if(!(q = dcgi_findtrack(id)))
-      error(0, "unknown queue id %s", id);
-    else switch(q->state) {
-    case playing_isscratch:
-    case playing_failed:
-    case playing_no_player:
-    case playing_ok:
-    case playing_quitting:
-    case playing_scratched:
-      error(0, "does not make sense to scratch %s", id);
-      break;
-    case playing_paused:                /* started but paused */
-    case playing_started:               /* started to play */
+      disorder_error(0, "unknown queue id %s", id);
+    else if(q->origin == origin_scratch)
+      /* can't scratch scratches */
+      disorder_error(0, "does not make sense to scratch or remove %s", id);
+    else if(q->state == playing_paused
+            || q->state == playing_started)
+      /* removing the playing track = scratching */
       disorder_scratch(dcgi_client, id);
-      break;
-    case playing_random:                /* unplayed randomly chosen track */
-    case playing_unplayed:              /* haven't played this track yet */
+    else if(q->state == playing_unplayed)
+      /* otherwise it must be in the queue */
       disorder_remove(dcgi_client, id);
-      break;
-    }
+    else
+      /* various error states */
+      disorder_error(0, "does not make sense to scratch or remove %s", id);
   }
   redirect(0);
 }
 
-/*! move
+/*$ move
  *
  * Moves the track given by the \fBid\fR argument the distance given by the
  * \fBdelta\fR argument.  If this is positive the track is moved earlier in the
@@ -213,25 +212,25 @@ static void act_move(void) {
 
   if(dcgi_client) {
     if(!(id = cgi_get("id")))
-      error(0, "missing 'id' argument");
+      disorder_error(0, "missing 'id' argument");
     else if(!(delta = cgi_get("delta")))
-      error(0, "missing 'delta' argument");
+      disorder_error(0, "missing 'delta' argument");
     else if(!(q = dcgi_findtrack(id)))
-      error(0, "unknown queue id %s", id);
+      disorder_error(0, "unknown queue id %s", id);
     else switch(q->state) {
     case playing_random:                /* unplayed randomly chosen track */
     case playing_unplayed:              /* haven't played this track yet */
       disorder_move(dcgi_client, id, atol(delta));
       break;
     default:
-      error(0, "does not make sense to scratch %s", id);
+      disorder_error(0, "does not make sense to scratch %s", id);
       break;
     }
   }
   redirect(0);
 }
 
-/*! play
+/*$ play
  *
  * Play the track given by the \fBtrack\fR argument, or if that is not set all
  * the tracks in the directory given by the \fBdir\fR argument.
@@ -240,24 +239,18 @@ static void act_play(void) {
   const char *track, *dir;
   char **tracks;
   int ntracks, n;
-  struct dcgi_entry *e;
+  struct tracksort_data *tsd;
+  char *id;
   
   if(dcgi_client) {
     if((track = cgi_get("track"))) {
-      disorder_play(dcgi_client, track);
+      disorder_play(dcgi_client, track, &id);
     } else if((dir = cgi_get("dir"))) {
       if(disorder_files(dcgi_client, dir, 0, &tracks, &ntracks))
         ntracks = 0;
-      /* TODO use tracksort_init */
-      e = xmalloc(ntracks * sizeof (struct dcgi_entry));
-      for(n = 0; n < ntracks; ++n) {
-        e[n].track = tracks[n];
-        e[n].sort = trackname_transform("track", tracks[n], "sort");
-        e[n].display = trackname_transform("track", tracks[n], "display");
-      }
-      qsort(e, ntracks, sizeof (struct dcgi_entry), dcgi_compare_entry);
+      tsd = tracksort_init(ntracks, tracks, "track");
       for(n = 0; n < ntracks; ++n)
-        disorder_play(dcgi_client, e[n].track);
+        disorder_play(dcgi_client, tsd[n].track, &id);
     }
   }
   redirect(0);
@@ -271,7 +264,7 @@ static int clamp(int n, int min, int max) {
   return n;
 }
 
-/*! volume
+/*$ volume
  *
  * If the \fBdelta\fR argument is set: adjust both channels by that amount (up
  * if positive, down if negative).
@@ -333,7 +326,7 @@ static int login_as(const char *username, const char *password) {
   return 0;                             /* OK */
 }
 
-/*! login
+/*$ login
  *
  * If \fBusername\fR and \fBpassword\fR are set (and the username isn't
  * "guest") then attempt to log in using those credentials.  On success,
@@ -368,7 +361,7 @@ static void act_login(void) {
   }
 }
 
-/*! logout
+/*$ logout
  *
  * Logs out the current user and expands \fIlogin.tmpl\fR with \fBstatus\fR or
  * \fB@error\fR set according to the result.
@@ -393,7 +386,7 @@ static void act_logout(void) {
   dcgi_expand("login", 1);
 }
 
-/*! register
+/*$ register
  *
  * Register a new user using \fBusername\fR, \fBpassword1\fR, \fBpassword2\fR
  * and \fBemail\fR and expands \fIlogin.tmpl\fR with \fBstatus\fR or
@@ -435,8 +428,7 @@ static void act_register(void) {
   }
   /* We could well do better address validation but for now we'll just do the
    * minimum */
-   /* TODO use email_valid() */
-  if(!strchr(email, '@')) {
+  if(!email_valid(email)) {
     login_error("bademail");
     return;
   }
@@ -451,7 +443,7 @@ static void act_register(void) {
                 "\n"
                 "%s?c=%s\n", config->url, urlencodestring(confirm));
   if(!(text = mime_encode_text(text, &charset, &encoding)))
-    fatal(0, "cannot encode email");
+    disorder_fatal(0, "cannot encode email");
   byte_xasprintf(&content_type, "text/plain;charset=%s",
                 quote822(charset, 0));
   sendmail("", config->mail_sender, email, "Welcome to DisOrder",
@@ -461,7 +453,7 @@ static void act_register(void) {
   dcgi_expand("login", 1);
 }
 
-/*! confirm
+/*$ confirm
  *
  * Confirm a user registration using the nonce supplied in \fBc\fR and expands
  * \fIlogin.tmpl\fR with \fBstatus\fR or \fB@error\fR set according to the
@@ -497,7 +489,7 @@ static void act_confirm(void) {
   dcgi_expand("login", 1);
 }
 
-/*! edituser
+/*$ edituser
  *
  * Edit user details using \fBusername\fR, \fBchangepassword1\fR,
  * \fBchangepassword2\fR and \fBemail\fR and expands \fIlogin.tmpl\fR with
@@ -525,8 +517,7 @@ static void act_edituser(void) {
     }
   } else
     password = password2 = 0;
-  /* TODO use email_valid() */
-  if(email && !strchr(email, '@')) {
+  if(email && !email_valid(email)) {
     login_error("bademail");
     return;
   }
@@ -559,7 +550,7 @@ static void act_edituser(void) {
   dcgi_expand("login", 1);
 }
 
-/*! reminder
+/*$ reminder
  *
  * Issue an email password reminder to \fBusername\fR and expands
  * \fIlogin.tmpl\fR with \fBstatus\fR or \fB@error\fR set according to the
@@ -622,7 +613,7 @@ static int process_prefs(int numfile) {
     byte_xasprintf((char **)&name, "trackname_%s_%s", context, part);
     disorder_set(dcgi_client, file, name, value);
   }
-  if((value = numbered_arg("random", numfile)))
+  if(numbered_arg("random", numfile))
     disorder_unset(dcgi_client, file, "pick_at_random");
   else
     disorder_set(dcgi_client, file, "pick_at_random", "0");
@@ -641,7 +632,7 @@ static int process_prefs(int numfile) {
   return 0;
 }
 
-/*! prefs
+/*$ prefs
  *
  * Set preferences on a number of tracks.
  *
@@ -740,19 +731,19 @@ void dcgi_expand(const char *name, int header) {
     mx_expand_file(found, sink_discard(), 0);
   /* For unknown actions check that they aren't evil */
   if(!dcgi_valid_action(name))
-    fatal(0, "invalid action name '%s'", name);
+    disorder_fatal(0, "invalid action name '%s'", name);
   byte_xasprintf((char **)&p, "%s.tmpl", name);
   if(!(found = mx_find(p, 0/*report*/)))
-    fatal(errno, "cannot find %s", p);
+    disorder_fatal(errno, "cannot find %s", p);
   if(header) {
     if(printf("Content-Type: text/html; charset=UTF-8\n"
               "%s\n"
               "\n", dcgi_cookie_header()) < 0)
-      fatal(errno, "error writing to stdout");
+      disorder_fatal(errno, "error writing to stdout");
   }
   if(mx_expand_file(found, sink_stdio("stdout", stdout), 0) == -1
      || fflush(stdout) < 0)
-    fatal(errno, "error writing to stdout");
+    disorder_fatal(errno, "error writing to stdout");
 }
 
 /** @brief Execute a web action