chiark / gitweb /
reorg cgi code a bit...
[disorder] / server / macros-disorder.c
CommitLineData
9faa7a88
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/** @file server/macros-disorder.c
21 * @brief DisOrder-specific expansions
22 */
23
1e97629d 24#include "disorder-cgi.h"
9faa7a88 25
bca4e2b7 26/** @brief For error template */
1e97629d 27char *dcgi_error_string;
bca4e2b7 28
9faa7a88
RK
29/** @brief Locate a track by ID */
30static struct queue_entry *findtrack(const char *id) {
31 struct queue_entry *q;
32
1e97629d
RK
33 dcgi_lookup(DCGI_PLAYING);
34 if(dcgi_playing && !strcmp(dcgi_playing->id, id))
35 return dcgi_playing;
36 dcgi_lookup(DCGI_QUEUE);
37 for(q = dcgi_queue; q; q = q->next)
9faa7a88
RK
38 if(!strcmp(q->id, id))
39 return q;
1e97629d
RK
40 dcgi_lookup(DCGI_RECENT);
41 for(q = dcgi_recent; q; q = q->next)
9faa7a88
RK
42 if(!strcmp(q->id, id))
43 return q;
71634563 44 return NULL;
9faa7a88
RK
45}
46
47/** @brief Return @p i as a string */
48static const char *make_index(int i) {
49 char *s;
50
51 byte_xasprintf(&s, "%d", i);
52 return s;
53}
54
bca4e2b7 55/* @server-version
9faa7a88
RK
56 *
57 * Expands to the server's version string, or a (safe to use) error
58 * value if the server is unavailable or broken.
59 */
60static int exp_server_version(int attribute((unused)) nargs,
61 char attribute((unused)) **args,
62 struct sink *output,
63 void attribute((unused)) *u) {
64 const char *v;
65
1e97629d
RK
66 if(dcgi_client) {
67 if(disorder_version(dcgi_client, (char **)&v))
9faa7a88
RK
68 v = "(cannot get version)";
69 } else
70 v = "(server not running)";
71634563 71 return sink_writes(output, cgi_sgmlquote(v)) < 0 ? -1 : 0;
9faa7a88
RK
72}
73
bca4e2b7 74/* @version
9faa7a88
RK
75 *
76 * Expands to the local version string.
77 */
78static int exp_version(int attribute((unused)) nargs,
79 char attribute((unused)) **args,
80 struct sink *output,
81 void attribute((unused)) *u) {
71634563
RK
82 return sink_writes(output,
83 cgi_sgmlquote(disorder_short_version_string)) < 0 ? -1 : 0;
9faa7a88
RK
84}
85
bca4e2b7 86/* @url
9faa7a88
RK
87 *
88 * Expands to the base URL of the web interface.
89 */
90static int exp_url(int attribute((unused)) nargs,
91 char attribute((unused)) **args,
92 struct sink *output,
93 void attribute((unused)) *u) {
71634563
RK
94 return sink_writes(output,
95 cgi_sgmlquote(config->url)) < 0 ? -1 : 0;
9faa7a88
RK
96}
97
bca4e2b7 98/* @arg{NAME}
9faa7a88
RK
99 *
100 * Expands to the CGI argument NAME, or the empty string if there is
101 * no such argument.
102 */
103static int exp_arg(int attribute((unused)) nargs,
104 char **args,
105 struct sink *output,
106 void attribute((unused)) *u) {
107 const char *s = cgi_get(args[0]);
108 if(s)
71634563
RK
109 return sink_writes(output,
110 cgi_sgmlquote(s)) < 0 ? -1 : 0;
9faa7a88
RK
111 else
112 return 0;
113}
114
bca4e2b7 115/* @user
9faa7a88
RK
116 *
117 * Expands to the logged-in username (which might be "guest"), or to
118 * the empty string if not connected.
119 */
120static int exp_user(int attribute((unused)) nargs,
121 char attribute((unused)) **args,
122 struct sink *output,
123 void attribute((unused)) *u) {
71634563 124 const char *user;
9faa7a88 125
1e97629d 126 if(dcgi_client && (user = disorder_user(dcgi_client)))
71634563 127 return sink_writes(output, cgi_sgmlquote(user)) < 0 ? -1 : 0;
9faa7a88
RK
128 return 0;
129}
130
131/* @part{TRACK|ID}{PART}{CONTEXT}
132 *
133 * Expands to a track name part.
134 *
135 * A track may be identified by name or by queue ID.
136 *
137 * CONTEXT may be omitted. If it is then 'display' is assumed.
138 *
139 * If the CONTEXT is 'short' then the 'display' part is looked up, and the
140 * result truncated according to the length defined by the short_display
141 * configuration directive.
142 */
143static int exp_part(int nargs,
144 char **args,
145 struct sink *output,
146 void attribute((unused)) *u) {
147 const char *track = args[0], *part = args[1];
148 const char *context = nargs > 2 ? args[2] : "display";
149 char *s;
150
151 if(track[0] != '/') {
152 struct queue_entry *q = findtrack(track);
153
154 if(q)
155 track = q->track;
156 else
157 return 0;
158 }
1e97629d
RK
159 if(dcgi_client
160 && !disorder_part(dcgi_client, &s,
71634563
RK
161 track,
162 !strcmp(context, "short") ? "display" : context,
163 part))
164 return sink_writes(output, cgi_sgmlquote(s)) < 0 ? -1 : 0;
9faa7a88
RK
165 return 0;
166}
167
168/* @quote{STRING}
169 *
170 * SGML-quotes STRING. Note that most expansion results are already suitable
171 * quoted, so this expansion is usually not required.
172 */
173static int exp_quote(int attribute((unused)) nargs,
174 char **args,
175 struct sink *output,
176 void attribute((unused)) *u) {
71634563 177 return sink_writes(output, cgi_sgmlquote(args[0])) < 0 ? -1 : 0;
9faa7a88
RK
178}
179
180/* @who{ID}
181 *
182 * Expands to the name of the submitter of track ID, which must be a playing
183 * track, in the queue, or in the recent list.
184 */
185static int exp_who(int attribute((unused)) nargs,
186 char **args,
187 struct sink *output,
188 void attribute((unused)) *u) {
189 struct queue_entry *q = findtrack(args[0]);
190
191 if(q && q->submitter)
71634563 192 return sink_writes(output, cgi_sgmlquote(q->submitter)) < 0 ? -1 : 0;
9faa7a88
RK
193 return 0;
194}
195
196/* @when{ID}
197 *
198 * Expands to the time a track started or is expected to start. The track must
199 * be a playing track, in the queue, or in the recent list.
200 */
201static int exp_when(int attribute((unused)) nargs,
202 char **args,
203 struct sink *output,
204 void attribute((unused)) *u) {
205 struct queue_entry *q = findtrack(args[0]);
206 const struct tm *w = 0;
207
208 if(q) {
209 switch(q->state) {
210 case playing_isscratch:
211 case playing_unplayed:
212 case playing_random:
213 if(q->expected)
214 w = localtime(&q->expected);
215 break;
216 case playing_failed:
217 case playing_no_player:
218 case playing_ok:
219 case playing_scratched:
220 case playing_started:
221 case playing_paused:
222 case playing_quitting:
223 if(q->played)
224 w = localtime(&q->played);
225 break;
226 }
227 if(w)
228 return sink_printf(output, "%d:%02d", w->tm_hour, w->tm_min) < 0 ? -1 : 0;
229 }
71634563 230 return sink_writes(output, "&nbsp;") < 0 ? -1 : 0;
9faa7a88
RK
231}
232
233/* @length{ID|TRACK}
234 *
235 * Expands to the length of a track, identified by its queue ID or its name.
236 * If it is the playing track (identified by ID) then the amount played so far
237 * is included.
238 */
239static int exp_length(int attribute((unused)) nargs,
240 char **args,
241 struct sink *output,
242 void attribute((unused)) *u) {
243 struct queue_entry *q;
244 long length = 0;
245 const char *name;
246
247 if(args[0][0] == '/')
248 /* Track identified by name */
249 name = args[0];
250 else {
251 /* Track identified by queue ID */
252 if(!(q = findtrack(args[0])))
253 return 0;
71634563 254 if(q->state == playing_started || q->state == playing_paused)
9faa7a88
RK
255 if(sink_printf(output, "%ld:%02ld/", q->sofar / 60, q->sofar % 60) < 0)
256 return -1;
257 name = q->track;
258 }
1e97629d 259 if(dcgi_client && disorder_length(dcgi_client, name, &length))
9faa7a88
RK
260 return sink_printf(output, "%ld:%02ld",
261 length / 60, length % 60) < 0 ? -1 : 0;
71634563 262 return sink_writes(output, "&nbsp;") < 0 ? -1 : 0;
9faa7a88
RK
263}
264
bca4e2b7 265/* @removable{ID}
9faa7a88
RK
266 *
267 * Expands to "true" if track ID is removable (or scratchable, if it is the
268 * playing track) and "false" otherwise.
269 */
270static int exp_removable(int attribute((unused)) nargs,
271 char **args,
272 struct sink *output,
273 void attribute((unused)) *u) {
274 struct queue_entry *q = findtrack(args[0]);
275 /* TODO would be better to reject recent */
276
1e97629d 277 if(!q || !dcgi_client)
9faa7a88 278 return mx_bool_result(output, 0);
1e97629d 279 dcgi_lookup(DCGI_RIGHTS);
9faa7a88 280 return mx_bool_result(output,
1e97629d
RK
281 (q == dcgi_playing ? right_scratchable : right_removable)
282 (dcgi_rights, disorder_user(dcgi_client), q));
9faa7a88
RK
283}
284
bca4e2b7 285/* @movable{ID}
9faa7a88
RK
286 *
287 * Expands to "true" if track ID is movable and "false" otherwise.
288 */
289static int exp_movable(int attribute((unused)) nargs,
290 char **args,
291 struct sink *output,
292 void attribute((unused)) *u) {
293 struct queue_entry *q = findtrack(args[0]);
294 /* TODO would be better to recent playing/recent */
295
1e97629d 296 if(!q || !dcgi_client)
9faa7a88 297 return mx_bool_result(output, 0);
1e97629d 298 dcgi_lookup(DCGI_RIGHTS);
9faa7a88 299 return mx_bool_result(output,
1e97629d 300 right_movable(dcgi_rights, disorder_user(dcgi_client), q));
9faa7a88
RK
301}
302
303/* @playing{TEMPLATE}
304 *
305 * Expands to TEMPLATE, with:
306 * - @id@ expanded to the queue ID of the playing track
307 * - @track@ expanded to its UNQUOTED name
308 *
309 * If no track is playing expands to nothing.
310 *
311 * TEMPLATE is optional. If it is left out then instead expands to the queue
312 * ID of the playing track.
313 */
314static int exp_playing(int nargs,
315 const struct mx_node **args,
316 struct sink *output,
71634563 317 void *u) {
1e97629d
RK
318 dcgi_lookup(DCGI_PLAYING);
319 if(!dcgi_playing)
9faa7a88
RK
320 return 0;
321 if(!nargs)
1e97629d 322 return sink_writes(output, dcgi_playing->id) < 0 ? -1 : 0;
71634563 323 return mx_expand(mx_rewritel(args[0],
1e97629d
RK
324 "id", dcgi_playing->id,
325 "track", dcgi_playing->track,
71634563
RK
326 (char *)0),
327 output, u);
9faa7a88
RK
328}
329
330/* @queue{TEMPLATE}
331 *
332 * For each track in the queue, expands TEMPLATE with the following expansions:
333 * - @id@ to the queue ID of the track
334 * - @track@ to the UNQUOTED track name
335 * - @index@ to the track number from 0
336 * - @parity@ to "even" or "odd" alternately
337 * - @first@ to "true" on the first track and "false" otherwise
338 * - @last@ to "true" on the last track and "false" otherwise
339 */
340static int exp_queue(int attribute((unused)) nargs,
341 const struct mx_node **args,
342 struct sink *output,
71634563 343 void *u) {
9faa7a88
RK
344 struct queue_entry *q;
345 int rc, i;
346
1e97629d
RK
347 dcgi_lookup(DCGI_QUEUE);
348 for(q = dcgi_queue, i = 0; q; q = q->next, ++i)
71634563
RK
349 if((rc = mx_expand(mx_rewritel(args[0],
350 "id", q->id,
351 "track", q->track,
352 "index", make_index(i),
353 "parity", i % 2 ? "odd" : "even",
1e97629d 354 "first", q == dcgi_queue ? "true" : "false",
71634563
RK
355 "last", q->next ? "false" : "true",
356 (char *)0),
357 output, u)))
9faa7a88
RK
358 return rc;
359 return 0;
360}
361
362/* @recent{TEMPLATE}
363 *
364 * For each track in the recently played list, expands TEMPLATE with the
365 * following expansions:
366 * - @id@ to the queue ID of the track
367 * - @track@ to the UNQUOTED track name
368 * - @index@ to the track number from 0
369 * - @parity@ to "even" or "odd" alternately
370 * - @first@ to "true" on the first track and "false" otherwise
371 * - @last@ to "true" on the last track and "false" otherwise
372 */
373static int exp_recent(int attribute((unused)) nargs,
374 const struct mx_node **args,
375 struct sink *output,
71634563 376 void *u) {
9faa7a88
RK
377 struct queue_entry *q;
378 int rc, i;
379
1e97629d
RK
380 dcgi_lookup(DCGI_RECENT);
381 for(q = dcgi_recent, i = 0; q; q = q->next, ++i)
71634563
RK
382 if((rc = mx_expand(mx_rewritel(args[0],
383 "id", q->id,
384 "track", q->track,
385 "index", make_index(i),
386 "parity", i % 2 ? "odd" : "even",
1e97629d 387 "first", q == dcgi_recent ? "true" : "false",
71634563
RK
388 "last", q->next ? "false" : "true",
389 (char *)0),
390 output, u)))
9faa7a88
RK
391 return rc;
392 return 0;
393}
394
395/* @new{TEMPLATE}
396 *
397 * For each track in the newly added list, expands TEMPLATE wit the following
398 * expansions:
399 * - @track@ to the UNQUOTED track name
400 * - @index@ to the track number from 0
401 * - @parity@ to "even" or "odd" alternately
402 * - @first@ to "true" on the first track and "false" otherwise
403 * - @last@ to "true" on the last track and "false" otherwise
404 *
405 * Note that unlike @playing@, @queue@ and @recent@ which are otherwise
406 * superficially similar, there is no @id@ sub-expansion here.
407 */
408static int exp_new(int attribute((unused)) nargs,
409 const struct mx_node **args,
410 struct sink *output,
71634563 411 void *u) {
9faa7a88
RK
412 int rc, i;
413
1e97629d 414 dcgi_lookup(DCGI_NEW);
9faa7a88 415 /* TODO perhaps we should generate an ID value for tracks in the new list */
1e97629d 416 for(i = 0; i < dcgi_nnew; ++i)
71634563 417 if((rc = mx_expand(mx_rewritel(args[0],
1e97629d 418 "track", dcgi_new[i],
71634563
RK
419 "index", make_index(i),
420 "parity", i % 2 ? "odd" : "even",
421 "first", i == 0 ? "true" : "false",
1e97629d 422 "last", i == dcgi_nnew - 1 ? "false" : "true",
71634563
RK
423 (char *)0),
424 output, u)))
9faa7a88
RK
425 return rc;
426 return 0;
427}
428
bca4e2b7 429/* @volume{CHANNEL}
9faa7a88
RK
430 *
431 * Expands to the volume in a given channel. CHANNEL must be "left" or
432 * "right".
433 */
434static int exp_volume(int attribute((unused)) nargs,
435 char **args,
436 struct sink *output,
437 void attribute((unused)) *u) {
1e97629d 438 dcgi_lookup(DCGI_VOLUME);
71634563
RK
439 return sink_printf(output, "%d",
440 !strcmp(args[0], "left")
1e97629d 441 ? dcgi_volume_left : dcgi_volume_right) < 0 ? -1 : 0;
9faa7a88
RK
442}
443
bca4e2b7 444/* @isplaying
9faa7a88
RK
445 *
446 * Expands to "true" if there is a playing track, otherwise "false".
447 */
448static int exp_isplaying(int attribute((unused)) nargs,
449 char attribute((unused)) **args,
450 struct sink *output,
451 void attribute((unused)) *u) {
1e97629d
RK
452 dcgi_lookup(DCGI_PLAYING);
453 return mx_bool_result(output, !!dcgi_playing);
9faa7a88
RK
454}
455
bca4e2b7 456/* @isqueue
9faa7a88
RK
457 *
458 * Expands to "true" if there the queue is nonempty, otherwise "false".
459 */
460static int exp_isqueue(int attribute((unused)) nargs,
461 char attribute((unused)) **args,
462 struct sink *output,
463 void attribute((unused)) *u) {
1e97629d
RK
464 dcgi_lookup(DCGI_QUEUE);
465 return mx_bool_result(output, !!dcgi_queue);
9faa7a88
RK
466}
467
468/* @isrecent@
469 *
470 * Expands to "true" if there the recently played list is nonempty, otherwise
471 * "false".
472 */
473static int exp_isrecent(int attribute((unused)) nargs,
474 char attribute((unused)) **args,
475 struct sink *output,
476 void attribute((unused)) *u) {
1e97629d
RK
477 dcgi_lookup(DCGI_RECENT);
478 return mx_bool_result(output, !!dcgi_recent);
9faa7a88
RK
479}
480
bca4e2b7 481/* @isnew
9faa7a88
RK
482 *
483 * Expands to "true" if there the newly added track list is nonempty, otherwise
484 * "false".
485 */
486static int exp_isnew(int attribute((unused)) nargs,
487 char attribute((unused)) **args,
488 struct sink *output,
489 void attribute((unused)) *u) {
1e97629d
RK
490 dcgi_lookup(DCGI_NEW);
491 return mx_bool_result(output, !!dcgi_nnew);
9faa7a88
RK
492}
493
bca4e2b7 494/* @pref{TRACK}{KEY}
9faa7a88
RK
495 *
496 * Expands to a track preference.
497 */
498static int exp_pref(int attribute((unused)) nargs,
499 char **args,
500 struct sink *output,
501 void attribute((unused)) *u) {
502 char *value;
503
1e97629d 504 if(dcgi_client && !disorder_get(dcgi_client, args[0], args[1], &value))
71634563
RK
505 return sink_writes(output, cgi_sgmlquote(value)) < 0 ? -1 : 0;
506 return 0;
9faa7a88
RK
507}
508
bca4e2b7 509/* @prefs{TRACK}{TEMPLATE}
9faa7a88
RK
510 *
511 * For each track preference of track TRACK, expands TEMPLATE with the
512 * following expansions:
513 * - @name@ to the UNQUOTED preference name
514 * - @index@ to the preference number from 0
515 * - @value@ to the UNQUOTED preference value
516 * - @parity@ to "even" or "odd" alternately
517 * - @first@ to "true" on the first preference and "false" otherwise
518 * - @last@ to "true" on the last preference and "false" otherwise
519 *
520 * Use @quote@ to quote preference names and values where necessary; see below.
521 */
522static int exp_prefs(int attribute((unused)) nargs,
523 const struct mx_node **args,
524 struct sink *output,
71634563 525 void *u) {
9faa7a88
RK
526 int rc, i;
527 struct kvp *k, *head;
528 char *track;
529
530 if((rc = mx_expandstr(args[0], &track, u, "argument #0 (TRACK)")))
531 return rc;
1e97629d 532 if(!dcgi_client || disorder_prefs(dcgi_client, track, &head))
9faa7a88
RK
533 return 0;
534 for(k = head, i = 0; k; k = k->next, ++i)
71634563
RK
535 if((rc = mx_expand(mx_rewritel(args[1],
536 "index", make_index(i),
537 "parity", i % 2 ? "odd" : "even",
538 "name", k->name,
539 "value", k->value,
540 "first", k == head ? "true" : "false",
541 "last", k->next ? "false" : "true",
542 (char *)0),
543 output, u)))
9faa7a88
RK
544 return rc;
545 return 0;
546}
547
bca4e2b7 548/* @transform{TRACK}{TYPE}{CONTEXT}
9faa7a88
RK
549 *
550 * Transforms a track name (if TYPE is "track") or directory name (if type is
551 * "dir"). CONTEXT should be the context, if it is left out then "display" is
552 * assumed.
553 */
554static int exp_transform(int nargs,
555 char **args,
556 struct sink *output,
557 void attribute((unused)) *u) {
558 const char *t = trackname_transform(args[1], args[0],
71634563
RK
559 (nargs > 2 ? args[2] : "display"));
560 return sink_writes(output, cgi_sgmlquote(t)) < 0 ? -1 : 0;
9faa7a88
RK
561}
562
563/* @enabled@
564 *
565 * Expands to "true" if playing is enabled, otherwise "false".
566 */
567static int exp_enabled(int attribute((unused)) nargs,
568 char attribute((unused)) **args,
569 struct sink *output,
570 void attribute((unused)) *u) {
1e97629d 571 int e = 0;
9faa7a88 572
1e97629d
RK
573 if(dcgi_client)
574 disorder_enabled(dcgi_client, &e);
575 return mx_bool_result(output, e);
9faa7a88
RK
576}
577
bca4e2b7 578/* @random-enabled
9faa7a88
RK
579 *
580 * Expands to "true" if random play is enabled, otherwise "false".
581 */
71634563
RK
582static int exp_random_enabled(int attribute((unused)) nargs,
583 char attribute((unused)) **args,
584 struct sink *output,
585 void attribute((unused)) *u) {
1e97629d 586 int e = 0;
9faa7a88 587
1e97629d
RK
588 if(dcgi_client)
589 disorder_random_enabled(dcgi_client, &e);
590 return mx_bool_result(output, e);
9faa7a88
RK
591}
592
04024c2c 593/* @trackstate{TRACK}
9faa7a88
RK
594 *
595 * Expands to "playing" if TRACK is currently playing, or "queue" if it is in
596 * the queue, otherwise to nothing.
597 */
598static int exp_trackstate(int attribute((unused)) nargs,
599 char **args,
600 struct sink *output,
601 void attribute((unused)) *u) {
602 char *track;
603 struct queue_entry *q;
604
1e97629d 605 if(!dcgi_client)
9faa7a88 606 return 0;
1e97629d 607 if(disorder_resolve(dcgi_client, &track, args[0]))
9faa7a88 608 return 0;
1e97629d
RK
609 dcgi_lookup(DCGI_PLAYING);
610 if(dcgi_playing && !strcmp(track, dcgi_playing->track))
71634563 611 return sink_writes(output, "playing") < 0 ? -1 : 0;
1e97629d
RK
612 dcgi_lookup(DCGI_QUEUE);
613 for(q = dcgi_queue; q; q = q->next)
9faa7a88 614 if(!strcmp(track, q->track))
71634563 615 return sink_writes(output, "queued") < 0 ? -1 : 0;
9faa7a88
RK
616 return 0;
617}
618
bca4e2b7 619/* @thisurl
9faa7a88
RK
620 *
621 * Expands to an UNQUOTED URL which points back to the current page. (NB it
622 * might not be byte for byte identical - for instance, CGI arguments might be
623 * re-ordered.)
624 */
625static int exp_thisurl(int attribute((unused)) nargs,
626 char attribute((unused)) **args,
627 struct sink *output,
628 void attribute((unused)) *u) {
71634563 629 return sink_writes(output, cgi_thisurl(config->url)) < 0 ? -1 : 0;
9faa7a88
RK
630}
631
bca4e2b7 632/* @resolve{TRACK}
9faa7a88
RK
633 *
634 * Expands to an UNQUOTED name for the TRACK that is not an alias, or to
635 * nothing if it is not a valid track.
636 */
637static int exp_resolve(int attribute((unused)) nargs,
638 char **args,
639 struct sink *output,
640 void attribute((unused)) *u) {
641 char *r;
642
1e97629d 643 if(dcgi_client && !disorder_resolve(dcgi_client, &r, args[0]))
71634563 644 return sink_writes(output, r) < 0 ? -1 : 0;
9faa7a88
RK
645 return 0;
646}
647
bca4e2b7 648/* @paused
9faa7a88
RK
649 *
650 * Expands to "true" if the playing track is paused, to "false" if it is
651 * playing (or if there is no playing track at all).
652 */
653static int exp_paused(int attribute((unused)) nargs,
654 char attribute((unused)) **args,
655 struct sink *output,
656 void attribute((unused)) *u) {
1e97629d
RK
657 dcgi_lookup(DCGI_PLAYING);
658 return mx_bool_result(output, (dcgi_playing
659 && dcgi_playing->state == playing_paused));
9faa7a88
RK
660}
661
662/* @state{ID}@
663 *
664 * Expands to the current state of track ID.
665 */
666static int exp_state(int attribute((unused)) nargs,
667 char **args,
668 struct sink *output,
669 void attribute((unused)) *u) {
670 struct queue_entry *q = findtrack(args[0]);
671
672 if(q)
71634563 673 return sink_writes(output, playing_states[q->state]) < 0 ? -1 : 0;
9faa7a88
RK
674 return 0;
675}
676
677/* @right{RIGHT}{WITH-RIGHT}{WITHOUT-RIGHT}@
678 *
679 * Expands to WITH-RIGHT if the current user has right RIGHT, otherwise to
680 * WITHOUT-RIGHT. The WITHOUT-RIGHT argument may be left out.
681 *
682 * If both WITH-RIGHT and WITHOUT-RIGHT are left out then expands to "true" if
683 * the user has the right and "false" otherwise.
684 *
685 * If there is no connection to the server then expands to nothing (in all
686 * cases).
687 */
688static int exp_right(int nargs,
689 const struct mx_node **args,
690 struct sink *output,
71634563 691 void *u) {
9faa7a88
RK
692 char *right;
693 rights_type r;
71634563 694 int rc;
9faa7a88 695
1e97629d 696 if(!dcgi_client)
9faa7a88 697 return 0;
1e97629d 698 dcgi_lookup(DCGI_RIGHTS);
71634563 699 if((rc = mx_expandstr(args[0], &right, u, "argument #0 (RIGHT)")))
9faa7a88
RK
700 return rc;
701 if(parse_rights(right, &r, 1/*report*/))
702 return 0;
703 /* Single-argument form */
704 if(nargs == 1)
1e97629d 705 return mx_bool_result(output, !!(r & dcgi_rights));
9faa7a88 706 /* Multiple argument form */
1e97629d 707 if(r & dcgi_rights)
71634563 708 return mx_expand(args[1], output, u);
9faa7a88 709 if(nargs == 3)
71634563 710 return mx_expand(args[2], output, u);
9faa7a88
RK
711 return 0;
712}
713
bca4e2b7 714/* @userinfo{PROPERTY}
9faa7a88
RK
715 *
716 * Expands to the named property of the current user.
717 */
718static int exp_userinfo(int attribute((unused)) nargs,
719 char **args,
720 struct sink *output,
721 void attribute((unused)) *u) {
722 char *v;
723
1e97629d
RK
724 if(dcgi_client
725 && !disorder_userinfo(dcgi_client, disorder_user(dcgi_client),
726 args[0], &v))
71634563 727 return sink_writes(output, v) < 0 ? -1 : 0;
9faa7a88
RK
728 return 0;
729}
730
bca4e2b7
RK
731/* @error
732 *
733 * Expands to the latest error string.
734 */
735static int exp_error(int attribute((unused)) nargs,
736 char attribute((unused)) **args,
737 struct sink *output,
738 void attribute((unused)) *u) {
1e97629d 739 return sink_writes(output, cgi_sgmlquote(dcgi_error_string)) < 0 ? -1 : 0;
bca4e2b7
RK
740}
741
9faa7a88 742/** @brief Register DisOrder-specific expansions */
1e97629d 743void dcgi_expansions(void) {
71634563
RK
744 mx_register("arg", 1, 1, exp_arg);
745 mx_register("enabled", 0, 0, exp_enabled);
746 mx_register("error", 0, 0, exp_error);
747 mx_register("isnew", 0, 0, exp_isnew);
748 mx_register("isplaying", 0, 0, exp_isplaying);
749 mx_register("isplaying", 0, 0, exp_isqueue);
750 mx_register("isrecent", 0, 0, exp_isrecent);
751 mx_register("length", 1, 1, exp_length);
752 mx_register("movable", 1, 1, exp_movable);
753 mx_register("part", 2, 3, exp_part);
754 mx_register("paused", 0, 0, exp_paused);
755 mx_register("pref", 2, 2, exp_pref);
756 mx_register("quote", 1, 1, exp_quote);
757 mx_register("random-enabled", 0, 0, exp_random_enabled);
758 mx_register("removable", 1, 1, exp_removable);
759 mx_register("resolve", 1, 1, exp_resolve);
760 mx_register("server-version", 0, 0, exp_server_version);
761 mx_register("state", 1, 1, exp_state);
762 mx_register("thisurl", 0, 0, exp_thisurl);
763 mx_register("trackstate", 1, 1, exp_trackstate);
764 mx_register("transform", 2, 3, exp_transform);
765 mx_register("url", 0, 0, exp_url);
766 mx_register("user", 0, 0, exp_user);
767 mx_register("userinfo", 1, 1, exp_userinfo);
768 mx_register("version", 0, 0, exp_version);
769 mx_register("volume", 1, 1, exp_volume);
770 mx_register("when", 1, 1, exp_when);
771 mx_register("who", 1, 1, exp_who);
772 mx_register_magic("new", 1, 1, exp_new);
773 mx_register_magic("playing", 0, 1, exp_playing);
774 mx_register_magic("prefs", 2, 2, exp_prefs);
775 mx_register_magic("queue", 1, 1, exp_queue);
776 mx_register_magic("recent", 1, 1, exp_recent);
777 mx_register_magic("right", 1, 3, exp_right);
bca4e2b7
RK
778}
779
9faa7a88
RK
780/*
781Local Variables:
782c-basic-offset:2
783comment-column:40
784fill-column:79
785indent-tabs-mode:nil
786End:
787*/