Fix login/logout/etc and misc other bits and pieces
[disorder] / server / dcgi.c
CommitLineData
460b9539 1
2#include <stdio.h>
3#include <errno.h>
4#include <sys/types.h>
5#include <sys/socket.h>
6#include <stddef.h>
7#include <stdlib.h>
8#include <time.h>
9#include <unistd.h>
10#include <string.h>
11#include <sys/wait.h>
12#include <pcre.h>
13#include <assert.h>
14
15#include "client.h"
16#include "mem.h"
17#include "vector.h"
18#include "sink.h"
9faa7a88 19#include "server-cgi.h"
460b9539 20#include "log.h"
21#include "configuration.h"
22#include "table.h"
23#include "queue.h"
24#include "plugin.h"
25#include "split.h"
460b9539 26#include "wstat.h"
27#include "kvp.h"
28#include "syscalls.h"
29#include "printf.h"
30#include "regsub.h"
31#include "defs.h"
32#include "trackname.h"
61507e3c 33#include "charset.h"
938d8157 34#include "dcgi.h"
36bde473 35#include "url.h"
36#include "mime.h"
bb6ae3fb 37#include "sendmail.h"
f902253a 38#include "base64.h"
460b9539 39
460b9539 40struct entry {
41 const char *path;
42 const char *sort;
43 const char *display;
44};
45
460b9539 46static int compare_entry(const void *a, const void *b) {
47 const struct entry *ea = a, *eb = b;
48
49 return compare_tracks(ea->sort, eb->sort,
50 ea->display, eb->display,
51 ea->path, eb->path);
52}
53
54static const char *front_url(void) {
55 char *url;
56 const char *mgmt;
57
58 /* preserve management interface visibility */
59 if((mgmt = cgi_get("mgmt")) && !strcmp(mgmt, "true")) {
60 byte_xasprintf(&url, "%s?mgmt=true", config->url);
61 return url;
62 }
63 return config->url;
64}
65
7f66f58b 66static void redirect(struct sink *output) {
67 const char *back;
68
69 back = cgi_get("back");
70 cgi_header(output, "Location", back && *back ? back : front_url());
71 header_cookie(output);
72 cgi_body(output);
fdf98378 73}
74
75static void expand_template(dcgi_state *ds, cgi_sink *output,
76 const char *action) {
77 cgi_header(output->sink, "Content-Type", "text/html");
7f66f58b 78 header_cookie(output->sink);
fdf98378 79 cgi_body(output->sink);
80 expand(output, action, ds);
81}
82
460b9539 83/* actions ********************************************************************/
84
460b9539 85static void act_prefs_errors(const char *msg,
86 void attribute((unused)) *u) {
87 fatal(0, "error splitting parts list: %s", msg);
88}
89
90static const char *numbered_arg(const char *argname, int numfile) {
91 char *fullname;
92
93 byte_xasprintf(&fullname, "%d_%s", numfile, argname);
94 return cgi_get(fullname);
95}
96
97static void process_prefs(dcgi_state *ds, int numfile) {
98 const char *file, *name, *value, *part, *parts, *current, *context;
99 char **partslist;
100
101 if(!(file = numbered_arg("file", numfile)))
102 /* The first file doesn't need numbering. */
103 if(numfile > 0 || !(file = cgi_get("file")))
104 return;
105 if((parts = numbered_arg("parts", numfile))
106 || (parts = cgi_get("parts"))) {
107 /* Default context is display. Other contexts not actually tested. */
108 if(!(context = numbered_arg("context", numfile))) context = "display";
109 partslist = split(parts, 0, 0, act_prefs_errors, 0);
110 while((part = *partslist++)) {
111 if(!(value = numbered_arg(part, numfile)))
112 continue;
113 /* If it's already right (whether regexps or db) don't change anything,
114 * so we don't fill the database up with rubbish. */
115 if(disorder_part(ds->g->client, (char **)&current,
116 file, context, part))
117 fatal(0, "disorder_part() failed");
118 if(!strcmp(current, value))
119 continue;
120 byte_xasprintf((char **)&name, "trackname_%s_%s", context, part);
121 disorder_set(ds->g->client, file, name, value);
122 }
123 if((value = numbered_arg("random", numfile)))
124 disorder_unset(ds->g->client, file, "pick_at_random");
125 else
126 disorder_set(ds->g->client, file, "pick_at_random", "0");
2d0cdf2b
RK
127 if((value = numbered_arg("tags", numfile))) {
128 if(!*value)
129 disorder_unset(ds->g->client, file, "tags");
130 else
131 disorder_set(ds->g->client, file, "tags", value);
132 }
133 if((value = numbered_arg("weight", numfile))) {
134 if(!*value || !strcmp(value, "90000"))
135 disorder_unset(ds->g->client, file, "weight");
136 else
137 disorder_set(ds->g->client, file, "weight", value);
138 }
460b9539 139 } else if((name = cgi_get("name"))) {
140 /* Raw preferences. Not well supported in the templates at the moment. */
141 value = cgi_get("value");
142 if(value)
143 disorder_set(ds->g->client, file, name, value);
144 else
145 disorder_unset(ds->g->client, file, name);
146 }
147}
148
149static void act_prefs(cgi_sink *output, dcgi_state *ds) {
150 const char *files;
151 int nfiles, numfile;
152
153 if((files = cgi_get("files"))) nfiles = atoi(files);
154 else nfiles = 1;
155 for(numfile = 0; numfile < nfiles; ++numfile)
156 process_prefs(ds, numfile);
157 cgi_header(output->sink, "Content-Type", "text/html");
7f66f58b 158 header_cookie(output->sink);
460b9539 159 cgi_body(output->sink);
160 expand(output, "prefs", ds);
161}
460b9539 162/* expansions *****************************************************************/
163
460b9539 164static void exp_label(int attribute((unused)) nargs,
165 char **args,
166 cgi_sink *output,
167 void attribute((unused)) *u) {
168 cgi_output(output, "%s", cgi_label(args[0]));
169}
170
171struct trackinfo_state {
172 dcgi_state *ds;
173 const struct queue_entry *q;
174 long length;
175 time_t when;
176};
177
460b9539 178struct result {
179 char *track;
180 const char *sort;
181};
182
183static int compare_result(const void *a, const void *b) {
184 const struct result *ra = a, *rb = b;
185 int c;
186
187 if(!(c = strcmp(ra->sort, rb->sort)))
188 c = strcmp(ra->track, rb->track);
189 return c;
190}
191
460b9539 192static void exp_stats(int attribute((unused)) nargs,
193 char attribute((unused)) **args,
194 cgi_sink *output,
195 void *u) {
196 dcgi_state *ds = u;
197 char **v;
198
199 cgi_opentag(output->sink, "pre", "class", "stats", (char *)0);
200 if(!disorder_stats(ds->g->client, &v, 0)) {
201 while(*v)
202 cgi_output(output, "%s\n", *v++);
203 }
204 cgi_closetag(output->sink, "pre");
205}
206
460b9539 207static char *expandarg(const char *arg, dcgi_state *ds) {
208 struct dynstr d;
209 cgi_sink output;
210
211 dynstr_init(&d);
212 output.quote = 0;
213 output.sink = sink_dynstr(&d);
214 expandstring(&output, arg, ds);
215 dynstr_terminate(&d);
216 return d.vec;
217}
218
460b9539 219static void exp_navigate(int attribute((unused)) nargs,
220 char **args,
221 cgi_sink *output,
222 void *u) {
223 dcgi_state *ds = u;
224 dcgi_state substate;
225 const char *path = expandarg(args[0], ds);
226 const char *ptr;
227 int dirlen;
228
229 if(*path) {
230 memset(&substate, 0, sizeof substate);
231 substate.g = ds->g;
232 ptr = path + 1; /* skip root */
233 dirlen = 0;
234 substate.nav_path = path;
235 substate.first = 1;
236 while(*ptr) {
237 while(*ptr && *ptr != '/')
238 ++ptr;
239 substate.last = !*ptr;
240 substate.nav_len = ptr - path;
241 substate.nav_dirlen = dirlen;
242 expandstring(output, args[1], &substate);
243 dirlen = substate.nav_len;
244 if(*ptr) ++ptr;
245 substate.first = 0;
246 }
247 }
248}
249
250static void exp_fullname(int attribute((unused)) nargs,
251 char attribute((unused)) **args,
252 cgi_sink *output,
253 void *u) {
254 dcgi_state *ds = u;
255 cgi_output(output, "%.*s", ds->nav_len, ds->nav_path);
256}
257
460b9539 258/*
259Local Variables:
260c-basic-offset:2
261comment-column:40
262fill-column:79
263End:
264*/