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