* 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"
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
&& 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
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.
*/
redirect(0);
}
-/*! enable
+/*$ enable
*
* Enables play.
*/
redirect(0);
}
-/*! random-disable
+/*$ random-disable
*
* Disables random play.
*/
redirect(0);
}
-/*! random-enable
+/*$ random-enable
*
* Enables random play.
*/
redirect(0);
}
-/*! pause
+/*$ pause
*
* Pauses the current track (if there is one and it's not paused already).
*/
redirect(0);
}
-/*! resume
+/*$ resume
*
* Resumes the current track (if there is one and it's paused).
*/
redirect(0);
}
-/*! remove
+/*$ remove
*
* Removes the track given by the \fBid\fR argument. If this is the currently
* playing track then it is scratched.
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
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.
const char *track, *dir;
char **tracks;
int ntracks, n;
- struct dcgi_entry *e;
+ struct tracksort_data *tsd;
if(dcgi_client) {
if((track = cgi_get("track"))) {
} 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);
}
}
redirect(0);
return n;
}
-/*! volume
+/*$ volume
*
* If the \fBdelta\fR argument is set: adjust both channels by that amount (up
* if positive, down if negative).
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,
}
}
-/*! logout
+/*$ logout
*
* Logs out the current user and expands \fIlogin.tmpl\fR with \fBstatus\fR or
* \fB@error\fR set according to the result.
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
return;
}
/* We could well do better address validation but for now we'll just do the
- * minimum
- /* TODO use email_valid() */
- if(!strchr(email, '@')) {
+ * minimum */
+ if(!email_valid(email)) {
login_error("bademail");
return;
}
"\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",
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
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
}
} else
password = password2 = 0;
- /* TODO use email_valid() */
- if(email && !strchr(email, '@')) {
+ if(email && !email_valid(email)) {
login_error("bademail");
return;
}
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
return 0;
}
-/*! prefs
+/*$ prefs
*
* Set preferences on a number of tracks.
*
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\n"
+ 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