protogen: missing generated docs
[disorder] / scripts / protocol
CommitLineData
200adb00
RK
1#! /usr/bin/perl -w
2#
3# This file is part of DisOrder.
ff75e16e 4# Copyright (C) 2010-11 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",
483 "\n");
484
ff75e16e 485push(@c, @generated, @gpl,
200adb00
RK
486 "\n");
487
ca7f5bb3
RK
488push(@ah, @generated, @gpl,
489 "#ifndef ECLIENT_STUBS_H\n",
490 "#define ECLIENT_STUBS_H\n",
491 "\n");
492
493push(@ac, @generated, @gpl,
494 "\n");
495
200adb00
RK
496# The protocol ----------------------------------------------------------------
497
96b1cf08
RK
498simple("adopt",
499 "Adopt a track",
500 "Makes the calling user owner of a randomly picked track.",
50d905eb 501 [["string", "id", "Track ID"]]);
200adb00 502
96b1cf08
RK
503simple("adduser",
504 "Create a user",
505 "Create a new user. Requires the 'admin' right. Email addresses etc must be filled in in separate commands.",
50d905eb
RK
506 [["string", "user", "New username"],
507 ["string", "password", "Initial password"],
508 ["string", "rights", "Initial rights (optional)"]]);
200adb00 509
830d5c43
RK
510simple("allfiles",
511 "List files and directories in a directory",
512 "See 'files' and 'dirs' for more specific lists.",
513 [["string", "dir", "Directory to list (optional)"],
514 ["string", "re", "Regexp that results must match (optional)"]],
c12575c6 515 [["body", "files", "List of matching files and directories"]]);
200adb00 516
f4522fa7
RK
517simple("confirm",
518 "Confirm registration",
519 "The confirmation string must have been created with 'register'. The username is returned so the caller knows who they are.",
520 [["string", "confirmation", "Confirmation string"]],
c12575c6 521 [["user"]]);
f4522fa7
RK
522
523simple("cookie",
524 "Log in with a cookie",
525 "The cookie must have been created with 'make-cookie'. The username is returned so the caller knows who they are.",
526 [["string", "cookie", "Cookie string"]],
c12575c6 527 [["user"]]);
200adb00 528
96b1cf08
RK
529simple("deluser",
530 "Delete user",
531 "Requires the 'admin' right.",
50d905eb 532 [["string", "user", "User to delete"]]);
200adb00 533
830d5c43
RK
534simple("dirs",
535 "List directories in a directory",
536 "",
537 [["string", "dir", "Directory to list (optional)"],
538 ["string", "re", "Regexp that results must match (optional)"]],
c12575c6 539 [["body", "files", "List of matching directories"]]);
200adb00 540
96b1cf08
RK
541simple("disable",
542 "Disable play",
543 "Play will stop at the end of the current track, if one is playing. Requires the 'global prefs' right.",
544 []);
545
546simple("edituser",
547 "Set a user property",
548 "With the 'admin' right you can do anything. Otherwise you need the 'userinfo' right and can only set 'email' and 'password'.",
50d905eb
RK
549 [["string", "username", "User to modify"],
550 ["string", "property", "Property name"],
551 ["string", "value", "New property value"]]);
96b1cf08
RK
552
553simple("enable",
554 "Enable play",
555 "Requires the 'global prefs' right.",
556 []);
557
830d5c43
RK
558simple("enabled",
559 "Detect whether play is enabled",
560 "",
561 [],
c12575c6 562 [["boolean", "enabled", "1 if play is enabled and 0 otherwise"]]);
830d5c43
RK
563
564simple("exists",
565 "Test whether a track exists",
566 "",
567 [["string", "track", "Track name"]],
c12575c6 568 [["boolean", "exists", "1 if the track exists and 0 otherwise"]]);
830d5c43
RK
569
570simple("files",
571 "List files in a directory",
572 "",
573 [["string", "dir", "Directory to list (optional)"],
574 ["string", "re", "Regexp that results must match (optional)"]],
c12575c6 575 [["body", "files", "List of matching files"]]);
830d5c43
RK
576
577simple("get",
7788b7c7
RK
578 "Get a track preference",
579 "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
580 [["string", "track", "Track name"],
581 ["string", "pref", "Preference name"]],
c12575c6 582 [["string", "value", "Preference value"]]);
200adb00 583
830d5c43 584simple("get-global",
7788b7c7
RK
585 "Get a global preference",
586 "If the preference does exist not then a null value is returned.",
50d905eb 587 [["string", "pref", "Global preference name"]],
c12575c6 588 [["string", "value", "Preference value"]]);
200adb00 589
830d5c43
RK
590simple("length",
591 "Get a track's length",
592 "If the track does not exist an error is returned.",
593 [["string", "track", "Track name"]],
c12575c6 594 [["integer", "length", "Track length in seconds"]]);
200adb00
RK
595
596# TODO log
597
830d5c43 598simple("make-cookie",
7788b7c7
RK
599 "Create a login cookie for this user",
600 "The cookie may be redeemed via the 'cookie' command",
601 [],
c12575c6 602 [["string", "cookie", "Newly created cookie"]]);
200adb00 603
0bc1d67c
RK
604simple("move",
605 "Move a track",
606 "Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.",
607 [["string", "track", "Track ID or name"],
608 ["integer", "delta", "How far to move the track towards the head of the queue"]]);
200adb00 609
0bc1d67c
RK
610simple("moveafter",
611 "Move multiple tracks",
612 "Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.",
613 [["string", "target", "Move after this track, or to head if \"\""],
614 ["list", "ids", "List of tracks to move by ID"]]);
200adb00 615
ff75e16e
RK
616simple(["new", "new_tracks"],
617 "List recently added tracks",
618 "",
619 [["integer", "max", "Maximum tracks to fetch, or 0 for all available"]],
c12575c6 620 [["body", "tracks", "Recently added tracks"]]);
200adb00 621
96b1cf08
RK
622simple("nop",
623 "Do nothing",
624 "Used as a keepalive. No authentication required.",
625 []);
200adb00 626
830d5c43 627simple("part",
7788b7c7
RK
628 "Get a track name part",
629 "If the name part cannot be constructed an empty string is returned.",
50d905eb
RK
630 [["string", "track", "Track name"],
631 ["string", "context", "Context (\"sort\" or \"display\")"],
632 ["string", "part", "Name part (\"artist\", \"album\" or \"title\")"]],
c12575c6 633 [["string", "part", "Value of name part"]]);
200adb00 634
96b1cf08
RK
635simple("pause",
636 "Pause the currently playing track",
637 "Requires the 'pause' right.",
638 []);
200adb00 639
830d5c43 640simple("play",
00861dcb
RK
641 "Play a track",
642 "Requires the 'play' right.",
50d905eb 643 [["string", "track", "Track to play"]],
c12575c6 644 [["string-raw", "id", "Queue ID of new track"]]);
00861dcb 645
0bc1d67c
RK
646simple("playafter",
647 "Play multiple tracks",
648 "Requires the 'play' right.",
649 [["string", "target", "Insert into queue after this track, or at head if \"\""],
650 ["list", "tracks", "List of track names to play"]]);
200adb00 651
ec9c0462
RK
652simple("playing",
653 "Retrieve the playing track",
654 "",
655 [],
c12575c6 656 [["queue-one", "playing", "Details of the playing track"]]);
200adb00 657
96b1cf08
RK
658simple("playlist-delete",
659 "Delete a playlist",
660 "Requires the 'play' right and permission to modify the playlist.",
50d905eb 661 [["string", "playlist", "Playlist to delete"]]);
200adb00 662
830d5c43
RK
663simple("playlist-get",
664 "List the contents of a playlist",
665 "Requires the 'read' right and oermission to read the playlist.",
666 [["string", "playlist", "Playlist name"]],
c12575c6 667 [["body", "tracks", "List of tracks in playlist"]]);
200adb00 668
830d5c43 669simple("playlist-get-share",
7788b7c7
RK
670 "Get a playlist's sharing status",
671 "Requires the 'read' right and permission to read the playlist.",
50d905eb 672 [["string", "playlist", "Playlist to read"]],
c12575c6 673 [["string-raw", "share", "Sharing status (\"public\", \"private\" or \"shared\")"]]);
200adb00 674
3680ef53
RK
675simple("playlist-lock",
676 "Lock a playlist",
677 "Requires the 'play' right and permission to modify the playlist. A given connection may lock at most one playlist.",
50d905eb 678 [["string", "playlist", "Playlist to delete"]]);
3680ef53 679
08af2413
RK
680simple("playlist-set",
681 "Set the contents of a playlist",
682 "Requires the 'play' right and permission to modify the playlist, which must be locked.",
683 [["string", "playlist", "Playlist to modify"],
0bc1d67c 684 ["body", "tracks", "New list of tracks for playlist"]]);
08af2413 685
96b1cf08
RK
686simple("playlist-set-share",
687 "Set a playlist's sharing status",
7788b7c7 688 "Requires the 'play' right and permission to modify the playlist.",
50d905eb
RK
689 [["string", "playlist", "Playlist to modify"],
690 ["string", "share", "New sharing status (\"public\", \"private\" or \"shared\")"]]);
200adb00 691
96b1cf08
RK
692simple("playlist-unlock",
693 "Unlock the locked playlist playlist",
694 "The playlist to unlock is implicit in the connection.",
695 []);
200adb00 696
830d5c43
RK
697simple("playlists",
698 "List playlists",
699 "Requires the 'read' right. Only playlists that you have permission to read are returned.",
700 [],
c12575c6 701 [["body", "playlists", "Playlist names"]]);
200adb00 702
5dc19ffd
RK
703simple("prefs",
704 "Get all the preferences for a track",
705 "",
706 [["string", "track", "Track name"]],
c12575c6 707 [["pair-list", "prefs", "Track preferences"]]);
200adb00 708
08af2413
RK
709simple("queue",
710 "List the queue",
711 "",
712 [],
c12575c6 713 [["queue", "queue", "Current queue contents"]]);
200adb00 714
96b1cf08
RK
715simple("random-disable",
716 "Disable random play",
717 "Requires the 'global prefs' right.",
718 []);
719
720simple("random-enable",
721 "Enable random play",
722 "Requires the 'global prefs' right.",
723 []);
200adb00 724
830d5c43
RK
725simple("random-enabled",
726 "Detect whether random play is enabled",
727 "Random play counts as enabled even if play is disabled.",
728 [],
c12575c6 729 [["boolean", "enabled", "1 if random play is enabled and 0 otherwise"]]);
200adb00 730
08af2413
RK
731simple("recent",
732 "List recently played tracks",
733 "",
734 [],
c12575c6 735 [["queue", "recent", "Recently played tracks"]]);
200adb00 736
96b1cf08
RK
737simple("reconfigure",
738 "Re-read configuraiton file.",
739 "Requires the 'admin' right.",
740 []);
200adb00 741
830d5c43 742simple("register",
7788b7c7
RK
743 "Register a new user",
744 "Requires the 'register' right which is usually only available to the 'guest' user. Redeem the confirmation string via 'confirm' to complete registration.",
50d905eb
RK
745 [["string", "username", "Requested new username"],
746 ["string", "password", "Requested initial password"],
747 ["string", "email", "New user's email address"]],
c12575c6 748 [["string", "confirmation", "Confirmation string"]]);
200adb00 749
96b1cf08
RK
750simple("reminder",
751 "Send a password reminder.",
752 "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 753 [["string", "username", "User to remind"]]);
200adb00 754
96b1cf08
RK
755simple("remove",
756 "Remove a track form the queue.",
757 "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 758 [["string", "id", "Track ID"]]);
200adb00 759
96b1cf08
RK
760simple("rescan",
761 "Rescan all collections for new or obsolete tracks.",
762 "Requires the 'rescan' right.",
7788b7c7 763 []); # TODO wait/fresh flags
200adb00 764
830d5c43 765simple("resolve",
7788b7c7
RK
766 "Resolve a track name",
767 "Converts aliases to non-alias track names",
50d905eb 768 [["string", "track", "Track name (might be an alias)"]],
c12575c6 769 [["string", "resolved", "Resolve track name (definitely not an alias)"]]);
200adb00 770
96b1cf08
RK
771simple("resume",
772 "Resume the currently playing track",
773 "Requires the 'pause' right.",
774 []);
200adb00 775
96b1cf08
RK
776simple("revoke",
777 "Revoke a cookie.",
778 "It will not subsequently be possible to log in with the cookie.",
08af2413 779 []);
200adb00 780
c12575c6
RK
781simple("rtp-address",
782 "Get the server's RTP address information",
783 "",
784 [],
785 [["string", "address", "Where to store hostname or address"],
786 ["string", "port", "Where to store service name or port number"]]);
200adb00 787
96b1cf08
RK
788simple("scratch",
789 "Terminate the playing track.",
790 "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 791 [["string", "id", "Track ID (optional)"]]);
200adb00 792
4d80373d
RK
793simple(["schedule-add", "schedule_add_play"],
794 "Schedule a track to play in the future",
795 "",
796 [["time", "when", "When to play the track"],
797 ["string", "priority", "Event priority (\"normal\" or \"junk\")"],
798 ["literal", "play", ""],
799 ["string", "track", "Track to play"]]);
800
801simple(["schedule-add", "schedule_add_set_global"],
802 "Schedule a global setting to be changed in the future",
803 "",
804 [["time", "when", "When to change the setting"],
805 ["string", "priority", "Event priority (\"normal\" or \"junk\")"],
806 ["literal", "set-global", ""],
807 ["string", "pref", "Global preference to set"],
808 ["string", "value", "New value of global preference"]]);
809
810simple(["schedule-add", "schedule_add_unset_global"],
811 "Schedule a global setting to be unset in the future",
812 "",
813 [["time", "when", "When to change the setting"],
814 ["string", "priority", "Event priority (\"normal\" or \"junk\")"],
815 ["literal", "set-global", ""],
816 ["string", "pref", "Global preference to set"]]);
200adb00 817
96b1cf08
RK
818simple("schedule-del",
819 "Delete a scheduled event.",
820 "Users can always delete their own scheduled events; with the admin right you can delete any event.",
50d905eb 821 [["string", "event", "ID of event to delete"]]);
200adb00 822
5dc19ffd
RK
823simple("schedule-get",
824 "Get the details of scheduled event",
825 "",
826 [["string", "id", "Event ID"]],
c12575c6 827 [["pair-list", "actiondata", "Details of event"]]);
200adb00 828
830d5c43
RK
829simple("schedule-list",
830 "List scheduled events",
831 "This just lists IDs. Use 'schedule-get' to retrieve more detail",
832 [],
c12575c6 833 [["body", "ids", "List of event IDs"]]);
200adb00 834
830d5c43
RK
835simple("search",
836 "Search for tracks",
837 "Terms are either keywords or tags formatted as 'tag:TAG-NAME'.",
838 [["string", "terms", "List of search terms"]],
c12575c6 839 [["body", "tracks", "List of matching tracks"]]);
200adb00 840
96b1cf08
RK
841simple("set",
842 "Set a track preference",
843 "Requires the 'prefs' right.",
50d905eb
RK
844 [["string", "track", "Track name"],
845 ["string", "pref", "Preference name"],
846 ["string", "value", "New value"]]);
200adb00 847
96b1cf08
RK
848simple("set-global",
849 "Set a global preference",
850 "Requires the 'global prefs' right.",
50d905eb
RK
851 [["string", "pref", "Preference name"],
852 ["string", "value", "New value"]]);
200adb00 853
eea34c08
RK
854simple("shutdown",
855 "Request server shutdown",
856 "Requires the 'admin' right.",
857 []);
7788b7c7 858
830d5c43
RK
859simple("stats",
860 "Get server statistics",
c12575c6 861 "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 862 [],
c12575c6 863 [["body", "stats", "List of server information strings."]]);
200adb00 864
830d5c43
RK
865simple("tags",
866 "Get a list of known tags",
867 "Only tags which apply to at least one track are returned.",
868 [],
c12575c6 869 [["body", "tags", "List of tags"]]);
200adb00 870
96b1cf08
RK
871simple("unset",
872 "Unset a track preference",
873 "Requires the 'prefs' right.",
50d905eb
RK
874 [["string", "track", "Track name"],
875 ["string", "pref", "Preference name"]]);
200adb00 876
96b1cf08
RK
877simple("unset-global",
878 "Set a global preference",
879 "Requires the 'global prefs' right.",
50d905eb 880 [["string", "pref", "Preference name"]]);
200adb00 881
50d905eb 882# 'user' only used for authentication
200adb00 883
830d5c43 884simple("userinfo",
7788b7c7
RK
885 "Get a user property.",
886 "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
887 [["string", "username", "User to read"],
888 ["string", "property", "Property to read"]],
c12575c6 889 [["string", "value", "Value of property"]]);
200adb00 890
830d5c43
RK
891simple("users",
892 "Get a list of users",
893 "",
894 [],
c12575c6 895 [["body", "users", "List of users"]]);
200adb00 896
830d5c43 897simple("version",
7788b7c7
RK
898 "Get the server version",
899 "",
900 [],
c12575c6
RK
901 [["string", "version", "Server version string"]]);
902
903simple(["volume", "set_volume"],
904 "Set the volume",
905 "",
906 [["integer", "left", "Left channel volume"],
907 ["integer", "right", "Right channel volume"]]);
200adb00 908
c12575c6
RK
909simple(["volume", "get_volume"],
910 "Get the volume",
911 "",
912 [],
913 [["integer", "left", "Left channel volume"],
914 ["integer", "right", "Right channel volume"]]);
200adb00
RK
915
916# End matter ------------------------------------------------------------------
917
918push(@h, "#endif\n");
919
ca7f5bb3
RK
920push(@ah, "#endif\n");
921
200adb00
RK
922# Write it all out ------------------------------------------------------------
923
7788b7c7
RK
924Write("lib/client-stubs.h", \@h);
925Write("lib/client-stubs.c", \@c);
ca7f5bb3
RK
926
927Write("lib/eclient-stubs.h", \@ah);
928Write("lib/eclient-stubs.c", \@ac);
929
930if(scalar @missing) {
931 print "Missing:\n";
932 print map(" $_\n", @missing);
933}