chiark / gitweb /
Almost compiles, will not work though
[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
71634563
RK
24#include <string.h>
25#include <stdio.h>
26#include <errno.h>
27#include <time.h>
28#include <ctype.h>
29#include <stddef.h>
30
31#include "hash.h"
32#include "table.h"
33#include "client.h"
34#include "rights.h"
35#include "mem.h"
36#include "sink.h"
37#include "vector.h"
38#include "printf.h"
448d3570 39#include "actions.h"
71634563
RK
40#include "lookup.h"
41#include "url.h"
42#include "configuration.h"
43#include "cgi.h"
44#include "log.h"
45#include "queue.h"
46#include "macros.h"
47#include "macros-disorder.h"
448d3570 48
bca4e2b7
RK
49/** @brief Login cookie */
50char *login_cookie;
51
5a7df048
RK
52/** @brief Return a Cookie: header */
53static char *cookie(void) {
54 struct dynstr d[1];
55 struct url u;
56 char *s;
57
58 memset(&u, 0, sizeof u);
59 dynstr_init(d);
60 parse_url(config->url, &u);
61 if(login_cookie) {
62 dynstr_append_string(d, "disorder=");
63 dynstr_append_string(d, login_cookie);
64 } else {
65 /* Force browser to discard cookie */
66 dynstr_append_string(d, "disorder=none;Max-Age=0");
67 }
68 if(u.path) {
69 /* The default domain matches the request host, so we need not override
70 * that. But the default path only goes up to the rightmost /, which would
71 * cause the browser to expose the cookie to other CGI programs on the same
72 * web server. */
73 dynstr_append_string(d, ";Version=1;Path=");
74 /* Formally we are supposed to quote the path, since it invariably has a
75 * slash in it. However Safari does not parse quoted paths correctly, so
76 * this won't work. Fortunately nothing else seems to care about proper
77 * quoting of paths, so in practice we get with it. (See also
78 * parse_cookie() where we are liberal about cookie paths on the way back
79 * in.) */
80 dynstr_append_string(d, u.path);
81 }
82 dynstr_terminate(d);
83 byte_xasprintf(&s, "Set-Cookie: %s", d->vec);
84 return s;
85}
86
87/** @brief Redirect to some other action or URL */
88static void redirect(const char *url) {
89 /* By default use the 'back' argument */
90 if(!url)
71634563 91 url = cgi_get("back");
5a7df048
RK
92 if(url) {
93 if(!strncmp(url, "http", 4))
94 /* If the target is not a full URL assume it's the action */
95 url = cgi_makeurl(config->url, "action", url, (char *)0);
96 } else {
97 /* If back= is not set just go back to the front page */
98 url = config->url;
99 }
100 if(printf("Location: %s\n"
101 "%s\n"
102 "\n", url, cookie()) < 0)
103 fatal(errno, "error writing to stdout");
104}
105
448d3570
RK
106/* 'playing' and 'manage' just add a Refresh: header */
107static void act_playing(void) {
108 long refresh = config->refresh;
109 long length;
110 time_t now, fin;
111 char *url;
71634563 112 const char *action;
448d3570 113
71634563 114 lookup(DC_PLAYING|DC_QUEUE|DC_ENABLED|DC_RANDOM_ENABLED);
448d3570
RK
115 if(playing
116 && playing->state == playing_started /* i.e. not paused */
117 && !disorder_length(client, playing->track, &length)
118 && length
119 && playing->sofar >= 0) {
120 /* Try to put the next refresh at the start of the next track. */
121 time(&now);
122 fin = now + length - playing->sofar + config->gap;
123 if(now + refresh > fin)
124 refresh = fin - now;
125 }
126 if(queue && queue->state == playing_isscratch) {
127 /* next track is a scratch, don't leave more than the inter-track gap */
128 if(refresh > config->gap)
129 refresh = config->gap;
130 }
131 if(!playing
132 && ((queue
133 && queue->state != playing_random)
134 || random_enabled)
135 && enabled) {
136 /* no track playing but playing is enabled and there is something coming
137 * up, must be in a gap */
138 if(refresh > config->gap)
139 refresh = config->gap;
140 }
141 if((action = cgi_get("action")))
142 url = cgi_makeurl(config->url, "action", action, (char *)0);
143 else
144 url = config->url;
145 if(printf("Content-Type: text/html\n"
146 "Refresh: %ld;url=%s\n"
5a7df048 147 "%s\n"
448d3570 148 "\n",
5a7df048 149 refresh, url, cookie()) < 0)
448d3570
RK
150 fatal(errno, "error writing to stdout");
151 disorder_cgi_expand(action ? action : "playing");
152}
153
bca4e2b7
RK
154/** @brief Table of actions */
155static const struct action {
156 /** @brief Action name */
157 const char *name;
158 /** @brief Action handler */
159 void (*handler)(void);
160} actions[] = {
04024c2c 161#if 0
bca4e2b7
RK
162 { "confirm", act_confirm },
163 { "disable", act_disable },
164 { "edituser", act_edituser },
165 { "enable", act_enable },
166 { "login", act_login },
167 { "logout", act_logout },
448d3570 168 { "manage", act_playing },
bca4e2b7
RK
169 { "move", act_move },
170 { "pause", act_pause },
171 { "play", act_play },
04024c2c 172#endif
bca4e2b7 173 { "playing", act_playing },
04024c2c 174#if 0
bca4e2b7
RK
175 { "prefs", act_prefs },
176 { "random-disable", act_random_disable },
177 { "random-enable", act_random_enable },
178 { "register", act_register },
179 { "reminder", act_reminder },
180 { "remove", act_remove },
181 { "resume", act_resume },
182 { "scratch", act_scratch },
183 { "volume", act_volume },
04024c2c 184#endif
bca4e2b7
RK
185};
186
187/** @brief Expand a template
188 * @param name Base name of template, or NULL to consult CGI args
189 */
190void disorder_cgi_expand(const char *name) {
191 const char *p;
192
193 /* For unknown actions check that they aren't evil */
194 for(p = name; *p && isalnum((unsigned char)*p); ++p)
195 ;
196 if(*p)
71634563
RK
197 fatal(0, "invalid action name '%s'", name);
198 byte_xasprintf((char **)&p, "%s.tmpl", name);
199 if(mx_expand_file(p, sink_stdio("stdout", stdout), 0) == -1
bca4e2b7
RK
200 || fflush(stdout) < 0)
201 fatal(errno, "error writing to stdout");
202}
203
204/** @brief Execute a web action
205 * @param action Action to perform, or NULL to consult CGI args
206 *
207 * If no recognized action is specified then 'playing' is assumed.
208 */
209void disorder_cgi_action(const char *action) {
210 int n;
bca4e2b7
RK
211
212 /* Consult CGI args if caller had no view */
213 if(!action)
214 action = cgi_get("action");
215 /* Pick a default if nobody cares at all */
216 if(!action) {
217 /* We allow URLs which are just c=... in order to keep confirmation URLs,
218 * which are user-facing, as short as possible. Actually we could lose the
219 * c= for this... */
220 if(cgi_get("c"))
221 action = "confirm";
222 else
223 action = "playing";
5a7df048
RK
224 /* Make sure 'action' is always set */
225 cgi_set("action", action);
bca4e2b7
RK
226 }
227 if((n = TABLE_FIND(actions, struct action, name, action)) >= 0)
228 /* Its a known action */
229 actions[n].handler();
448d3570 230 else {
bca4e2b7 231 /* Just expand the template */
448d3570 232 if(printf("Content-Type: text/html\n"
5a7df048
RK
233 "%s\n"
234 "\n", cookie()) < 0)
448d3570 235 fatal(errno, "error writing to stdout");
bca4e2b7 236 disorder_cgi_expand(action);
448d3570 237 }
bca4e2b7
RK
238}
239
240/** @brief Generate an error page */
241void disorder_cgi_error(const char *msg, ...) {
242 va_list ap;
243
244 va_start(ap, msg);
245 byte_xvasprintf(&error_string, msg, ap);
246 va_end(ap);
247 disorder_cgi_expand("error");
248}
249
250/** @brief Log in as the current user or guest if none */
71634563 251void disorder_cgi_login(void) {
bca4e2b7 252 /* Junk old data */
71634563 253 lookup_reset();
bca4e2b7
RK
254 /* Reconnect */
255 if(disorder_connect_cookie(client, login_cookie)) {
256 disorder_cgi_error("Cannot connect to server");
257 exit(0);
258 }
259 /* If there was a cookie but it went bad, we forget it */
71634563 260 if(login_cookie && !strcmp(disorder_user(client), "guest"))
bca4e2b7
RK
261 login_cookie = 0;
262}
263
264/*
265Local Variables:
266c-basic-offset:2
267comment-column:40
268fill-column:79
269indent-tabs-mode:nil
270End:
271*/