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