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
27 /** @brief Login cookie */
30 /** @brief Return a Cookie: header */
31 static char *cookie(void) {
36 memset(&u, 0, sizeof u);
38 parse_url(config->url, &u);
40 dynstr_append_string(d, "disorder=");
41 dynstr_append_string(d, login_cookie);
43 /* Force browser to discard cookie */
44 dynstr_append_string(d, "disorder=none;Max-Age=0");
47 /* The default domain matches the request host, so we need not override
48 * that. But the default path only goes up to the rightmost /, which would
49 * cause the browser to expose the cookie to other CGI programs on the same
51 dynstr_append_string(d, ";Version=1;Path=");
52 /* Formally we are supposed to quote the path, since it invariably has a
53 * slash in it. However Safari does not parse quoted paths correctly, so
54 * this won't work. Fortunately nothing else seems to care about proper
55 * quoting of paths, so in practice we get with it. (See also
56 * parse_cookie() where we are liberal about cookie paths on the way back
58 dynstr_append_string(d, u.path);
61 byte_xasprintf(&s, "Set-Cookie: %s", d->vec);
65 /** @brief Redirect to some other action or URL */
66 static void redirect(const char *url) {
67 /* By default use the 'back' argument */
71 if(!strncmp(url, "http", 4))
72 /* If the target is not a full URL assume it's the action */
73 url = cgi_makeurl(config->url, "action", url, (char *)0);
75 /* If back= is not set just go back to the front page */
78 if(printf("Location: %s\n"
80 "\n", url, cookie()) < 0)
81 fatal(errno, "error writing to stdout");
84 /* 'playing' and 'manage' just add a Refresh: header */
85 static void act_playing(void) {
86 long refresh = config->refresh;
91 lookups(DC_PLAYING|DC_QUEUE|DC_ENABLED|DC_RANDOM_ENABLED);
93 && playing->state == playing_started /* i.e. not paused */
94 && !disorder_length(client, playing->track, &length)
96 && playing->sofar >= 0) {
97 /* Try to put the next refresh at the start of the next track. */
99 fin = now + length - playing->sofar + config->gap;
100 if(now + refresh > fin)
103 if(queue && queue->state == playing_isscratch) {
104 /* next track is a scratch, don't leave more than the inter-track gap */
105 if(refresh > config->gap)
106 refresh = config->gap;
110 && queue->state != playing_random)
113 /* no track playing but playing is enabled and there is something coming
114 * up, must be in a gap */
115 if(refresh > config->gap)
116 refresh = config->gap;
118 if((action = cgi_get("action")))
119 url = cgi_makeurl(config->url, "action", action, (char *)0);
122 if(printf("Content-Type: text/html\n"
123 "Refresh: %ld;url=%s\n"
126 refresh, url, cookie()) < 0)
127 fatal(errno, "error writing to stdout");
128 disorder_cgi_expand(action ? action : "playing");
131 /** @brief Table of actions */
132 static const struct action {
133 /** @brief Action name */
135 /** @brief Action handler */
136 void (*handler)(void);
139 { "confirm", act_confirm },
140 { "disable", act_disable },
141 { "edituser", act_edituser },
142 { "enable", act_enable },
143 { "login", act_login },
144 { "logout", act_logout },
145 { "manage", act_playing },
146 { "move", act_move },
147 { "pause", act_pause },
148 { "play", act_play },
150 { "playing", act_playing },
152 { "prefs", act_prefs },
153 { "random-disable", act_random_disable },
154 { "random-enable", act_random_enable },
155 { "register", act_register },
156 { "reminder", act_reminder },
157 { "remove", act_remove },
158 { "resume", act_resume },
159 { "scratch", act_scratch },
160 { "volume", act_volume },
164 /** @brief Expand a template
165 * @param name Base name of template, or NULL to consult CGI args
167 void disorder_cgi_expand(const char *name) {
170 /* For unknown actions check that they aren't evil */
171 for(p = name; *p && isalnum((unsigned char)*p); ++p)
174 fatal(0, "invalid action name '%s'", action);
175 byte_xasprintf((char **)&p, "%s.tmpl", action);
176 if(mx_expand_file(p, sink_stdio(stdout), 0) == -1
177 || fflush(stdout) < 0)
178 fatal(errno, "error writing to stdout");
181 /** @brief Execute a web action
182 * @param action Action to perform, or NULL to consult CGI args
184 * If no recognized action is specified then 'playing' is assumed.
186 void disorder_cgi_action(const char *action) {
190 /* Consult CGI args if caller had no view */
192 action = cgi_get("action");
193 /* Pick a default if nobody cares at all */
195 /* We allow URLs which are just c=... in order to keep confirmation URLs,
196 * which are user-facing, as short as possible. Actually we could lose the
202 /* Make sure 'action' is always set */
203 cgi_set("action", action);
205 if((n = TABLE_FIND(actions, struct action, name, action)) >= 0)
206 /* Its a known action */
207 actions[n].handler();
209 /* Just expand the template */
210 if(printf("Content-Type: text/html\n"
213 fatal(errno, "error writing to stdout");
214 disorder_cgi_expand(action);
218 /** @brief Generate an error page */
219 void disorder_cgi_error(const char *msg, ...) {
223 byte_xvasprintf(&error_string, msg, ap);
225 disorder_cgi_expand("error");
228 /** @brief Log in as the current user or guest if none */
229 void disorder_cgi_login(dcgi_state *ds, struct sink *output) {
231 disorder_macros_reset();
233 if(disorder_connect_cookie(client, login_cookie)) {
234 disorder_cgi_error("Cannot connect to server");
237 /* If there was a cookie but it went bad, we forget it */
238 if(login_cookie && !strcmp(disorder_user(>client), "guest"))