chiark / gitweb /
redirect()
[disorder] / server / actions.c
CommitLineData
bca4e2b7
RK
1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2004-2008 Richard Kettlewell
4 *
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.
9 *
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.
14 *
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
18 * USA
19 */
20
21#include <config.h>
22#include "types.h"
23
448d3570
RK
24#include "actions.h"
25#include "lookups.h"
26
bca4e2b7
RK
27/** @brief Login cookie */
28char *login_cookie;
29
5a7df048
RK
30/** @brief Return a Cookie: header */
31static char *cookie(void) {
32 struct dynstr d[1];
33 struct url u;
34 char *s;
35
36 memset(&u, 0, sizeof u);
37 dynstr_init(d);
38 parse_url(config->url, &u);
39 if(login_cookie) {
40 dynstr_append_string(d, "disorder=");
41 dynstr_append_string(d, login_cookie);
42 } else {
43 /* Force browser to discard cookie */
44 dynstr_append_string(d, "disorder=none;Max-Age=0");
45 }
46 if(u.path) {
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
50 * web server. */
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
57 * in.) */
58 dynstr_append_string(d, u.path);
59 }
60 dynstr_terminate(d);
61 byte_xasprintf(&s, "Set-Cookie: %s", d->vec);
62 return s;
63}
64
65/** @brief Redirect to some other action or URL */
66static void redirect(const char *url) {
67 /* By default use the 'back' argument */
68 if(!url)
69 url = cgi_get("back")
70 if(url) {
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);
74 } else {
75 /* If back= is not set just go back to the front page */
76 url = config->url;
77 }
78 if(printf("Location: %s\n"
79 "%s\n"
80 "\n", url, cookie()) < 0)
81 fatal(errno, "error writing to stdout");
82}
83
448d3570
RK
84/* 'playing' and 'manage' just add a Refresh: header */
85static void act_playing(void) {
86 long refresh = config->refresh;
87 long length;
88 time_t now, fin;
89 char *url;
90
91 lookups(DC_PLAYING|DC_QUEUE|DC_ENABLED|DC_RANDOM_ENABLED);
92 if(playing
93 && playing->state == playing_started /* i.e. not paused */
94 && !disorder_length(client, playing->track, &length)
95 && length
96 && playing->sofar >= 0) {
97 /* Try to put the next refresh at the start of the next track. */
98 time(&now);
99 fin = now + length - playing->sofar + config->gap;
100 if(now + refresh > fin)
101 refresh = fin - now;
102 }
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;
107 }
108 if(!playing
109 && ((queue
110 && queue->state != playing_random)
111 || random_enabled)
112 && enabled) {
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;
117 }
118 if((action = cgi_get("action")))
119 url = cgi_makeurl(config->url, "action", action, (char *)0);
120 else
121 url = config->url;
122 if(printf("Content-Type: text/html\n"
123 "Refresh: %ld;url=%s\n"
5a7df048 124 "%s\n"
448d3570 125 "\n",
5a7df048 126 refresh, url, cookie()) < 0)
448d3570
RK
127 fatal(errno, "error writing to stdout");
128 disorder_cgi_expand(action ? action : "playing");
129}
130
bca4e2b7
RK
131/** @brief Table of actions */
132static const struct action {
133 /** @brief Action name */
134 const char *name;
135 /** @brief Action handler */
136 void (*handler)(void);
137} actions[] = {
138 { "confirm", act_confirm },
139 { "disable", act_disable },
140 { "edituser", act_edituser },
141 { "enable", act_enable },
142 { "login", act_login },
143 { "logout", act_logout },
448d3570 144 { "manage", act_playing },
bca4e2b7
RK
145 { "move", act_move },
146 { "pause", act_pause },
147 { "play", act_play },
148 { "playing", act_playing },
149 { "prefs", act_prefs },
150 { "random-disable", act_random_disable },
151 { "random-enable", act_random_enable },
152 { "register", act_register },
153 { "reminder", act_reminder },
154 { "remove", act_remove },
155 { "resume", act_resume },
156 { "scratch", act_scratch },
157 { "volume", act_volume },
158};
159
160/** @brief Expand a template
161 * @param name Base name of template, or NULL to consult CGI args
162 */
163void disorder_cgi_expand(const char *name) {
164 const char *p;
165
166 /* For unknown actions check that they aren't evil */
167 for(p = name; *p && isalnum((unsigned char)*p); ++p)
168 ;
169 if(*p)
170 fatal(0, "invalid action name '%s'", action);
171 byte_xasprintf((char **)&p, "%s.tmpl", action);
172 if(mx_expand_file(p, sink_stdio(stdout), 0) == -1
173 || fflush(stdout) < 0)
174 fatal(errno, "error writing to stdout");
175}
176
177/** @brief Execute a web action
178 * @param action Action to perform, or NULL to consult CGI args
179 *
180 * If no recognized action is specified then 'playing' is assumed.
181 */
182void disorder_cgi_action(const char *action) {
183 int n;
184 char *s;
185
186 /* Consult CGI args if caller had no view */
187 if(!action)
188 action = cgi_get("action");
189 /* Pick a default if nobody cares at all */
190 if(!action) {
191 /* We allow URLs which are just c=... in order to keep confirmation URLs,
192 * which are user-facing, as short as possible. Actually we could lose the
193 * c= for this... */
194 if(cgi_get("c"))
195 action = "confirm";
196 else
197 action = "playing";
5a7df048
RK
198 /* Make sure 'action' is always set */
199 cgi_set("action", action);
bca4e2b7
RK
200 }
201 if((n = TABLE_FIND(actions, struct action, name, action)) >= 0)
202 /* Its a known action */
203 actions[n].handler();
448d3570 204 else {
bca4e2b7 205 /* Just expand the template */
448d3570 206 if(printf("Content-Type: text/html\n"
5a7df048
RK
207 "%s\n"
208 "\n", cookie()) < 0)
448d3570 209 fatal(errno, "error writing to stdout");
bca4e2b7 210 disorder_cgi_expand(action);
448d3570 211 }
bca4e2b7
RK
212}
213
214/** @brief Generate an error page */
215void disorder_cgi_error(const char *msg, ...) {
216 va_list ap;
217
218 va_start(ap, msg);
219 byte_xvasprintf(&error_string, msg, ap);
220 va_end(ap);
221 disorder_cgi_expand("error");
222}
223
224/** @brief Log in as the current user or guest if none */
225void disorder_cgi_login(dcgi_state *ds, struct sink *output) {
226 /* Junk old data */
227 disorder_macros_reset();
228 /* Reconnect */
229 if(disorder_connect_cookie(client, login_cookie)) {
230 disorder_cgi_error("Cannot connect to server");
231 exit(0);
232 }
233 /* If there was a cookie but it went bad, we forget it */
234 if(login_cookie && !strcmp(disorder_user(>client), "guest"))
235 login_cookie = 0;
236}
237
238/*
239Local Variables:
240c-basic-offset:2
241comment-column:40
242fill-column:79
243indent-tabs-mode:nil
244End:
245*/