chiark / gitweb /
Merge branch 'mdw/gstdecode'
[disorder] / scripts / protocol
CommitLineData
200adb00
RK
1#! /usr/bin/perl -w
2#
3# This file is part of DisOrder.
b0116b5c 4# Copyright (C) 2010-11, 13 Richard Kettlewell
200adb00
RK
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <http://www.gnu.org/licenses/>.
18#
19use strict;
20
ec9c0462
RK
21# This file contains the definition of the disorder protocol, plus
22# code to generates stubs for it in the various supported languages.
23#
24# At the time of writing it is a work in progress!
25
26#
27# Types:
28#
29# string A (Unicode) string.
c12575c6 30# string-raw A string that is not subject to de-quoting (return only)
ec9c0462 31# integer An integer. Decimal on the wire.
4d80373d 32# time A timestamp. Decimal on the wire.
ec9c0462
RK
33# boolean True or false. "yes" or "no" on the wire.
34# list In commands: a list of strings in the command.
35# In returns: a list of lines in the response.
5dc19ffd 36# pair-list In returns: a list of key-value pairs in a response body.
ec9c0462
RK
37# body In commands: a list of strings as a command body.
38# In returns: a list of strings as a response body.
39# queue In returns: a list of queue entries in a response body.
40# queue-one In returns: a queue entry in the response.
4d80373d 41# literal Constant string sent in sequence
ec9c0462
RK
42#
43
200adb00
RK
44# Variables and utilities -----------------------------------------------------
45
46our @h = ();
47our @c = ();
ca7f5bb3
RK
48our @ah = ();
49our @ac = ();
50our @missing = ();
51
52# Mapping of return type sequences to eclient callbacks
53our @eclient_return = (
54 ["no_response" => []],
55 ["string_response" => ["string"]],
56 ["string_response" => ["string-raw"]],
57 ["integer_response" => ["integer"]],
58 ["integer_response" => ["boolean"]],
59 ["time_response" => ["time"]],
60 ["pair_integer_response" => ["integer", "integer"]],
61 ["queue_response" => ["queue"]],
ad131c25 62 ["playing_response" => ["queue-one"]],
ca7f5bb3
RK
63 ["list_response" => ["body"]],
64 );
65
66# eclient_response_matces(RETURNS, VARIANT)
67#
68# Return true if VARIANT matches RETURNS
69sub eclient_response_matches {
70 my $returns = shift;
71 my $variant = shift;
72 my $types = $variant->[1];
73 if(scalar @$returns != scalar @$types) { return 0; }
74 for my $n (0 .. $#$returns) {
75 my $return = $returns->[$n];
76 my $type = $return->[0];
77 if($type ne $types->[$n]) { return 0; }
78 }
79 return 1;
80}
81
82# find_eclient_type(RETURNS)
83#
84# Find the result type for an eclient call
85sub find_eclient_response {
86 my $returns = shift;
87 if(!defined $returns) {
88 $returns = [];
89 }
90 for my $variant (@eclient_return) {
91 if(eclient_response_matches($returns, $variant)) {
92 return $variant->[0];
93 }
94 }
95 return undef;
96}
200adb00 97
ea9f5de5
RK
98# Write(PATH, LINES)
99#
100# Write array ref LINES to file PATH.
200adb00
RK
101sub Write {
102 my $path = shift;
103 my $lines = shift;
104
105 (open(F, ">$path")
106 and print F @$lines
107 and close F)
7788b7c7 108 or die "$0: $path: $!\n";
200adb00
RK
109}
110
111# Command classes -------------------------------------------------------------
112
ea9f5de5
RK
113# c_in_decl([TYPE, NAME])
114#
115# Return the C declaration for an input parameter of type TYPE with
116# name NAME.
50d905eb
RK
117sub c_in_decl {
118 my $arg = shift;
119
120 my $type = $arg->[0];
121 my $name = $arg->[1];
122 if($type eq 'string') {
123 return "const char *$name";
124 } elsif($type eq 'integer') {
125 return "long $name";
4d80373d
RK
126 } elsif($type eq 'time') {
127 return "time_t $name";
0bc1d67c 128 } elsif($type eq 'list' or $type eq 'body') {
08af2413
RK
129 return ("char **$name",
130 "int n$name");
4d80373d
RK
131 } elsif($type eq 'literal') {
132 return ();
50d905eb 133 } else {
ec9c0462 134 die "$0: c_in_decl: unknown type '$type'\n";
50d905eb
RK
135 }
136}
137
ea9f5de5
RK
138# c_out_decl([TYPE, NAME])
139#
140# Return the C declaration for an output (reference) parameter of type
141# TYPE with name NAME.
50d905eb
RK
142sub c_out_decl {
143 my $arg = shift;
144
830d5c43 145 return () unless defined $arg;
50d905eb
RK
146 my $type = $arg->[0];
147 my $name = $arg->[1];
c12575c6 148 if($type eq 'string' or $type eq 'string-raw') {
830d5c43 149 return ("char **${name}p");
50d905eb 150 } elsif($type eq 'integer') {
830d5c43 151 return ("long *${name}p");
4d80373d
RK
152 } elsif($type eq 'time') {
153 return ("time_t *${name}p");
830d5c43
RK
154 } elsif($type eq 'boolean') {
155 return ("int *${name}p");
ec9c0462 156 } elsif($type eq 'list' or $type eq 'body') {
830d5c43
RK
157 return ("char ***${name}p",
158 "int *n${name}p");
5dc19ffd
RK
159 } elsif($type eq 'pair-list') {
160 return ("struct kvp **${name}p");
ec9c0462 161 } elsif($type eq 'queue' or $type eq 'queue-one') {
08af2413 162 return ("struct queue_entry **${name}p");
f4522fa7
RK
163 } elsif($type eq 'user') {
164 return ();
50d905eb 165 } else {
ec9c0462 166 die "$0: c_out_decl: unknown type '$type'\n";
50d905eb
RK
167 }
168}
169
ea9f5de5
RK
170# c_param_docs([TYPE, NAME})
171#
172# Return the doc string for a C input parameter.
50d905eb
RK
173sub c_param_docs {
174 my $args = shift;
08af2413
RK
175 my @d = ();
176 for my $arg (@$args) {
4d80373d
RK
177 my $type = $arg->[0];
178 my $name = $arg->[1];
179 my $description = $arg->[2];
180 if($type eq 'body' or $type eq 'list') {
08af2413 181 push(@d,
4d80373d
RK
182 " * \@param $name $description\n",
183 " * \@param n$name Length of $name\n");
184 } elsif($type ne 'literal') {
185 push(@d, " * \@param $name $description\n");
08af2413
RK
186 }
187 }
188 return @d;
50d905eb
RK
189}
190
ea9f5de5
RK
191# c_param_docs([TYPE, NAME})
192#
193# Return the doc string for a C output parameter.
830d5c43 194sub c_return_docs {
c12575c6
RK
195 my $returns = shift;
196 return () unless defined $returns;
1f60835b 197 my @docs = ();
c12575c6
RK
198 for my $return (@$returns) {
199 my $type = $return->[0];
200 my $name = $return->[1];
201 my $descr = $return->[2];
202 if($type eq 'string'
203 or $type eq 'string-raw'
204 or $type eq 'integer'
4d80373d 205 or $type eq 'time'
c12575c6 206 or $type eq 'boolean') {
1f60835b
RK
207 push(@docs,
208 " * \@param ${name}p $descr\n");
c12575c6 209 } elsif($type eq 'list' or $type eq 'body') {
1f60835b
RK
210 push(@docs,
211 " * \@param ${name}p $descr\n",
212 " * \@param n${name}p Number of elements in ${name}p\n");
c12575c6 213 } elsif($type eq 'pair-list') {
1f60835b
RK
214 push(@docs,
215 " * \@param ${name}p $descr\n");
c12575c6 216 } elsif($type eq 'queue' or $type eq 'queue-one') {
1f60835b
RK
217 push(@docs,
218 " * \@param ${name}p $descr\n");
c12575c6 219 } elsif($type eq 'user') {
1f60835b 220 # nothing
c12575c6
RK
221 } else {
222 die "$0: c_return_docs: unknown type '$type'\n";
223 }
830d5c43 224 }
1f60835b 225 return @docs;
7788b7c7
RK
226}
227
830d5c43
RK
228# simple(CMD, SUMMARY, DETAIL,
229# [[TYPE,NAME,DESCR], [TYPE,NAME,DESCR], ...],
c12575c6 230# [[RETURN-TYPE, RETURN-NAME, RETURN_DESCR]])
ff75e16e
RK
231#
232# CMD is normally just the name of the command, but can
233# be [COMMAND,FUNCTION] if the function name should differ
234# from the protocol command.
830d5c43 235sub simple {
7788b7c7
RK
236 my $cmd = shift;
237 my $summary = shift;
238 my $detail = shift;
239 my $args = shift;
c12575c6 240 my $returns = shift;
7788b7c7 241
ff75e16e
RK
242 my $cmdc;
243 if(ref $cmd eq 'ARRAY') {
244 $cmdc = $$cmd[1];
245 $cmd = $$cmd[0];
246 } else {
247 $cmdc = $cmd;
248 $cmdc =~ s/-/_/g;
249 }
ec9c0462 250 print STDERR "Processing $cmd... ";
bcb2af72 251 # C argument types
816fbc19 252 my @cargs = ();
816fbc19
RK
253 for my $arg (@$args) {
254 if($arg->[0] eq 'body' or $arg->[0] eq 'list') {
ad131c25 255 push(@cargs, "disorder__$arg->[0]", $arg->[1], "n$arg->[1]");
816fbc19
RK
256 } elsif($arg->[0] eq 'string') {
257 push(@cargs, $arg->[1]);
bcb2af72
RK
258 } elsif($arg->[0] eq 'integer'
259 or $arg->[0] eq 'time') {
260 push(@cargs, "disorder__$arg->[0]", "$arg->[1]");
816fbc19
RK
261 } elsif($arg->[0] eq 'literal') {
262 push(@cargs, "\"$arg->[1]\"");
263 } else {
264 die "$0: unsupported arg type '$arg->[0]' for '$cmd'\n";
265 }
266 }
7788b7c7 267 # Synchronous C API
ec9c0462 268 print STDERR "H ";
7788b7c7
RK
269 push(@h, "/** \@brief $summary\n",
270 " *\n",
271 " * $detail\n",
272 " *\n",
08af2413 273 " * \@param c Client\n",
830d5c43 274 c_param_docs($args),
c12575c6 275 c_return_docs($returns),
7788b7c7
RK
276 " * \@return 0 on success, non-0 on error\n",
277 " */\n",
50d905eb
RK
278 "int disorder_$cmdc(",
279 join(", ", "disorder_client *c",
280 map(c_in_decl($_), @$args),
c12575c6 281 map(c_out_decl($_), @$returns)),
830d5c43 282 ");\n\n");
ec9c0462 283 print STDERR "C ";
50d905eb
RK
284 push(@c, "int disorder_$cmdc(",
285 join(", ", "disorder_client *c",
286 map(c_in_decl($_), @$args),
c12575c6 287 map(c_out_decl($_), @$returns)),
bcb2af72 288 ") {\n");
c12575c6
RK
289 if(!defined $returns or scalar @$returns == 0) {
290 # Simple case
08af2413 291 push(@c, " return disorder_simple(",
c12575c6 292 join(", ", "c", "NULL", "\"$cmd\"", @cargs, "(char *)NULL"),
08af2413 293 ");\n");
c12575c6
RK
294 } elsif(scalar @$returns == 1
295 and $returns->[0]->[0] eq 'queue-one') {
296 # Special case
297 my $return = $$returns[0];
ec9c0462 298 push(@c, " return onequeue(c, \"$cmd\", $return->[1]p);\n");
c12575c6
RK
299 } elsif(scalar @$returns == 1
300 and $returns->[0]->[0] eq 'string-raw') {
301 # Special case
302 my $return = $$returns[0];
303 push(@c, " return disorder_simple(",
304 join(", ", "c", "$return->[1]p", "\"$cmd\"", @cargs, "(char *)NULL"),
305 ");\n");
306 } elsif(scalar @$returns == 1
307 and $returns->[0]->[0] eq 'pair-list') {
308 # Special case
309 my $return = $$returns[0];
eff6238f
RK
310 push(@c, " return pairlist(",
311 join(", ", "c", "$return->[1]p", "\"$cmd\"",
312 @cargs,
c12575c6 313 "(char *)NULL"),
eff6238f 314 ");\n");
830d5c43 315 } else {
dab87ecc 316 my $expected = 0;
c12575c6
RK
317 for(my $n = 0; $n < scalar @$returns; ++$n) {
318 my $return = $returns->[$n];
319 my $type = $return->[0];
320 my $name = $return->[1];
321 if($type eq 'string'
322 or $type eq 'boolean'
323 or $type eq 'integer'
4d80373d 324 or $type eq 'time'
c12575c6 325 or $type eq 'user') {
dab87ecc 326 ++$expected;
c12575c6
RK
327 }
328 }
dab87ecc
RK
329 if($expected) {
330 push(@c, " char **v;\n",
331 " int nv, rc = disorder_simple_split(",
332 join(", ",
333 "c",
334 "&v",
335 "&nv",
336 $expected,
337 "\"$cmd\"",
338 @cargs,
339 "(char *)NULL"),
340 ");\n",
341 " if(rc)\n",
342 " return rc;\n");
343 } else {
344 push(@c,
345 " int rc = disorder_simple(",
346 join(", ",
347 "c",
348 "NULL",
349 "\"$cmd\"",
350 @cargs,
351 "(char *)NULL"),
352 ");\n",
353 " if(rc)\n",
354 " return rc;\n");
355 }
c12575c6
RK
356 for(my $n = 0; $n < scalar @$returns; ++$n) {
357 my $return = $returns->[$n];
358 my $type = $return->[0];
359 my $name = $return->[1];
360 if($type eq 'string') {
361 push(@c,
e721e6b9
RK
362 " *${name}p = v[$n];\n",
363 " v[$n] = NULL;\n");
c12575c6
RK
364 } elsif($type eq 'boolean') {
365 push(@c,
366 " if(boolean(\"$cmd\", v[$n], ${name}p))\n",
367 " return -1;\n");
368 } elsif($type eq 'integer') {
369 push(@c,
370 " *${name}p = atol(v[$n]);\n");
4d80373d
RK
371 } elsif($type eq 'time') {
372 push(@c,
373 " *${name}p = atoll(v[$n]);\n");
c12575c6
RK
374 } elsif($type eq 'user') {
375 push(@c,
e721e6b9
RK
376 " c->user = v[$n];\n",
377 " v[$n] = NULL;\n");
c12575c6
RK
378 } elsif($type eq 'body') {
379 push(@c,
380 " if(readlist(c, ${name}p, n${name}p))\n",
381 " return -1;\n");
382 } elsif($type eq 'queue') {
383 push(@c,
384 " if(readqueue(c, ${name}p))\n",
385 " return -1;\n");
386 } else {
387 die "$0: C API: unknown return type '$type' for '$name'\n";
388 }
389 }
e721e6b9
RK
390 if($expected) {
391 push(@c,
392 " free_strings(nv, v);\n");
393 }
c12575c6 394 push(@c, " return 0;\n");
830d5c43
RK
395 }
396 push(@c, "}\n\n");
7788b7c7
RK
397
398 # Asynchronous C API
ca7f5bb3
RK
399 my $variant = find_eclient_response($returns);
400 if(defined $variant) {
401 print STDERR "AH ";
402 push(@ah,
403 "/** \@brief $summary\n",
404 " *\n",
405 " * $detail\n",
406 " *\n",
407 " * \@param c Client\n",
ca7f5bb3 408 " * \@param completed Called upon completion\n",
ad131c25
RK
409 c_param_docs($args),
410 " * \@param v Passed to \@p completed\n",
ca7f5bb3
RK
411 " * \@return 0 if the command was queued successfuly, non-0 on error\n",
412 " */\n",
413 "int disorder_eclient_$cmdc(",
414 join(", ", "disorder_eclient *c",
ad131c25 415 "disorder_eclient_$variant *completed",
ca7f5bb3 416 map(c_in_decl($_), @$args),
ad131c25 417 "void *v"),
ca7f5bb3
RK
418 ");\n\n");
419
ad131c25
RK
420 print STDERR "AC ";
421 push(@ac,
422 "int disorder_eclient_$cmdc(",
423 join(", ", "disorder_eclient *c",
424 "disorder_eclient_$variant *completed",
425 map(c_in_decl($_), @$args),
426 "void *v"),
bcb2af72 427 ") {\n");
ad131c25
RK
428 push(@ac, " return simple(",
429 join(", ",
430 "c",
431 "${variant}_opcallback",
432 "(void (*)())completed",
433 "v",
434 "\"$cmd\"",
435 @cargs,
436 "(char *)0"),
437 ");\n");
438 push(@ac, "}\n\n");
ca7f5bb3
RK
439 } else {
440 push(@missing, "disorder_eclient_$cmdc");
441 }
7788b7c7
RK
442
443 # Python API
444 # TODO
445
446 # Java API
447 # TODO
ec9c0462 448 print STDERR "\n";
7788b7c7
RK
449}
450
200adb00
RK
451# TODO other command classes
452
453# Front matter ----------------------------------------------------------------
454
ff75e16e
RK
455our @generated = ("/*\n",
456 " * Automatically generated file, see scripts/protocol\n",
457 " *\n",
458 " * DO NOT EDIT.\n",
459 " */\n");
460
200adb00 461our @gpl = ("/*\n",
7788b7c7 462 " * This file is part of DisOrder.\n",
ff75e16e 463 " * Copyright (C) 2010-11 Richard Kettlewell\n",
7788b7c7
RK
464 " *\n",
465 " * This program is free software: you can redistribute it and/or modify\n",
466 " * it under the terms of the GNU General Public License as published by\n",
467 " * the Free Software Foundation, either version 3 of the License, or\n",
468 " * (at your option) any later version.\n",
469 " *\n",
470 " * This program is distributed in the hope that it will be useful,\n",
471 " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n",
472 " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n",
473 " * GNU General Public License for more details.\n",
474 " *\n",
475 " * You should have received a copy of the GNU General Public License\n",
476 " * along with this program. If not, see <http://www.gnu.org/licenses/>.\n",
477 " */\n");
200adb00
RK
478
479
ff75e16e 480push(@h, @generated, @gpl,
200adb00
RK
481 "#ifndef CLIENT_STUBS_H\n",
482 "#define CLIENT_STUBS_H\n",
d0f01ef6
RK
483 "/** \@file lib/client-stubs.h\n",
484 " * \@brief Generated client API\n",
485 " *\n",
486 " * Don't include this file directly - use \@ref lib/client.h instead.\n",
487 " */\n",
200adb00
RK
488 "\n");
489
ff75e16e 490push(@c, @generated, @gpl,
d0f01ef6
RK
491 "/** \@file lib/client-stubs.c\n",
492 " * \@brief Generated client API implementation\n",
493 " */\n",
200adb00
RK
494 "\n");
495
ca7f5bb3
RK
496push(@ah, @generated, @gpl,
497 "#ifndef ECLIENT_STUBS_H\n",
498 "#define ECLIENT_STUBS_H\n",
d0f01ef6
RK
499 "/** \@file lib/client-stubs.h\n",
500 " * \@brief Generated asynchronous client API\n",
501 " *\n",
502 " * Don't include this file directly - use \@ref lib/eclient.h instead.\n",
503 " */\n",
ca7f5bb3
RK
504 "\n");
505
506push(@ac, @generated, @gpl,
d0f01ef6
RK
507 "/** \@file lib/client-stubs.c\n",
508 " * \@brief Generated asynchronous client API implementation\n",
509 " */\n",
ca7f5bb3
RK
510 "\n");
511
200adb00
RK
512# The protocol ----------------------------------------------------------------
513
96b1cf08
RK
514simple("adopt",
515 "Adopt a track",
516 "Makes the calling user owner of a randomly picked track.",
50d905eb 517 [["string", "id", "Track ID"]]);
200adb00 518
96b1cf08
RK
519simple("adduser",
520 "Create a user",
521 "Create a new user. Requires the 'admin' right. Email addresses etc must be filled in in separate commands.",
50d905eb
RK
522 [["string", "user", "New username"],
523 ["string", "password", "Initial password"],
524 ["string", "rights", "Initial rights (optional)"]]);
200adb00 525
830d5c43
RK
526simple("allfiles",
527 "List files and directories in a directory",
528 "See 'files' and 'dirs' for more specific lists.",
529 [["string", "dir", "Directory to list (optional)"],
530 ["string", "re", "Regexp that results must match (optional)"]],
c12575c6 531 [["body", "files", "List of matching files and directories"]]);
200adb00 532
f4522fa7
RK
533simple("confirm",
534 "Confirm registration",
535 "The confirmation string must have been created with 'register'. The username is returned so the caller knows who they are.",
536 [["string", "confirmation", "Confirmation string"]],
c12575c6 537 [["user"]]);
f4522fa7
RK
538
539simple("cookie",
540 "Log in with a cookie",
541 "The cookie must have been created with 'make-cookie'. The username is returned so the caller knows who they are.",
542 [["string", "cookie", "Cookie string"]],
c12575c6 543 [["user"]]);
200adb00 544
96b1cf08
RK
545simple("deluser",
546 "Delete user",
547 "Requires the 'admin' right.",
50d905eb 548 [["string", "user", "User to delete"]]);
200adb00 549
830d5c43
RK
550simple("dirs",
551 "List directories in a directory",
552 "",
553 [["string", "dir", "Directory to list (optional)"],
554 ["string", "re", "Regexp that results must match (optional)"]],
c12575c6 555 [["body", "files", "List of matching directories"]]);
200adb00 556
96b1cf08
RK
557simple("disable",
558 "Disable play",
559 "Play will stop at the end of the current track, if one is playing. Requires the 'global prefs' right.",
560 []);
561
562simple("edituser",
563 "Set a user property",
564 "With the 'admin' right you can do anything. Otherwise you need the 'userinfo' right and can only set 'email' and 'password'.",
50d905eb
RK
565 [["string", "username", "User to modify"],
566 ["string", "property", "Property name"],
567 ["string", "value", "New property value"]]);
96b1cf08
RK
568
569simple("enable",
570 "Enable play",
571 "Requires the 'global prefs' right.",
572 []);
573
830d5c43
RK
574simple("enabled",
575 "Detect whether play is enabled",
576 "",
577 [],
c12575c6 578 [["boolean", "enabled", "1 if play is enabled and 0 otherwise"]]);
830d5c43
RK
579
580simple("exists",
581 "Test whether a track exists",
582 "",
583 [["string", "track", "Track name"]],
c12575c6 584 [["boolean", "exists", "1 if the track exists and 0 otherwise"]]);
830d5c43
RK
585
586simple("files",
587 "List files in a directory",
588 "",
589 [["string", "dir", "Directory to list (optional)"],
590 ["string", "re", "Regexp that results must match (optional)"]],
c12575c6 591 [["body", "files", "List of matching files"]]);
830d5c43
RK
592
593simple("get",
7788b7c7
RK
594 "Get a track preference",
595 "If the track does not exist that is an error. If the track exists but the preference does not then a null value is returned.",
50d905eb
RK
596 [["string", "track", "Track name"],
597 ["string", "pref", "Preference name"]],
c12575c6 598 [["string", "value", "Preference value"]]);
200adb00 599
830d5c43 600simple("get-global",
7788b7c7
RK
601 "Get a global preference",
602 "If the preference does exist not then a null value is returned.",
50d905eb 603 [["string", "pref", "Global preference name"]],
c12575c6 604 [["string", "value", "Preference value"]]);
200adb00 605
830d5c43
RK
606simple("length",
607 "Get a track's length",
608 "If the track does not exist an error is returned.",
609 [["string", "track", "Track name"]],
c12575c6 610 [["integer", "length", "Track length in seconds"]]);
200adb00
RK
611
612# TODO log
613
830d5c43 614simple("make-cookie",
7788b7c7
RK
615 "Create a login cookie for this user",
616 "The cookie may be redeemed via the 'cookie' command",
617 [],
c12575c6 618 [["string", "cookie", "Newly created cookie"]]);
200adb00 619
0bc1d67c
RK
620simple("move",
621 "Move a track",
622 "Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.",
623 [["string", "track", "Track ID or name"],
624 ["integer", "delta", "How far to move the track towards the head of the queue"]]);
200adb00 625
0bc1d67c
RK
626simple("moveafter",
627 "Move multiple tracks",
628 "Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.",
629 [["string", "target", "Move after this track, or to head if \"\""],
630 ["list", "ids", "List of tracks to move by ID"]]);
200adb00 631
ff75e16e
RK
632simple(["new", "new_tracks"],
633 "List recently added tracks",
634 "",
635 [["integer", "max", "Maximum tracks to fetch, or 0 for all available"]],
c12575c6 636 [["body", "tracks", "Recently added tracks"]]);
200adb00 637
96b1cf08
RK
638simple("nop",
639 "Do nothing",
640 "Used as a keepalive. No authentication required.",
641 []);
200adb00 642
830d5c43 643simple("part",
7788b7c7
RK
644 "Get a track name part",
645 "If the name part cannot be constructed an empty string is returned.",
50d905eb
RK
646 [["string", "track", "Track name"],
647 ["string", "context", "Context (\"sort\" or \"display\")"],
648 ["string", "part", "Name part (\"artist\", \"album\" or \"title\")"]],
c12575c6 649 [["string", "part", "Value of name part"]]);
200adb00 650
96b1cf08
RK
651simple("pause",
652 "Pause the currently playing track",
653 "Requires the 'pause' right.",
654 []);
200adb00 655
830d5c43 656simple("play",
00861dcb
RK
657 "Play a track",
658 "Requires the 'play' right.",
50d905eb 659 [["string", "track", "Track to play"]],
c12575c6 660 [["string-raw", "id", "Queue ID of new track"]]);
00861dcb 661
0bc1d67c
RK
662simple("playafter",
663 "Play multiple tracks",
664 "Requires the 'play' right.",
665 [["string", "target", "Insert into queue after this track, or at head if \"\""],
666 ["list", "tracks", "List of track names to play"]]);
200adb00 667
ec9c0462
RK
668simple("playing",
669 "Retrieve the playing track",
670 "",
671 [],
c12575c6 672 [["queue-one", "playing", "Details of the playing track"]]);
200adb00 673
96b1cf08
RK
674simple("playlist-delete",
675 "Delete a playlist",
676 "Requires the 'play' right and permission to modify the playlist.",
50d905eb 677 [["string", "playlist", "Playlist to delete"]]);
200adb00 678
830d5c43
RK
679simple("playlist-get",
680 "List the contents of a playlist",
681 "Requires the 'read' right and oermission to read the playlist.",
682 [["string", "playlist", "Playlist name"]],
c12575c6 683 [["body", "tracks", "List of tracks in playlist"]]);
200adb00 684
830d5c43 685simple("playlist-get-share",
7788b7c7
RK
686 "Get a playlist's sharing status",
687 "Requires the 'read' right and permission to read the playlist.",
50d905eb 688 [["string", "playlist", "Playlist to read"]],
c12575c6 689 [["string-raw", "share", "Sharing status (\"public\", \"private\" or \"shared\")"]]);
200adb00 690
3680ef53
RK
691simple("playlist-lock",
692 "Lock a playlist",
693 "Requires the 'play' right and permission to modify the playlist. A given connection may lock at most one playlist.",
50d905eb 694 [["string", "playlist", "Playlist to delete"]]);
3680ef53 695
08af2413
RK
696simple("playlist-set",
697 "Set the contents of a playlist",
698 "Requires the 'play' right and permission to modify the playlist, which must be locked.",
699 [["string", "playlist", "Playlist to modify"],
0bc1d67c 700 ["body", "tracks", "New list of tracks for playlist"]]);
08af2413 701
96b1cf08
RK
702simple("playlist-set-share",
703 "Set a playlist's sharing status",
7788b7c7 704 "Requires the 'play' right and permission to modify the playlist.",
50d905eb
RK
705 [["string", "playlist", "Playlist to modify"],
706 ["string", "share", "New sharing status (\"public\", \"private\" or \"shared\")"]]);
200adb00 707
96b1cf08
RK
708simple("playlist-unlock",
709 "Unlock the locked playlist playlist",
710 "The playlist to unlock is implicit in the connection.",
711 []);
200adb00 712
830d5c43
RK
713simple("playlists",
714 "List playlists",
715 "Requires the 'read' right. Only playlists that you have permission to read are returned.",
716 [],
c12575c6 717 [["body", "playlists", "Playlist names"]]);
200adb00 718
5dc19ffd
RK
719simple("prefs",
720 "Get all the preferences for a track",
721 "",
722 [["string", "track", "Track name"]],
c12575c6 723 [["pair-list", "prefs", "Track preferences"]]);
200adb00 724
08af2413
RK
725simple("queue",
726 "List the queue",
727 "",
728 [],
c12575c6 729 [["queue", "queue", "Current queue contents"]]);
200adb00 730
96b1cf08
RK
731simple("random-disable",
732 "Disable random play",
733 "Requires the 'global prefs' right.",
734 []);
735
736simple("random-enable",
737 "Enable random play",
738 "Requires the 'global prefs' right.",
739 []);
200adb00 740
830d5c43
RK
741simple("random-enabled",
742 "Detect whether random play is enabled",
743 "Random play counts as enabled even if play is disabled.",
744 [],
c12575c6 745 [["boolean", "enabled", "1 if random play is enabled and 0 otherwise"]]);
200adb00 746
08af2413
RK
747simple("recent",
748 "List recently played tracks",
749 "",
750 [],
c12575c6 751 [["queue", "recent", "Recently played tracks"]]);
200adb00 752
96b1cf08
RK
753simple("reconfigure",
754 "Re-read configuraiton file.",
755 "Requires the 'admin' right.",
756 []);
200adb00 757
830d5c43 758simple("register",
7788b7c7
RK
759 "Register a new user",
760 "Requires the 'register' right which is usually only available to the 'guest' user. Redeem the confirmation string via 'confirm' to complete registration.",
50d905eb
RK
761 [["string", "username", "Requested new username"],
762 ["string", "password", "Requested initial password"],
763 ["string", "email", "New user's email address"]],
c12575c6 764 [["string", "confirmation", "Confirmation string"]]);
200adb00 765
96b1cf08
RK
766simple("reminder",
767 "Send a password reminder.",
768 "If the user has no valid email address, or no password, or a reminder has been sent too recently, then no reminder will be sent.",
50d905eb 769 [["string", "username", "User to remind"]]);
200adb00 770
96b1cf08
RK
771simple("remove",
772 "Remove a track form the queue.",
773 "Requires one of the 'remove mine', 'remove random' or 'remove any' rights depending on how the track came to be added to the queue.",
50d905eb 774 [["string", "id", "Track ID"]]);
200adb00 775
96b1cf08
RK
776simple("rescan",
777 "Rescan all collections for new or obsolete tracks.",
778 "Requires the 'rescan' right.",
7788b7c7 779 []); # TODO wait/fresh flags
200adb00 780
830d5c43 781simple("resolve",
7788b7c7
RK
782 "Resolve a track name",
783 "Converts aliases to non-alias track names",
50d905eb 784 [["string", "track", "Track name (might be an alias)"]],
c12575c6 785 [["string", "resolved", "Resolve track name (definitely not an alias)"]]);
200adb00 786
96b1cf08
RK
787simple("resume",
788 "Resume the currently playing track",
789 "Requires the 'pause' right.",
790 []);
200adb00 791
96b1cf08
RK
792simple("revoke",
793 "Revoke a cookie.",
794 "It will not subsequently be possible to log in with the cookie.",
08af2413 795 []);
200adb00 796
c12575c6
RK
797simple("rtp-address",
798 "Get the server's RTP address information",
799 "",
800 [],
801 [["string", "address", "Where to store hostname or address"],
802 ["string", "port", "Where to store service name or port number"]]);
200adb00 803
b0116b5c
RK
804simple("rtp-cancel",
805 "Cancel RTP stream",
806 "",
807 []);
808
809simple("rtp-request",
810 "Request a unicast RTP stream",
811 "",
812 [["string", "address", "Destination address"],
813 ["string", "port", "Destination port number"]]);
814
96b1cf08
RK
815simple("scratch",
816 "Terminate the playing track.",
817 "Requires one of the 'scratch mine', 'scratch random' or 'scratch any' rights depending on how the track came to be added to the queue.",
50d905eb 818 [["string", "id", "Track ID (optional)"]]);
200adb00 819
4d80373d
RK
820simple(["schedule-add", "schedule_add_play"],
821 "Schedule a track to play in the future",
822 "",
823 [["time", "when", "When to play the track"],
824 ["string", "priority", "Event priority (\"normal\" or \"junk\")"],
825 ["literal", "play", ""],
826 ["string", "track", "Track to play"]]);
827
828simple(["schedule-add", "schedule_add_set_global"],
829 "Schedule a global setting to be changed in the future",
830 "",
831 [["time", "when", "When to change the setting"],
832 ["string", "priority", "Event priority (\"normal\" or \"junk\")"],
833 ["literal", "set-global", ""],
834 ["string", "pref", "Global preference to set"],
835 ["string", "value", "New value of global preference"]]);
836
837simple(["schedule-add", "schedule_add_unset_global"],
838 "Schedule a global setting to be unset in the future",
839 "",
840 [["time", "when", "When to change the setting"],
841 ["string", "priority", "Event priority (\"normal\" or \"junk\")"],
842 ["literal", "set-global", ""],
843 ["string", "pref", "Global preference to set"]]);
200adb00 844
96b1cf08
RK
845simple("schedule-del",
846 "Delete a scheduled event.",
847 "Users can always delete their own scheduled events; with the admin right you can delete any event.",
50d905eb 848 [["string", "event", "ID of event to delete"]]);
200adb00 849
5dc19ffd
RK
850simple("schedule-get",
851 "Get the details of scheduled event",
852 "",
853 [["string", "id", "Event ID"]],
c12575c6 854 [["pair-list", "actiondata", "Details of event"]]);
200adb00 855
830d5c43
RK
856simple("schedule-list",
857 "List scheduled events",
858 "This just lists IDs. Use 'schedule-get' to retrieve more detail",
859 [],
c12575c6 860 [["body", "ids", "List of event IDs"]]);
200adb00 861
830d5c43
RK
862simple("search",
863 "Search for tracks",
864 "Terms are either keywords or tags formatted as 'tag:TAG-NAME'.",
865 [["string", "terms", "List of search terms"]],
c12575c6 866 [["body", "tracks", "List of matching tracks"]]);
200adb00 867
96b1cf08
RK
868simple("set",
869 "Set a track preference",
870 "Requires the 'prefs' right.",
50d905eb
RK
871 [["string", "track", "Track name"],
872 ["string", "pref", "Preference name"],
873 ["string", "value", "New value"]]);
200adb00 874
96b1cf08
RK
875simple("set-global",
876 "Set a global preference",
877 "Requires the 'global prefs' right.",
50d905eb
RK
878 [["string", "pref", "Preference name"],
879 ["string", "value", "New value"]]);
200adb00 880
eea34c08
RK
881simple("shutdown",
882 "Request server shutdown",
883 "Requires the 'admin' right.",
884 []);
7788b7c7 885
830d5c43
RK
886simple("stats",
887 "Get server statistics",
c12575c6 888 "The details of what the server reports are not really defined. The returned strings are intended to be printed out one to a line.",
830d5c43 889 [],
c12575c6 890 [["body", "stats", "List of server information strings."]]);
200adb00 891
830d5c43
RK
892simple("tags",
893 "Get a list of known tags",
894 "Only tags which apply to at least one track are returned.",
895 [],
c12575c6 896 [["body", "tags", "List of tags"]]);
200adb00 897
96b1cf08
RK
898simple("unset",
899 "Unset a track preference",
900 "Requires the 'prefs' right.",
50d905eb
RK
901 [["string", "track", "Track name"],
902 ["string", "pref", "Preference name"]]);
200adb00 903
96b1cf08
RK
904simple("unset-global",
905 "Set a global preference",
906 "Requires the 'global prefs' right.",
50d905eb 907 [["string", "pref", "Preference name"]]);
200adb00 908
50d905eb 909# 'user' only used for authentication
200adb00 910
830d5c43 911simple("userinfo",
7788b7c7
RK
912 "Get a user property.",
913 "If the user does not exist an error is returned, if the user exists but the property does not then a null value is returned.",
50d905eb
RK
914 [["string", "username", "User to read"],
915 ["string", "property", "Property to read"]],
c12575c6 916 [["string", "value", "Value of property"]]);
200adb00 917
830d5c43
RK
918simple("users",
919 "Get a list of users",
920 "",
921 [],
c12575c6 922 [["body", "users", "List of users"]]);
200adb00 923
830d5c43 924simple("version",
7788b7c7
RK
925 "Get the server version",
926 "",
927 [],
c12575c6
RK
928 [["string", "version", "Server version string"]]);
929
930simple(["volume", "set_volume"],
931 "Set the volume",
932 "",
933 [["integer", "left", "Left channel volume"],
934 ["integer", "right", "Right channel volume"]]);
200adb00 935
c12575c6
RK
936simple(["volume", "get_volume"],
937 "Get the volume",
938 "",
939 [],
940 [["integer", "left", "Left channel volume"],
941 ["integer", "right", "Right channel volume"]]);
200adb00
RK
942
943# End matter ------------------------------------------------------------------
944
945push(@h, "#endif\n");
946
ca7f5bb3
RK
947push(@ah, "#endif\n");
948
200adb00
RK
949# Write it all out ------------------------------------------------------------
950
7788b7c7
RK
951Write("lib/client-stubs.h", \@h);
952Write("lib/client-stubs.c", \@c);
ca7f5bb3
RK
953
954Write("lib/eclient-stubs.h", \@ah);
955Write("lib/eclient-stubs.c", \@ac);
956
957if(scalar @missing) {
958 print "Missing:\n";
959 print map(" $_\n", @missing);
960}