2 * This file is part of DisOrder.
3 * Copyright (C) 2004-2008 Richard Kettlewell
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 /** @file server/actions.c
21 * @brief DisOrder web actions
23 * Actions are anything that the web interface does beyond passive template
24 * expansion and inspection of state recieved from the server. This means
25 * playing tracks, editing prefs etc but also setting extra headers e.g. to
26 * auto-refresh the playing list.
29 #include "disorder-cgi.h"
31 /** @brief Redirect to some other action or URL */
32 static void redirect(const char *url) {
33 /* By default use the 'back' argument */
35 url = cgi_get("back");
37 if(!strncmp(url, "http", 4))
38 /* If the target is not a full URL assume it's the action */
39 url = cgi_makeurl(config->url, "action", url, (char *)0);
41 /* If back= is not set just go back to the front page */
44 if(printf("Location: %s\n"
46 "\n", url, dcgi_cookie_header()) < 0)
47 fatal(errno, "error writing to stdout");
50 /* 'playing' and 'manage' just add a Refresh: header */
51 static void act_playing(void) {
52 long refresh = config->refresh;
58 dcgi_lookup(DCGI_PLAYING|DCGI_QUEUE|DCGI_ENABLED|DCGI_RANDOM_ENABLED);
60 && dcgi_playing->state == playing_started /* i.e. not paused */
61 && !disorder_length(dcgi_client, dcgi_playing->track, &length)
63 && dcgi_playing->sofar >= 0) {
64 /* Try to put the next refresh at the start of the next track. */
66 fin = now + length - dcgi_playing->sofar + config->gap;
67 if(now + refresh > fin)
70 if(dcgi_queue && dcgi_queue->state == playing_isscratch) {
71 /* next track is a scratch, don't leave more than the inter-track gap */
72 if(refresh > config->gap)
73 refresh = config->gap;
77 && dcgi_queue->state != playing_random)
78 || dcgi_random_enabled)
80 /* no track playing but playing is enabled and there is something coming
81 * up, must be in a gap */
82 if(refresh > config->gap)
83 refresh = config->gap;
85 if((action = cgi_get("action")))
86 url = cgi_makeurl(config->url, "action", action, (char *)0);
89 if(printf("Content-Type: text/html\n"
90 "Refresh: %ld;url=%s\n"
93 refresh, url, dcgi_cookie_header()) < 0)
94 fatal(errno, "error writing to stdout");
95 dcgi_expand("playing");
98 static void act_disable(void) {
100 disorder_disable(dcgi_client);
104 static void act_enable(void) {
106 disorder_enable(dcgi_client);
110 static void act_random_disable(void) {
112 disorder_random_disable(dcgi_client);
116 static void act_random_enable(void) {
118 disorder_random_enable(dcgi_client);
122 static void act_remove(void) {
124 struct queue_entry *q;
127 if(!(id = cgi_get("id")))
128 error(0, "missing 'id' argument");
129 else if(!(q = dcgi_findtrack(id)))
130 error(0, "unknown queue id %s", id);
131 else switch(q->state) {
132 case playing_isscratch:
134 case playing_no_player:
136 case playing_quitting:
137 case playing_scratched:
138 error(0, "does not make sense to scratch %s", id);
140 case playing_paused: /* started but paused */
141 case playing_started: /* started to play */
142 disorder_scratch(dcgi_client, id);
144 case playing_random: /* unplayed randomly chosen track */
145 case playing_unplayed: /* haven't played this track yet */
146 disorder_remove(dcgi_client, id);
153 /** @brief Table of actions */
154 static const struct action {
155 /** @brief Action name */
157 /** @brief Action handler */
158 void (*handler)(void);
160 { "disable", act_disable },
161 { "enable", act_enable },
162 { "manage", act_playing },
163 { "playing", act_playing },
164 { "randomdisable", act_random_disable },
165 { "randomenable", act_random_enable },
166 { "remove", act_remove },
169 /** @brief Check that an action name is valid
171 * @return 1 if valid, 0 if not
173 static int dcgi_valid_action(const char *name) {
176 /* First character must be letter or digit (this also requires there to _be_
177 * a first character) */
178 if(!isalnum((unsigned char)*name))
180 /* Only letters, digits, '.' and '-' allowed */
181 while((c = (unsigned char)*name++)) {
190 /** @brief Expand a template
191 * @param name Base name of template, or NULL to consult CGI args
193 void dcgi_expand(const char *name) {
194 const char *p, *found;
196 /* Parse macros first */
197 if((found = mx_find("macros.tmpl")))
198 mx_expand_file(found, sink_discard(), 0);
199 /* For unknown actions check that they aren't evil */
200 if(!dcgi_valid_action(name))
201 fatal(0, "invalid action name '%s'", name);
202 byte_xasprintf((char **)&p, "%s.tmpl", name);
203 if(!(found = mx_find(p)))
204 fatal(errno, "cannot find %s", p);
205 if(mx_expand_file(found, sink_stdio("stdout", stdout), 0) == -1
206 || fflush(stdout) < 0)
207 fatal(errno, "error writing to stdout");
210 /** @brief Execute a web action
211 * @param action Action to perform, or NULL to consult CGI args
213 * If no recognized action is specified then 'playing' is assumed.
215 void dcgi_action(const char *action) {
218 /* Consult CGI args if caller had no view */
220 action = cgi_get("action");
221 /* Pick a default if nobody cares at all */
223 /* We allow URLs which are just c=... in order to keep confirmation URLs,
224 * which are user-facing, as short as possible. Actually we could lose the
230 /* Make sure 'action' is always set */
231 cgi_set("action", action);
233 if((n = TABLE_FIND(actions, struct action, name, action)) >= 0)
234 /* Its a known action */
235 actions[n].handler();
237 /* Just expand the template */
238 if(printf("Content-Type: text/html\n"
240 "\n", dcgi_cookie_header()) < 0)
241 fatal(errno, "error writing to stdout");
246 /** @brief Generate an error page */
247 void dcgi_error(const char *key) {
248 dcgi_error_string = xstrdup(key);
249 dcgi_expand("error");