5 #include <sys/socket.h>
19 #include "server-cgi.h"
21 #include "configuration.h"
32 #include "trackname.h"
46 static int compare_entry(const void *a, const void *b) {
47 const struct entry *ea = a, *eb = b;
49 return compare_tracks(ea->sort, eb->sort,
50 ea->display, eb->display,
54 static const char *front_url(void) {
58 /* preserve management interface visibility */
59 if((mgmt = cgi_get("mgmt")) && !strcmp(mgmt, "true")) {
60 byte_xasprintf(&url, "%s?mgmt=true", config->url);
66 static void redirect(struct sink *output) {
69 back = cgi_get("back");
70 cgi_header(output, "Location", back && *back ? back : front_url());
71 header_cookie(output);
75 static void expand_template(dcgi_state *ds, cgi_sink *output,
77 cgi_header(output->sink, "Content-Type", "text/html");
78 header_cookie(output->sink);
79 cgi_body(output->sink);
80 expand(output, action, ds);
83 /* actions ********************************************************************/
85 static void act_prefs_errors(const char *msg,
86 void attribute((unused)) *u) {
87 fatal(0, "error splitting parts list: %s", msg);
90 static const char *numbered_arg(const char *argname, int numfile) {
93 byte_xasprintf(&fullname, "%d_%s", numfile, argname);
94 return cgi_get(fullname);
97 static void process_prefs(dcgi_state *ds, int numfile) {
98 const char *file, *name, *value, *part, *parts, *current, *context;
101 if(!(file = numbered_arg("file", numfile)))
102 /* The first file doesn't need numbering. */
103 if(numfile > 0 || !(file = cgi_get("file")))
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)))
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 **)¤t,
116 file, context, part))
117 fatal(0, "disorder_part() failed");
118 if(!strcmp(current, value))
120 byte_xasprintf((char **)&name, "trackname_%s_%s", context, part);
121 disorder_set(ds->g->client, file, name, value);
123 if((value = numbered_arg("random", numfile)))
124 disorder_unset(ds->g->client, file, "pick_at_random");
126 disorder_set(ds->g->client, file, "pick_at_random", "0");
127 if((value = numbered_arg("tags", numfile))) {
129 disorder_unset(ds->g->client, file, "tags");
131 disorder_set(ds->g->client, file, "tags", value);
133 if((value = numbered_arg("weight", numfile))) {
134 if(!*value || !strcmp(value, "90000"))
135 disorder_unset(ds->g->client, file, "weight");
137 disorder_set(ds->g->client, file, "weight", value);
139 } else if((name = cgi_get("name"))) {
140 /* Raw preferences. Not well supported in the templates at the moment. */
141 value = cgi_get("value");
143 disorder_set(ds->g->client, file, name, value);
145 disorder_unset(ds->g->client, file, name);
149 static void act_prefs(cgi_sink *output, dcgi_state *ds) {
153 if((files = cgi_get("files"))) nfiles = atoi(files);
155 for(numfile = 0; numfile < nfiles; ++numfile)
156 process_prefs(ds, numfile);
157 cgi_header(output->sink, "Content-Type", "text/html");
158 header_cookie(output->sink);
159 cgi_body(output->sink);
160 expand(output, "prefs", ds);
163 static void act_login(cgi_sink *output,
165 const char *username, *password, *back;
168 username = cgi_get("username");
169 password = cgi_get("password");
170 if(!username || !password
171 || !strcmp(username, "guest")/*bodge to avoid guest cookies*/) {
172 /* We're just visiting the login page */
173 expand_template(ds, output, "login");
176 /* We'll need a new connection as we are going to stop being guest */
178 if(disorder_connect_user(c, username, password)) {
179 cgi_set_option("error", "loginfailed");
180 expand_template(ds, output, "login");
183 if(disorder_make_cookie(c, &login_cookie)) {
184 cgi_set_option("error", "cookiefailed");
185 expand_template(ds, output, "login");
188 /* Use the new connection henceforth */
191 /* We have a new cookie */
192 header_cookie(output->sink);
193 cgi_set_option("status", "loginok");
194 if((back = cgi_get("back")) && *back)
195 /* Redirect back to somewhere or other */
196 redirect(output->sink);
198 /* Stick to the login page */
199 expand_template(ds, output, "login");
202 static void act_logout(cgi_sink *output,
204 disorder_revoke(ds->g->client);
206 /* Reconnect as guest */
207 disorder_cgi_login(ds, output);
208 /* Back to the login page */
209 cgi_set_option("status", "logoutok");
210 expand_template(ds, output, "login");
213 static void act_register(cgi_sink *output,
215 const char *username, *password, *password2, *email;
216 char *confirm, *content_type;
217 const char *text, *encoding, *charset;
219 username = cgi_get("username");
220 password = cgi_get("password1");
221 password2 = cgi_get("password2");
222 email = cgi_get("email");
224 if(!username || !*username) {
225 cgi_set_option("error", "nousername");
226 expand_template(ds, output, "login");
229 if(!password || !*password) {
230 cgi_set_option("error", "nopassword");
231 expand_template(ds, output, "login");
234 if(!password2 || !*password2 || strcmp(password, password2)) {
235 cgi_set_option("error", "passwordmismatch");
236 expand_template(ds, output, "login");
239 if(!email || !*email) {
240 cgi_set_option("error", "noemail");
241 expand_template(ds, output, "login");
244 /* We could well do better address validation but for now we'll just do the
246 if(!strchr(email, '@')) {
247 cgi_set_option("error", "bademail");
248 expand_template(ds, output, "login");
251 if(disorder_register(ds->g->client, username, password, email, &confirm)) {
252 cgi_set_option("error", "cannotregister");
253 expand_template(ds, output, "login");
256 /* Send the user a mail */
257 /* TODO templatize this */
258 byte_xasprintf((char **)&text,
259 "Welcome to DisOrder. To active your login, please visit this URL:\n"
261 "%s?c=%s\n", config->url, urlencodestring(confirm));
262 if(!(text = mime_encode_text(text, &charset, &encoding)))
263 fatal(0, "cannot encode email");
264 byte_xasprintf(&content_type, "text/plain;charset=%s",
265 quote822(charset, 0));
266 sendmail("", config->mail_sender, email, "Welcome to DisOrder",
267 encoding, content_type, text); /* TODO error checking */
268 /* We'll go back to the login page with a suitable message */
269 cgi_set_option("status", "registered");
270 expand_template(ds, output, "login");
273 static void act_confirm(cgi_sink *output,
275 const char *confirmation;
277 if(!(confirmation = cgi_get("c"))) {
278 cgi_set_option("error", "noconfirm");
279 expand_template(ds, output, "login");
281 /* Confirm our registration */
282 if(disorder_confirm(ds->g->client, confirmation)) {
283 cgi_set_option("error", "badconfirm");
284 expand_template(ds, output, "login");
287 if(disorder_make_cookie(ds->g->client, &login_cookie)) {
288 cgi_set_option("error", "cookiefailed");
289 expand_template(ds, output, "login");
292 /* Discard any cached data JIC */
294 /* We have a new cookie */
295 header_cookie(output->sink);
296 cgi_set_option("status", "confirmed");
297 expand_template(ds, output, "login");
300 static void act_edituser(cgi_sink *output,
302 const char *email = cgi_get("email"), *password = cgi_get("changepassword1");
303 const char *password2 = cgi_get("changepassword2");
307 if((password && *password) || (password && *password2)) {
308 if(!password || !password2 || strcmp(password, password2)) {
309 cgi_set_option("error", "passwordmismatch");
310 expand_template(ds, output, "login");
314 password = password2 = 0;
317 if(disorder_edituser(ds->g->client, disorder_user(ds->g->client),
319 cgi_set_option("error", "badedit");
320 expand_template(ds, output, "login");
325 if(disorder_edituser(ds->g->client, disorder_user(ds->g->client),
326 "password", password)) {
327 cgi_set_option("error", "badedit");
328 expand_template(ds, output, "login");
334 login_cookie = 0; /* it'll be invalid now */
335 /* This is a bit duplicative of act_login() */
337 if(disorder_connect_user(c, disorder_user(ds->g->client), password)) {
338 cgi_set_option("error", "loginfailed");
339 expand_template(ds, output, "login");
342 if(disorder_make_cookie(c, &login_cookie)) {
343 cgi_set_option("error", "cookiefailed");
344 expand_template(ds, output, "login");
347 /* Use the new connection henceforth */
350 /* We have a new cookie */
351 header_cookie(output->sink);
353 cgi_set_option("status", "edited");
354 expand_template(ds, output, "login");
357 static void act_reminder(cgi_sink *output,
359 const char *const username = cgi_get("username");
361 if(!username || !*username) {
362 cgi_set_option("error", "nousername");
363 expand_template(ds, output, "login");
366 if(disorder_reminder(ds->g->client, username)) {
367 cgi_set_option("error", "reminderfailed");
368 expand_template(ds, output, "login");
371 cgi_set_option("status", "reminded");
372 expand_template(ds, output, "login");
375 /* expansions *****************************************************************/
377 static void exp_label(int attribute((unused)) nargs,
380 void attribute((unused)) *u) {
381 cgi_output(output, "%s", cgi_label(args[0]));
384 struct trackinfo_state {
386 const struct queue_entry *q;
396 static int compare_result(const void *a, const void *b) {
397 const struct result *ra = a, *rb = b;
400 if(!(c = strcmp(ra->sort, rb->sort)))
401 c = strcmp(ra->track, rb->track);
405 static void exp_stats(int attribute((unused)) nargs,
406 char attribute((unused)) **args,
412 cgi_opentag(output->sink, "pre", "class", "stats", (char *)0);
413 if(!disorder_stats(ds->g->client, &v, 0)) {
415 cgi_output(output, "%s\n", *v++);
417 cgi_closetag(output->sink, "pre");
420 static char *expandarg(const char *arg, dcgi_state *ds) {
426 output.sink = sink_dynstr(&d);
427 expandstring(&output, arg, ds);
428 dynstr_terminate(&d);
432 static void exp_navigate(int attribute((unused)) nargs,
438 const char *path = expandarg(args[0], ds);
443 memset(&substate, 0, sizeof substate);
445 ptr = path + 1; /* skip root */
447 substate.nav_path = path;
450 while(*ptr && *ptr != '/')
452 substate.last = !*ptr;
453 substate.nav_len = ptr - path;
454 substate.nav_dirlen = dirlen;
455 expandstring(output, args[1], &substate);
456 dirlen = substate.nav_len;
463 static void exp_fullname(int attribute((unused)) nargs,
464 char attribute((unused)) **args,
468 cgi_output(output, "%.*s", ds->nav_len, ds->nav_path);