chiark / gitweb /
protogen: support multiple return values.
[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
RK
31# integer An integer. Decimal on the wire.
32# boolean True or false. "yes" or "no" on the wire.
33# list In commands: a list of strings in the command.
34# In returns: a list of lines in the response.
5dc19ffd 35# pair-list In returns: a list of key-value pairs in a response body.
ec9c0462
RK
36# body In commands: a list of strings as a command body.
37# In returns: a list of strings as a response body.
38# queue In returns: a list of queue entries in a response body.
39# queue-one In returns: a queue entry in the response.
40#
41
200adb00
RK
42# Variables and utilities -----------------------------------------------------
43
44our @h = ();
45our @c = ();
46
ea9f5de5
RK
47# Write(PATH, LINES)
48#
49# Write array ref LINES to file PATH.
200adb00
RK
50sub Write {
51 my $path = shift;
52 my $lines = shift;
53
54 (open(F, ">$path")
55 and print F @$lines
56 and close F)
7788b7c7 57 or die "$0: $path: $!\n";
200adb00
RK
58}
59
60# Command classes -------------------------------------------------------------
61
ea9f5de5
RK
62# c_in_decl([TYPE, NAME])
63#
64# Return the C declaration for an input parameter of type TYPE with
65# name NAME.
50d905eb
RK
66sub c_in_decl {
67 my $arg = shift;
68
69 my $type = $arg->[0];
70 my $name = $arg->[1];
71 if($type eq 'string') {
72 return "const char *$name";
73 } elsif($type eq 'integer') {
74 return "long $name";
0bc1d67c 75 } elsif($type eq 'list' or $type eq 'body') {
08af2413
RK
76 return ("char **$name",
77 "int n$name");
50d905eb 78 } else {
ec9c0462 79 die "$0: c_in_decl: unknown type '$type'\n";
50d905eb
RK
80 }
81}
82
ea9f5de5
RK
83# c_out_decl([TYPE, NAME])
84#
85# Return the C declaration for an output (reference) parameter of type
86# TYPE with name NAME.
50d905eb
RK
87sub c_out_decl {
88 my $arg = shift;
89
830d5c43 90 return () unless defined $arg;
50d905eb
RK
91 my $type = $arg->[0];
92 my $name = $arg->[1];
c12575c6 93 if($type eq 'string' or $type eq 'string-raw') {
830d5c43 94 return ("char **${name}p");
50d905eb 95 } elsif($type eq 'integer') {
830d5c43
RK
96 return ("long *${name}p");
97 } elsif($type eq 'boolean') {
98 return ("int *${name}p");
ec9c0462 99 } elsif($type eq 'list' or $type eq 'body') {
830d5c43
RK
100 return ("char ***${name}p",
101 "int *n${name}p");
5dc19ffd
RK
102 } elsif($type eq 'pair-list') {
103 return ("struct kvp **${name}p");
ec9c0462 104 } elsif($type eq 'queue' or $type eq 'queue-one') {
08af2413 105 return ("struct queue_entry **${name}p");
f4522fa7
RK
106 } elsif($type eq 'user') {
107 return ();
50d905eb 108 } else {
ec9c0462 109 die "$0: c_out_decl: unknown type '$type'\n";
50d905eb
RK
110 }
111}
112
ea9f5de5
RK
113# c_param_docs([TYPE, NAME})
114#
115# Return the doc string for a C input parameter.
50d905eb
RK
116sub c_param_docs {
117 my $args = shift;
08af2413
RK
118 my @d = ();
119 for my $arg (@$args) {
0bc1d67c 120 if($arg->[0] eq 'body' or $arg->[0] eq 'list') {
08af2413
RK
121 push(@d,
122 " * \@param $arg->[1] $arg->[2]\n",
123 " * \@param n$arg->[1] Length of $arg->[1]\n");
124 } else {
125 push(@d, " * \@param $arg->[1] $arg->[2]\n");
126 }
127 }
128 return @d;
50d905eb
RK
129}
130
ea9f5de5
RK
131# c_param_docs([TYPE, NAME})
132#
133# Return the doc string for a C output parameter.
830d5c43 134sub c_return_docs {
c12575c6
RK
135 my $returns = shift;
136 return () unless defined $returns;
137 for my $return (@$returns) {
138 my $type = $return->[0];
139 my $name = $return->[1];
140 my $descr = $return->[2];
141 if($type eq 'string'
142 or $type eq 'string-raw'
143 or $type eq 'integer'
144 or $type eq 'boolean') {
145 return (" * \@param ${name}p $descr\n");
146 } elsif($type eq 'list' or $type eq 'body') {
147 return (" * \@param ${name}p $descr\n",
148 " * \@param n${name}p Number of elements in ${name}p\n");
149 } elsif($type eq 'pair-list') {
150 return (" * \@param ${name}p $descr\n");
151 } elsif($type eq 'queue' or $type eq 'queue-one') {
152 return (" * \@param ${name}p $descr\n");
153 } elsif($type eq 'user') {
154 return ();
155 } else {
156 die "$0: c_return_docs: unknown type '$type'\n";
157 }
830d5c43 158 }
7788b7c7
RK
159}
160
830d5c43
RK
161# simple(CMD, SUMMARY, DETAIL,
162# [[TYPE,NAME,DESCR], [TYPE,NAME,DESCR], ...],
c12575c6 163# [[RETURN-TYPE, RETURN-NAME, RETURN_DESCR]])
ff75e16e
RK
164#
165# CMD is normally just the name of the command, but can
166# be [COMMAND,FUNCTION] if the function name should differ
167# from the protocol command.
830d5c43 168sub simple {
7788b7c7
RK
169 my $cmd = shift;
170 my $summary = shift;
171 my $detail = shift;
172 my $args = shift;
c12575c6 173 my $returns = shift;
7788b7c7 174
ff75e16e
RK
175 my $cmdc;
176 if(ref $cmd eq 'ARRAY') {
177 $cmdc = $$cmd[1];
178 $cmd = $$cmd[0];
179 } else {
180 $cmdc = $cmd;
181 $cmdc =~ s/-/_/g;
182 }
ec9c0462 183 print STDERR "Processing $cmd... ";
7788b7c7 184 # Synchronous C API
ec9c0462 185 print STDERR "H ";
7788b7c7
RK
186 push(@h, "/** \@brief $summary\n",
187 " *\n",
188 " * $detail\n",
189 " *\n",
08af2413 190 " * \@param c Client\n",
830d5c43 191 c_param_docs($args),
c12575c6 192 c_return_docs($returns),
7788b7c7
RK
193 " * \@return 0 on success, non-0 on error\n",
194 " */\n",
50d905eb
RK
195 "int disorder_$cmdc(",
196 join(", ", "disorder_client *c",
197 map(c_in_decl($_), @$args),
c12575c6 198 map(c_out_decl($_), @$returns)),
830d5c43 199 ");\n\n");
ec9c0462 200 print STDERR "C ";
50d905eb
RK
201 push(@c, "int disorder_$cmdc(",
202 join(", ", "disorder_client *c",
203 map(c_in_decl($_), @$args),
c12575c6 204 map(c_out_decl($_), @$returns)),
830d5c43 205 ") {\n");
ff75e16e
RK
206 my @cargs = ();
207 for my $arg (@$args) {
208 if($arg->[0] eq 'body' or $arg->[0] eq 'list') {
209 push(@cargs, "disorder_$arg->[0]", $arg->[1], "n$arg->[1]");
210 } elsif($arg->[0] eq 'string') {
211 push(@cargs, $arg->[1]);
212 } elsif($arg->[0] eq 'integer') {
213 push(@cargs, "buf_$arg->[1]");
214 push(@c, " char buf_$arg->[1]\[16];\n",
215 " byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%ld\", $arg->[1]);\n");
216 } else {
217 die "$0: unsupported arg type '$arg->[0]' for '$cmd'\n";
218 }
219 }
c12575c6
RK
220 if(!defined $returns or scalar @$returns == 0) {
221 # Simple case
08af2413 222 push(@c, " return disorder_simple(",
c12575c6 223 join(", ", "c", "NULL", "\"$cmd\"", @cargs, "(char *)NULL"),
08af2413 224 ");\n");
c12575c6
RK
225 } elsif(scalar @$returns == 1
226 and $returns->[0]->[0] eq 'queue-one') {
227 # Special case
228 my $return = $$returns[0];
ec9c0462 229 push(@c, " return onequeue(c, \"$cmd\", $return->[1]p);\n");
c12575c6
RK
230 } elsif(scalar @$returns == 1
231 and $returns->[0]->[0] eq 'string-raw') {
232 # Special case
233 my $return = $$returns[0];
234 push(@c, " return disorder_simple(",
235 join(", ", "c", "$return->[1]p", "\"$cmd\"", @cargs, "(char *)NULL"),
236 ");\n");
237 } elsif(scalar @$returns == 1
238 and $returns->[0]->[0] eq 'pair-list') {
239 # Special case
240 my $return = $$returns[0];
eff6238f
RK
241 push(@c, " return pairlist(",
242 join(", ", "c", "$return->[1]p", "\"$cmd\"",
243 @cargs,
c12575c6 244 "(char *)NULL"),
eff6238f 245 ");\n");
830d5c43 246 } else {
c12575c6
RK
247 my $split = 0;
248 for(my $n = 0; $n < scalar @$returns; ++$n) {
249 my $return = $returns->[$n];
250 my $type = $return->[0];
251 my $name = $return->[1];
252 if($type eq 'string'
253 or $type eq 'boolean'
254 or $type eq 'integer'
255 or $type eq 'user') {
256 $split = 1;
257 }
258 }
259 if($split) {
260 push(@c, " char **v, *r;\n",
261 " int nv;\n");
262 }
263 push(@c,
264 " int rc = disorder_simple(",
265 join(", ",
266 "c",
267 $split ? "&r" : "NULL",
268 "\"$cmd\"",
269 @cargs,
270 "(char *)NULL"),
271 ");\n",
272 " if(rc)\n",
273 " return rc;\n");
274 if($split) {
275 push(@c,
276 " v = split(r, &nv, SPLIT_QUOTES, 0, 0);\n",
277 " if(nv != ", scalar @$returns, ") {\n",
278 " disorder_error(0, \"malformed reply to %s\", \"$cmd\");\n",
279 " return -1;\n",
280 " }\n");
281 }
282 for(my $n = 0; $n < scalar @$returns; ++$n) {
283 my $return = $returns->[$n];
284 my $type = $return->[0];
285 my $name = $return->[1];
286 if($type eq 'string') {
287 push(@c,
288 " *${name}p = v[$n];\n");
289 } elsif($type eq 'boolean') {
290 push(@c,
291 " if(boolean(\"$cmd\", v[$n], ${name}p))\n",
292 " return -1;\n");
293 } elsif($type eq 'integer') {
294 push(@c,
295 " *${name}p = atol(v[$n]);\n");
296 } elsif($type eq 'user') {
297 push(@c,
298 " c->user = v[$n];\n");
299 } elsif($type eq 'body') {
300 push(@c,
301 " if(readlist(c, ${name}p, n${name}p))\n",
302 " return -1;\n");
303 } elsif($type eq 'queue') {
304 push(@c,
305 " if(readqueue(c, ${name}p))\n",
306 " return -1;\n");
307 } else {
308 die "$0: C API: unknown return type '$type' for '$name'\n";
309 }
310 }
311 push(@c, " return 0;\n");
312 # TODO xfree unconsumed split output
830d5c43
RK
313 }
314 push(@c, "}\n\n");
7788b7c7
RK
315
316 # Asynchronous C API
317 # TODO
318
319 # Python API
320 # TODO
321
322 # Java API
323 # TODO
ec9c0462 324 print STDERR "\n";
7788b7c7
RK
325}
326
200adb00
RK
327# TODO other command classes
328
329# Front matter ----------------------------------------------------------------
330
ff75e16e
RK
331our @generated = ("/*\n",
332 " * Automatically generated file, see scripts/protocol\n",
333 " *\n",
334 " * DO NOT EDIT.\n",
335 " */\n");
336
200adb00 337our @gpl = ("/*\n",
7788b7c7 338 " * This file is part of DisOrder.\n",
ff75e16e 339 " * Copyright (C) 2010-11 Richard Kettlewell\n",
7788b7c7
RK
340 " *\n",
341 " * This program is free software: you can redistribute it and/or modify\n",
342 " * it under the terms of the GNU General Public License as published by\n",
343 " * the Free Software Foundation, either version 3 of the License, or\n",
344 " * (at your option) any later version.\n",
345 " *\n",
346 " * This program is distributed in the hope that it will be useful,\n",
347 " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n",
348 " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n",
349 " * GNU General Public License for more details.\n",
350 " *\n",
351 " * You should have received a copy of the GNU General Public License\n",
352 " * along with this program. If not, see <http://www.gnu.org/licenses/>.\n",
353 " */\n");
200adb00
RK
354
355
ff75e16e 356push(@h, @generated, @gpl,
200adb00
RK
357 "#ifndef CLIENT_STUBS_H\n",
358 "#define CLIENT_STUBS_H\n",
359 "\n");
360
ff75e16e 361push(@c, @generated, @gpl,
200adb00
RK
362 "\n");
363
364# The protocol ----------------------------------------------------------------
365
96b1cf08
RK
366simple("adopt",
367 "Adopt a track",
368 "Makes the calling user owner of a randomly picked track.",
50d905eb 369 [["string", "id", "Track ID"]]);
200adb00 370
96b1cf08
RK
371simple("adduser",
372 "Create a user",
373 "Create a new user. Requires the 'admin' right. Email addresses etc must be filled in in separate commands.",
50d905eb
RK
374 [["string", "user", "New username"],
375 ["string", "password", "Initial password"],
376 ["string", "rights", "Initial rights (optional)"]]);
200adb00 377
830d5c43
RK
378simple("allfiles",
379 "List files and directories in a directory",
380 "See 'files' and 'dirs' for more specific lists.",
381 [["string", "dir", "Directory to list (optional)"],
382 ["string", "re", "Regexp that results must match (optional)"]],
c12575c6 383 [["body", "files", "List of matching files and directories"]]);
200adb00 384
f4522fa7
RK
385simple("confirm",
386 "Confirm registration",
387 "The confirmation string must have been created with 'register'. The username is returned so the caller knows who they are.",
388 [["string", "confirmation", "Confirmation string"]],
c12575c6 389 [["user"]]);
f4522fa7
RK
390
391simple("cookie",
392 "Log in with a cookie",
393 "The cookie must have been created with 'make-cookie'. The username is returned so the caller knows who they are.",
394 [["string", "cookie", "Cookie string"]],
c12575c6 395 [["user"]]);
200adb00 396
96b1cf08
RK
397simple("deluser",
398 "Delete user",
399 "Requires the 'admin' right.",
50d905eb 400 [["string", "user", "User to delete"]]);
200adb00 401
830d5c43
RK
402simple("dirs",
403 "List directories in a directory",
404 "",
405 [["string", "dir", "Directory to list (optional)"],
406 ["string", "re", "Regexp that results must match (optional)"]],
c12575c6 407 [["body", "files", "List of matching directories"]]);
200adb00 408
96b1cf08
RK
409simple("disable",
410 "Disable play",
411 "Play will stop at the end of the current track, if one is playing. Requires the 'global prefs' right.",
412 []);
413
414simple("edituser",
415 "Set a user property",
416 "With the 'admin' right you can do anything. Otherwise you need the 'userinfo' right and can only set 'email' and 'password'.",
50d905eb
RK
417 [["string", "username", "User to modify"],
418 ["string", "property", "Property name"],
419 ["string", "value", "New property value"]]);
96b1cf08
RK
420
421simple("enable",
422 "Enable play",
423 "Requires the 'global prefs' right.",
424 []);
425
830d5c43
RK
426simple("enabled",
427 "Detect whether play is enabled",
428 "",
429 [],
c12575c6 430 [["boolean", "enabled", "1 if play is enabled and 0 otherwise"]]);
830d5c43
RK
431
432simple("exists",
433 "Test whether a track exists",
434 "",
435 [["string", "track", "Track name"]],
c12575c6 436 [["boolean", "exists", "1 if the track exists and 0 otherwise"]]);
830d5c43
RK
437
438simple("files",
439 "List files in a directory",
440 "",
441 [["string", "dir", "Directory to list (optional)"],
442 ["string", "re", "Regexp that results must match (optional)"]],
c12575c6 443 [["body", "files", "List of matching files"]]);
830d5c43
RK
444
445simple("get",
7788b7c7
RK
446 "Get a track preference",
447 "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
448 [["string", "track", "Track name"],
449 ["string", "pref", "Preference name"]],
c12575c6 450 [["string", "value", "Preference value"]]);
200adb00 451
830d5c43 452simple("get-global",
7788b7c7
RK
453 "Get a global preference",
454 "If the preference does exist not then a null value is returned.",
50d905eb 455 [["string", "pref", "Global preference name"]],
c12575c6 456 [["string", "value", "Preference value"]]);
200adb00 457
830d5c43
RK
458simple("length",
459 "Get a track's length",
460 "If the track does not exist an error is returned.",
461 [["string", "track", "Track name"]],
c12575c6 462 [["integer", "length", "Track length in seconds"]]);
200adb00
RK
463
464# TODO log
465
830d5c43 466simple("make-cookie",
7788b7c7
RK
467 "Create a login cookie for this user",
468 "The cookie may be redeemed via the 'cookie' command",
469 [],
c12575c6 470 [["string", "cookie", "Newly created cookie"]]);
200adb00 471
0bc1d67c
RK
472simple("move",
473 "Move a track",
474 "Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.",
475 [["string", "track", "Track ID or name"],
476 ["integer", "delta", "How far to move the track towards the head of the queue"]]);
200adb00 477
0bc1d67c
RK
478simple("moveafter",
479 "Move multiple tracks",
480 "Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.",
481 [["string", "target", "Move after this track, or to head if \"\""],
482 ["list", "ids", "List of tracks to move by ID"]]);
200adb00 483
ff75e16e
RK
484simple(["new", "new_tracks"],
485 "List recently added tracks",
486 "",
487 [["integer", "max", "Maximum tracks to fetch, or 0 for all available"]],
c12575c6 488 [["body", "tracks", "Recently added tracks"]]);
200adb00 489
96b1cf08
RK
490simple("nop",
491 "Do nothing",
492 "Used as a keepalive. No authentication required.",
493 []);
200adb00 494
830d5c43 495simple("part",
7788b7c7
RK
496 "Get a track name part",
497 "If the name part cannot be constructed an empty string is returned.",
50d905eb
RK
498 [["string", "track", "Track name"],
499 ["string", "context", "Context (\"sort\" or \"display\")"],
500 ["string", "part", "Name part (\"artist\", \"album\" or \"title\")"]],
c12575c6 501 [["string", "part", "Value of name part"]]);
200adb00 502
96b1cf08
RK
503simple("pause",
504 "Pause the currently playing track",
505 "Requires the 'pause' right.",
506 []);
200adb00 507
830d5c43 508simple("play",
00861dcb
RK
509 "Play a track",
510 "Requires the 'play' right.",
50d905eb 511 [["string", "track", "Track to play"]],
c12575c6 512 [["string-raw", "id", "Queue ID of new track"]]);
00861dcb 513
0bc1d67c
RK
514simple("playafter",
515 "Play multiple tracks",
516 "Requires the 'play' right.",
517 [["string", "target", "Insert into queue after this track, or at head if \"\""],
518 ["list", "tracks", "List of track names to play"]]);
200adb00 519
ec9c0462
RK
520simple("playing",
521 "Retrieve the playing track",
522 "",
523 [],
c12575c6 524 [["queue-one", "playing", "Details of the playing track"]]);
200adb00 525
96b1cf08
RK
526simple("playlist-delete",
527 "Delete a playlist",
528 "Requires the 'play' right and permission to modify the playlist.",
50d905eb 529 [["string", "playlist", "Playlist to delete"]]);
200adb00 530
830d5c43
RK
531simple("playlist-get",
532 "List the contents of a playlist",
533 "Requires the 'read' right and oermission to read the playlist.",
534 [["string", "playlist", "Playlist name"]],
c12575c6 535 [["body", "tracks", "List of tracks in playlist"]]);
200adb00 536
830d5c43 537simple("playlist-get-share",
7788b7c7
RK
538 "Get a playlist's sharing status",
539 "Requires the 'read' right and permission to read the playlist.",
50d905eb 540 [["string", "playlist", "Playlist to read"]],
c12575c6 541 [["string-raw", "share", "Sharing status (\"public\", \"private\" or \"shared\")"]]);
200adb00 542
3680ef53
RK
543simple("playlist-lock",
544 "Lock a playlist",
545 "Requires the 'play' right and permission to modify the playlist. A given connection may lock at most one playlist.",
50d905eb 546 [["string", "playlist", "Playlist to delete"]]);
3680ef53 547
08af2413
RK
548simple("playlist-set",
549 "Set the contents of a playlist",
550 "Requires the 'play' right and permission to modify the playlist, which must be locked.",
551 [["string", "playlist", "Playlist to modify"],
0bc1d67c 552 ["body", "tracks", "New list of tracks for playlist"]]);
08af2413 553
96b1cf08
RK
554simple("playlist-set-share",
555 "Set a playlist's sharing status",
7788b7c7 556 "Requires the 'play' right and permission to modify the playlist.",
50d905eb
RK
557 [["string", "playlist", "Playlist to modify"],
558 ["string", "share", "New sharing status (\"public\", \"private\" or \"shared\")"]]);
200adb00 559
96b1cf08
RK
560simple("playlist-unlock",
561 "Unlock the locked playlist playlist",
562 "The playlist to unlock is implicit in the connection.",
563 []);
200adb00 564
830d5c43
RK
565simple("playlists",
566 "List playlists",
567 "Requires the 'read' right. Only playlists that you have permission to read are returned.",
568 [],
c12575c6 569 [["body", "playlists", "Playlist names"]]);
200adb00 570
5dc19ffd
RK
571simple("prefs",
572 "Get all the preferences for a track",
573 "",
574 [["string", "track", "Track name"]],
c12575c6 575 [["pair-list", "prefs", "Track preferences"]]);
200adb00 576
08af2413
RK
577simple("queue",
578 "List the queue",
579 "",
580 [],
c12575c6 581 [["queue", "queue", "Current queue contents"]]);
200adb00 582
96b1cf08
RK
583simple("random-disable",
584 "Disable random play",
585 "Requires the 'global prefs' right.",
586 []);
587
588simple("random-enable",
589 "Enable random play",
590 "Requires the 'global prefs' right.",
591 []);
200adb00 592
830d5c43
RK
593simple("random-enabled",
594 "Detect whether random play is enabled",
595 "Random play counts as enabled even if play is disabled.",
596 [],
c12575c6 597 [["boolean", "enabled", "1 if random play is enabled and 0 otherwise"]]);
200adb00 598
08af2413
RK
599simple("recent",
600 "List recently played tracks",
601 "",
602 [],
c12575c6 603 [["queue", "recent", "Recently played tracks"]]);
200adb00 604
96b1cf08
RK
605simple("reconfigure",
606 "Re-read configuraiton file.",
607 "Requires the 'admin' right.",
608 []);
200adb00 609
830d5c43 610simple("register",
7788b7c7
RK
611 "Register a new user",
612 "Requires the 'register' right which is usually only available to the 'guest' user. Redeem the confirmation string via 'confirm' to complete registration.",
50d905eb
RK
613 [["string", "username", "Requested new username"],
614 ["string", "password", "Requested initial password"],
615 ["string", "email", "New user's email address"]],
c12575c6 616 [["string", "confirmation", "Confirmation string"]]);
200adb00 617
96b1cf08
RK
618simple("reminder",
619 "Send a password reminder.",
620 "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 621 [["string", "username", "User to remind"]]);
200adb00 622
96b1cf08
RK
623simple("remove",
624 "Remove a track form the queue.",
625 "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 626 [["string", "id", "Track ID"]]);
200adb00 627
96b1cf08
RK
628simple("rescan",
629 "Rescan all collections for new or obsolete tracks.",
630 "Requires the 'rescan' right.",
7788b7c7 631 []); # TODO wait/fresh flags
200adb00 632
830d5c43 633simple("resolve",
7788b7c7
RK
634 "Resolve a track name",
635 "Converts aliases to non-alias track names",
50d905eb 636 [["string", "track", "Track name (might be an alias)"]],
c12575c6 637 [["string", "resolved", "Resolve track name (definitely not an alias)"]]);
200adb00 638
96b1cf08
RK
639simple("resume",
640 "Resume the currently playing track",
641 "Requires the 'pause' right.",
642 []);
200adb00 643
96b1cf08
RK
644simple("revoke",
645 "Revoke a cookie.",
646 "It will not subsequently be possible to log in with the cookie.",
08af2413 647 []);
200adb00 648
c12575c6
RK
649simple("rtp-address",
650 "Get the server's RTP address information",
651 "",
652 [],
653 [["string", "address", "Where to store hostname or address"],
654 ["string", "port", "Where to store service name or port number"]]);
200adb00 655
96b1cf08
RK
656simple("scratch",
657 "Terminate the playing track.",
658 "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 659 [["string", "id", "Track ID (optional)"]]);
200adb00
RK
660
661# TODO schedule-add
662
96b1cf08
RK
663simple("schedule-del",
664 "Delete a scheduled event.",
665 "Users can always delete their own scheduled events; with the admin right you can delete any event.",
50d905eb 666 [["string", "event", "ID of event to delete"]]);
200adb00 667
5dc19ffd
RK
668simple("schedule-get",
669 "Get the details of scheduled event",
670 "",
671 [["string", "id", "Event ID"]],
c12575c6 672 [["pair-list", "actiondata", "Details of event"]]);
200adb00 673
830d5c43
RK
674simple("schedule-list",
675 "List scheduled events",
676 "This just lists IDs. Use 'schedule-get' to retrieve more detail",
677 [],
c12575c6 678 [["body", "ids", "List of event IDs"]]);
200adb00 679
830d5c43
RK
680simple("search",
681 "Search for tracks",
682 "Terms are either keywords or tags formatted as 'tag:TAG-NAME'.",
683 [["string", "terms", "List of search terms"]],
c12575c6 684 [["body", "tracks", "List of matching tracks"]]);
200adb00 685
96b1cf08
RK
686simple("set",
687 "Set a track preference",
688 "Requires the 'prefs' right.",
50d905eb
RK
689 [["string", "track", "Track name"],
690 ["string", "pref", "Preference name"],
691 ["string", "value", "New value"]]);
200adb00 692
96b1cf08
RK
693simple("set-global",
694 "Set a global preference",
695 "Requires the 'global prefs' right.",
50d905eb
RK
696 [["string", "pref", "Preference name"],
697 ["string", "value", "New value"]]);
200adb00 698
eea34c08
RK
699simple("shutdown",
700 "Request server shutdown",
701 "Requires the 'admin' right.",
702 []);
7788b7c7 703
830d5c43
RK
704simple("stats",
705 "Get server statistics",
c12575c6 706 "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 707 [],
c12575c6 708 [["body", "stats", "List of server information strings."]]);
200adb00 709
830d5c43
RK
710simple("tags",
711 "Get a list of known tags",
712 "Only tags which apply to at least one track are returned.",
713 [],
c12575c6 714 [["body", "tags", "List of tags"]]);
200adb00 715
96b1cf08
RK
716simple("unset",
717 "Unset a track preference",
718 "Requires the 'prefs' right.",
50d905eb
RK
719 [["string", "track", "Track name"],
720 ["string", "pref", "Preference name"]]);
200adb00 721
96b1cf08
RK
722simple("unset-global",
723 "Set a global preference",
724 "Requires the 'global prefs' right.",
50d905eb 725 [["string", "pref", "Preference name"]]);
200adb00 726
50d905eb 727# 'user' only used for authentication
200adb00 728
830d5c43 729simple("userinfo",
7788b7c7
RK
730 "Get a user property.",
731 "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
732 [["string", "username", "User to read"],
733 ["string", "property", "Property to read"]],
c12575c6 734 [["string", "value", "Value of property"]]);
200adb00 735
830d5c43
RK
736simple("users",
737 "Get a list of users",
738 "",
739 [],
c12575c6 740 [["body", "users", "List of users"]]);
200adb00 741
830d5c43 742simple("version",
7788b7c7
RK
743 "Get the server version",
744 "",
745 [],
c12575c6
RK
746 [["string", "version", "Server version string"]]);
747
748simple(["volume", "set_volume"],
749 "Set the volume",
750 "",
751 [["integer", "left", "Left channel volume"],
752 ["integer", "right", "Right channel volume"]]);
200adb00 753
c12575c6
RK
754simple(["volume", "get_volume"],
755 "Get the volume",
756 "",
757 [],
758 [["integer", "left", "Left channel volume"],
759 ["integer", "right", "Right channel volume"]]);
200adb00
RK
760
761# End matter ------------------------------------------------------------------
762
763push(@h, "#endif\n");
764
765# Write it all out ------------------------------------------------------------
766
7788b7c7
RK
767Write("lib/client-stubs.h", \@h);
768Write("lib/client-stubs.c", \@c);