* On Debian and derivatives this should work:
- apt-get install gcc libc-dev automake autoconf libtool libgtk2.0-dev \
+ apt-get install gcc libc6-dev automake autoconf libtool libgtk2.0-dev \
libgc-dev libgcrypt-dev libpcre3-dev libvorbis-dev \
- libao-dev libmad0-dev libasound2-dev libdb4.3-dev \
+ libao-dev libmad0-dev libasound2-dev libdb4.5-dev \
libflac-dev vorbis-tools wget libsamplerate0-dev
- On lenny use libdb4.5-deb. libdb4.6 does not work (and configure will
- refuse to use it).
+ libdb4.6 does not work (and configure will refuse to use it).
* On FreeBSD you'll need at least these packages:
autotools bash flac mad boehm-gc db43 gmake gsed libao libgcrypt wget
* To add a new command:
1) Add a new c_ function and table entry in server/server.c
2) Document the new command in doc/disorder_protocol.5.in
- 3) Add a new function to lib/client.c
+ 3) Add a new function to scripts/protocol.
4) Add a new function to lib/eclient.c
5) Add a new function to python/disorder.py.in
6) Add a new command to clients/disorder.c and update doc/disorder.1.in
7) Add a new test somewhere in tests/*.py
- Depending on the purpose of the command it may be acceptable to leave out
- some of the client side work - for instance commands only ever used by the
- web interface are not implemented in lib/eclient.c.
* See disorder_protocol(5) for details of how the status code is
constructed, and the existing commands for examples.
Web Interface:
+ * The support targets are current Firefox, Chrome, IE and Safari. Obscure,
+ obsolete or text-only browsers are not of significant interest.
+
* The web interface does not use Javascript or Flash and I would like to
- keep it that way; Javascript might be acceptable but it must degrade
- gracefuly if disabled. Clever use of CSS is OK provided it works well on
- the mainstream browsers.
+ keep it that way. Javascript is likely to be acceptable if useful, but it
+ should degrade gracefuly if unavailable. Clever use of CSS is OK provided
+ it works well on the mainstream browsers.
* Update templates/help.tmpl for any changes you make.
a problem for Disobedience than the server. Use the GLIB event loop to
deal with long-running operations if you do need any.
- * Update doc/disobedience.1.in for any changes you make.
+ * Update the contents of disobedience/manual/ for any changes you make.
New Platforms:
char **tracks;
int ntracks, n;
struct tracksort_data *tsd;
+ char *id;
if(dcgi_client) {
if((track = cgi_get("track"))) {
- disorder_play(dcgi_client, track);
+ disorder_play(dcgi_client, track, &id);
} else if((dir = cgi_get("dir"))) {
if(disorder_files(dcgi_client, dir, 0, &tracks, &ntracks))
ntracks = 0;
tsd = tracksort_init(ntracks, tracks, "track");
for(n = 0; n < ntracks; ++n)
- disorder_play(dcgi_client, tsd[n].track);
+ disorder_play(dcgi_client, tsd[n].track, &id);
}
}
redirect(0);
extern struct queue_entry *dcgi_playing;
extern struct queue_entry *dcgi_recent;
-extern int dcgi_volume_left;
-extern int dcgi_volume_right;
+extern long dcgi_volume_left;
+extern long dcgi_volume_right;
extern char **dcgi_new;
extern int dcgi_nnew;
struct queue_entry *dcgi_playing;
struct queue_entry *dcgi_recent;
-int dcgi_volume_left;
-int dcgi_volume_right;
+long dcgi_volume_left;
+long dcgi_volume_right;
char **dcgi_new;
int dcgi_nnew;
queuemap_add(dcgi_playing);
}
if(need & DCGI_NEW)
- disorder_new_tracks(dcgi_client, &dcgi_new, &dcgi_nnew, 0);
+ disorder_new_tracks(dcgi_client, 0, &dcgi_new, &dcgi_nnew);
if(need & DCGI_RECENT) {
/* we need to reverse the order of the list */
disorder_recent(dcgi_client, &r);
return 0;
}
if(dcgi_client
- && !disorder_part(dcgi_client, (char **)&s,
+ && !disorder_part(dcgi_client,
track,
!strcmp(context, "short") ? "display" : context,
- part)) {
+ part,
+ (char **)&s)) {
if(!strcmp(context, "short"))
s = truncate_for_display(s, config->short_display);
return sink_writes(output, cgi_sgmlquote(s)) < 0 ? -1 : 0;
struct sink *output,
void attribute((unused)) *u) {
dcgi_lookup(DCGI_VOLUME);
- return sink_printf(output, "%d",
+ return sink_printf(output, "%ld",
!strcmp(args[0], "left")
? dcgi_volume_left : dcgi_volume_right) < 0 ? -1 : 0;
}
if(!dcgi_client)
return 0;
- if(disorder_resolve(dcgi_client, &track, args[0]))
+ if(disorder_resolve(dcgi_client, args[0], &track))
return 0;
dcgi_lookup(DCGI_PLAYING);
if(dcgi_playing && !strcmp(track, dcgi_playing->track))
void attribute((unused)) *u) {
char *r;
- if(dcgi_client && !disorder_resolve(dcgi_client, &r, args[0]))
+ if(dcgi_client && !disorder_resolve(dcgi_client, args[0], &r))
return sink_writes(output, r) < 0 ? -1 : 0;
return 0;
}
const struct mx_node **args,
struct sink *output,
void *u) {
- return exp__files_dirs(nargs, args, output, u, "dir", disorder_directories);
+ return exp__files_dirs(nargs, args, output, u, "dir", disorder_dirs);
}
static int exp__search_shim(disorder_client *c, const char *terms,
}
static void cf_play(char **argv) {
+ char *id;
while(*argv)
- if(disorder_play(getclient(), *argv++)) exit(EXIT_FAILURE);
+ if(disorder_play(getclient(), *argv++, &id)) exit(EXIT_FAILURE);
}
static void cf_remove(char **argv) {
}
static void cf_dirs(char **argv) {
- cf_somelist(argv, disorder_directories);
+ cf_somelist(argv, disorder_dirs);
}
static void cf_files(char **argv) {
}
static void cf_get_volume(char attribute((unused)) **argv) {
- int l, r;
+ long l, r;
if(disorder_get_volume(getclient(), &l, &r)) exit(EXIT_FAILURE);
- xprintf("%d %d\n", l, r);
+ xprintf("%ld %ld\n", l, r);
}
static void cf_set_volume(char **argv) {
static void cf_part(char **argv) {
char *s;
- if(disorder_part(getclient(), &s, argv[0], argv[1], argv[2])) exit(EXIT_FAILURE);
+ if(disorder_part(getclient(), argv[0], argv[1], argv[2], &s)) exit(EXIT_FAILURE);
xprintf("%s\n", nullcheck(utf82mb_f(s)));
}
static void cf_resolve(char **argv) {
char *track;
- if(disorder_resolve(getclient(), &track, argv[0])) exit(EXIT_FAILURE);
+ if(disorder_resolve(getclient(), argv[0], &track)) exit(EXIT_FAILURE);
xprintf("%s\n", nullcheck(utf82mb_f(track)));
}
static void cf_new(char **argv) {
char **vec;
- if(disorder_new_tracks(getclient(), &vec, 0, argv[0] ? atoi(argv[0]) : 0))
+ if(disorder_new_tracks(getclient(), argv[0] ? atol(argv[0]) : 0, &vec, 0))
exit(EXIT_FAILURE);
while(*vec)
xprintf("%s\n", nullcheck(utf82mb(*vec++)));
}
static void cf_schedule_play(char **argv) {
- if(disorder_schedule_add(getclient(),
- dateparse(argv[0]),
- argv[1],
- "play",
- argv[2]))
+ if(disorder_schedule_add_play(getclient(),
+ dateparse(argv[0]),
+ argv[1],
+ argv[2]))
exit(EXIT_FAILURE);
}
static void cf_schedule_set_global(char **argv) {
- if(disorder_schedule_add(getclient(),
- dateparse(argv[0]),
- argv[1],
- "set-global",
- argv[2],
- argv[3]))
+ if(disorder_schedule_add_set_global(getclient(),
+ dateparse(argv[0]),
+ argv[1],
+ argv[2],
+ argv[3]))
exit(EXIT_FAILURE);
}
static void cf_schedule_unset_global(char **argv) {
- if(disorder_schedule_add(getclient(),
- dateparse(argv[0]),
- argv[1],
- "set-global",
- argv[2],
- (char *)0))
+ if(disorder_schedule_add_unset_global(getclient(),
+ dateparse(argv[0]),
+ argv[1],
+ argv[2]))
exit(EXIT_FAILURE);
}
static void choose_playchildren_received(void *v,
const char *err,
int nvec, char **vec);
-static void choose_playchildren_played(void *v, const char *err);
+static void choose_playchildren_played(void *v,
+ const char *err,
+ const char *id);
/** @brief Popup menu */
static GtkWidget *choose_menu;
choose_gather_selected_files_callback,
v);
for(int n = 0; n < v->nvec; ++n)
- disorder_eclient_play(client, v->vec[n], choose_play_completed, 0);
+ disorder_eclient_play(client, choose_play_completed, v->vec[n], 0);
}
static int choose_properties_sensitive(void *extra) {
return;
}
for(int n = 0; n < nvec; ++n)
- disorder_eclient_play(client, vec[n], choose_playchildren_played, NULL);
+ disorder_eclient_play(client, choose_playchildren_played, vec[n], NULL);
}
static void choose_playchildren_played(void attribute((unused)) *v,
- const char *err) {
+ const char *err,
+ const char attribute((unused)) *id) {
if(err) {
popup_protocol_error(0, err);
return;
}
void choose_play_completed(void attribute((unused)) *v,
- const char *err) {
+ const char *err,
+ const char attribute((unused)) *id) {
if(err)
popup_protocol_error(0, err);
}
const char *track = choose_get_track(it);
if(queued(track))
return;
- disorder_eclient_play(client, track, choose_play_completed, 0);
+ disorder_eclient_play(client, choose_play_completed, track, 0);
}
gboolean choose_button_event(GtkWidget *widget,
GdkEventButton *event,
gpointer user_data);
-void choose_play_completed(void attribute((unused)) *v,
- const char *err);
+void choose_play_completed(void *v,
+ const char *err,
+ const char *id);
char *choose_get_track(GtkTreeIter *iter);
char *choose_get_sort(GtkTreeIter *iter);
char *choose_get_display(GtkTreeIter *iter);
/** @brief Called when a volume command completes */
static void volume_completed(void attribute((unused)) *v,
- const char *err,
- int attribute((unused)) l,
- int attribute((unused)) r) {
+ const char *err) {
if(err)
popup_protocol_error(0, err);
/* We don't set the UI's notion of the volume here, it is set from the log
if(backend && backend->set_volume)
backend->set_volume(&l, &r);
} else
- disorder_eclient_volume(client, volume_completed,
- nearbyint(left(v, b) * 100),
- nearbyint(right(v, b) * 100),
- 0);
+ disorder_eclient_set_volume(client, volume_completed,
+ nearbyint(left(v, b) * 100),
+ nearbyint(right(v, b) * 100),
+ 0);
}
/** @brief Formats the volume value */
}
/** @brief Called when any global pref changes */
-static void globals_pref_changed(const char *event,
+static void globals_pref_changed(const char attribute((unused)) *event,
void *eventdata,
- void *callbackdata) {
+ void attribute((unused)) *callbackdata) {
const char *pref = eventdata;
if(!globals_window)
return; /* not paying attention */
D(("namepart_fill %s %s %s %s", track, context, part, key));
++namepart_lookups_outstanding;
D(("namepart_lookups_outstanding -> %d\n", namepart_lookups_outstanding));
- disorder_eclient_namepart(client, namepart_completed,
- track, context, part, (void *)key);
+ disorder_eclient_part(client, namepart_completed,
+ track, context, part, (void *)key);
}
/** @brief Look up a namepart
/* Playlists menu ----------------------------------------------------------- */
static void playlist_menu_playing(void attribute((unused)) *v,
- const char *err) {
+ const char *err,
+ const char attribute((unused)) *id) {
if(err)
popup_submsg(playlist_window, GTK_MESSAGE_ERROR, err);
}
return;
}
for(int n = 0; n < nvec; ++n)
- disorder_eclient_play(client, vec[n], playlist_menu_playing, NULL);
+ disorder_eclient_play(client, playlist_menu_playing, vec[n], NULL);
}
/** @brief Called to activate a playlist
* wanted was the underlying preference, but in fact it should always match
* and will supply a sane default without having to know how to parse tracks
* names (which implies knowing collection roots). */
- disorder_eclient_namepart(client, prefdata_completed,
- f->track, "display", f->p->part, f);
+ disorder_eclient_part(client, prefdata_completed,
+ f->track, "display", f->p->part, f);
}
static void completed_namepart(struct prefdata *f) {
struct queue_entry *q = ql_iter_to_q(model, iter);
if(q != playing_track)
- disorder_eclient_remove(client, q->id, ql_remove_completed, q);
+ disorder_eclient_remove(client, ql_remove_completed, q->id, q);
}
void ql_remove_activate(GtkMenuItem attribute((unused)) *menuitem,
&& gtk_tree_selection_count_selected_rows(ql->selection) > 0;
}
-static void ql_play_completed(void attribute((unused)) *v, const char *err) {
+static void ql_play_completed(void attribute((unused)) *v, const char *err,
+ const char attribute((unused)) *id) {
if(err)
popup_protocol_error(0, err);
}
gpointer attribute((unused)) data) {
struct queue_entry *q = ql_iter_to_q(model, iter);
- disorder_eclient_play(client, q->track, ql_play_completed, q);
+ disorder_eclient_play(client, ql_play_completed, q->track, q);
}
void ql_play_activate(GtkMenuItem attribute((unused)) *menuitem,
/* Tell the server to move them. The log will tell us about the change (if
* indeed it succeeds!), so no need to rearrange the model now. */
disorder_eclient_moveafter(client,
+ queue_drop_completed,
after_me ? after_me->id : "",
- ntracks, (const char **)ids,
- queue_drop_completed, NULL);
+ (char **)ids, ntracks,
+ NULL);
} else {
/* You can't tell the server to insert after the playing track by ID, you
* have to send "". */
after_me = NULL;
/* Play the tracks */
disorder_eclient_playafter(client,
+ queue_drop_completed,
after_me ? after_me->id : "",
- ntracks, (const char **)tracks,
- queue_drop_completed, NULL);
+ (char **)tracks, ntracks,
+ NULL);
}
}
.B confirm \fICONFIRMATION
Confirm user registration.
\fICONFIRMATION\fR is as returned from \fBregister\fR below.
-This command can be used without logging in.
+This command logs the user in.
+The response contains the logged-in username.
.TP
.B cookie \fICOOKIE
Log a user back in using a cookie created with \fBmake\-cookie\fR.
Resume the current track after a \fBpause\fR command.
Requires the \fBpause\fR right.
.TP
-.B revoke \fBcookie\fR
-Revoke a cookie previously created with \fBmake\-cookie\fR.
-It will not be possible to use this cookie in the future.
+.B revoke
+Revoke the current login's cookie.
+It will not be possible to use the cookie in the future.
.TP
.B rtp\-address
Report the RTP broadcast (or multicast) address, in the form \fIADDRESS
Set a global preference.
Requires the \fBglobal prefs\fR right.
.TP
+.B shutdown
+Requests server shutdown.
+Requires the \fBadmin\fR right.
+.TP
.B stats
Send server statistics in plain text in a response body.
.TP
#
# This file is part of DisOrder.
-# Copyright (C) 2004-2009 Richard Kettlewell
+# Copyright (C) 2004-2010 Richard Kettlewell
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
byte-order.h \
cache.c cache.h \
cgi.c cgi.h \
- client.c client.h \
+ client.c client.h client-stubs.h \
client-common.c client-common.h \
configuration.c configuration.h \
cookies.c cookies.h \
defs.o: definitions.h versionstring.h
defs.lo: definitions.h versionstring.h
+client.o: client-stubs.c
+
rebuild-unicode:
cd ${srcdir} && ${top_srcdir}/scripts/make-unidata
CLEANFILES=definitions.h definitions.h.new version-string versionstring.h \
*.gcda *.gcov *.gcno *.c.html index.html
-EXTRA_DIST=trackdb.c trackdb-stub.c
+EXTRA_DIST=trackdb.c trackdb-stub.c client-stubs.c
return len;
}
+const char disorder__body[1];
+const char disorder__list[1];
+const char disorder__integer[1];
+const char disorder__time[1];
+
/*
Local Variables:
c-basic-offset:2
socklen_t find_server(struct config *c, struct sockaddr **sap, char **namep);
+/** @brief Marker for a command body */
+extern const char disorder__body[1];
+
+/** @brief Marker for a list of args */
+extern const char disorder__list[1];
+
+/** @brief Marker for an integer */
+extern const char disorder__integer[1];
+
+/** @brief Marker for a timestamp */
+extern const char disorder__time[1];
+
#endif /* CLIENT_COMMON_H */
/*
--- /dev/null
+/*
+ * Automatically generated file, see scripts/protocol
+ *
+ * DO NOT EDIT.
+ */
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2010-11 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/client-stubs.c
+ * @brief Generated client API implementation
+ */
+
+int disorder_adopt(disorder_client *c, const char *id) {
+ return disorder_simple(c, NULL, "adopt", id, (char *)NULL);
+}
+
+int disorder_adduser(disorder_client *c, const char *user, const char *password, const char *rights) {
+ return disorder_simple(c, NULL, "adduser", user, password, rights, (char *)NULL);
+}
+
+int disorder_allfiles(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp) {
+ int rc = disorder_simple(c, NULL, "allfiles", dir, re, (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, filesp, nfilesp))
+ return -1;
+ return 0;
+}
+
+int disorder_confirm(disorder_client *c, const char *confirmation) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "confirm", confirmation, (char *)NULL);
+ if(rc)
+ return rc;
+ c->user = v[0];
+ v[0] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_cookie(disorder_client *c, const char *cookie) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "cookie", cookie, (char *)NULL);
+ if(rc)
+ return rc;
+ c->user = v[0];
+ v[0] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_deluser(disorder_client *c, const char *user) {
+ return disorder_simple(c, NULL, "deluser", user, (char *)NULL);
+}
+
+int disorder_dirs(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp) {
+ int rc = disorder_simple(c, NULL, "dirs", dir, re, (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, filesp, nfilesp))
+ return -1;
+ return 0;
+}
+
+int disorder_disable(disorder_client *c) {
+ return disorder_simple(c, NULL, "disable", (char *)NULL);
+}
+
+int disorder_edituser(disorder_client *c, const char *username, const char *property, const char *value) {
+ return disorder_simple(c, NULL, "edituser", username, property, value, (char *)NULL);
+}
+
+int disorder_enable(disorder_client *c) {
+ return disorder_simple(c, NULL, "enable", (char *)NULL);
+}
+
+int disorder_enabled(disorder_client *c, int *enabledp) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "enabled", (char *)NULL);
+ if(rc)
+ return rc;
+ if(boolean("enabled", v[0], enabledp))
+ return -1;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_exists(disorder_client *c, const char *track, int *existsp) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "exists", track, (char *)NULL);
+ if(rc)
+ return rc;
+ if(boolean("exists", v[0], existsp))
+ return -1;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_files(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp) {
+ int rc = disorder_simple(c, NULL, "files", dir, re, (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, filesp, nfilesp))
+ return -1;
+ return 0;
+}
+
+int disorder_get(disorder_client *c, const char *track, const char *pref, char **valuep) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "get", track, pref, (char *)NULL);
+ if(rc)
+ return rc;
+ *valuep = v[0];
+ v[0] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_get_global(disorder_client *c, const char *pref, char **valuep) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "get-global", pref, (char *)NULL);
+ if(rc)
+ return rc;
+ *valuep = v[0];
+ v[0] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_length(disorder_client *c, const char *track, long *lengthp) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "length", track, (char *)NULL);
+ if(rc)
+ return rc;
+ *lengthp = atol(v[0]);
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_make_cookie(disorder_client *c, char **cookiep) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "make-cookie", (char *)NULL);
+ if(rc)
+ return rc;
+ *cookiep = v[0];
+ v[0] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_move(disorder_client *c, const char *track, long delta) {
+ return disorder_simple(c, NULL, "move", track, disorder__integer, delta, (char *)NULL);
+}
+
+int disorder_moveafter(disorder_client *c, const char *target, char **ids, int nids) {
+ return disorder_simple(c, NULL, "moveafter", target, disorder__list, ids, nids, (char *)NULL);
+}
+
+int disorder_new_tracks(disorder_client *c, long max, char ***tracksp, int *ntracksp) {
+ int rc = disorder_simple(c, NULL, "new", disorder__integer, max, (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, tracksp, ntracksp))
+ return -1;
+ return 0;
+}
+
+int disorder_nop(disorder_client *c) {
+ return disorder_simple(c, NULL, "nop", (char *)NULL);
+}
+
+int disorder_part(disorder_client *c, const char *track, const char *context, const char *part, char **partp) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "part", track, context, part, (char *)NULL);
+ if(rc)
+ return rc;
+ *partp = v[0];
+ v[0] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_pause(disorder_client *c) {
+ return disorder_simple(c, NULL, "pause", (char *)NULL);
+}
+
+int disorder_play(disorder_client *c, const char *track, char **idp) {
+ return disorder_simple(c, idp, "play", track, (char *)NULL);
+}
+
+int disorder_playafter(disorder_client *c, const char *target, char **tracks, int ntracks) {
+ return disorder_simple(c, NULL, "playafter", target, disorder__list, tracks, ntracks, (char *)NULL);
+}
+
+int disorder_playing(disorder_client *c, struct queue_entry **playingp) {
+ return onequeue(c, "playing", playingp);
+}
+
+int disorder_playlist_delete(disorder_client *c, const char *playlist) {
+ return disorder_simple(c, NULL, "playlist-delete", playlist, (char *)NULL);
+}
+
+int disorder_playlist_get(disorder_client *c, const char *playlist, char ***tracksp, int *ntracksp) {
+ int rc = disorder_simple(c, NULL, "playlist-get", playlist, (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, tracksp, ntracksp))
+ return -1;
+ return 0;
+}
+
+int disorder_playlist_get_share(disorder_client *c, const char *playlist, char **sharep) {
+ return disorder_simple(c, sharep, "playlist-get-share", playlist, (char *)NULL);
+}
+
+int disorder_playlist_lock(disorder_client *c, const char *playlist) {
+ return disorder_simple(c, NULL, "playlist-lock", playlist, (char *)NULL);
+}
+
+int disorder_playlist_set(disorder_client *c, const char *playlist, char **tracks, int ntracks) {
+ return disorder_simple(c, NULL, "playlist-set", playlist, disorder__body, tracks, ntracks, (char *)NULL);
+}
+
+int disorder_playlist_set_share(disorder_client *c, const char *playlist, const char *share) {
+ return disorder_simple(c, NULL, "playlist-set-share", playlist, share, (char *)NULL);
+}
+
+int disorder_playlist_unlock(disorder_client *c) {
+ return disorder_simple(c, NULL, "playlist-unlock", (char *)NULL);
+}
+
+int disorder_playlists(disorder_client *c, char ***playlistsp, int *nplaylistsp) {
+ int rc = disorder_simple(c, NULL, "playlists", (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, playlistsp, nplaylistsp))
+ return -1;
+ return 0;
+}
+
+int disorder_prefs(disorder_client *c, const char *track, struct kvp **prefsp) {
+ return pairlist(c, prefsp, "prefs", track, (char *)NULL);
+}
+
+int disorder_queue(disorder_client *c, struct queue_entry **queuep) {
+ int rc = disorder_simple(c, NULL, "queue", (char *)NULL);
+ if(rc)
+ return rc;
+ if(readqueue(c, queuep))
+ return -1;
+ return 0;
+}
+
+int disorder_random_disable(disorder_client *c) {
+ return disorder_simple(c, NULL, "random-disable", (char *)NULL);
+}
+
+int disorder_random_enable(disorder_client *c) {
+ return disorder_simple(c, NULL, "random-enable", (char *)NULL);
+}
+
+int disorder_random_enabled(disorder_client *c, int *enabledp) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "random-enabled", (char *)NULL);
+ if(rc)
+ return rc;
+ if(boolean("random-enabled", v[0], enabledp))
+ return -1;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_recent(disorder_client *c, struct queue_entry **recentp) {
+ int rc = disorder_simple(c, NULL, "recent", (char *)NULL);
+ if(rc)
+ return rc;
+ if(readqueue(c, recentp))
+ return -1;
+ return 0;
+}
+
+int disorder_reconfigure(disorder_client *c) {
+ return disorder_simple(c, NULL, "reconfigure", (char *)NULL);
+}
+
+int disorder_register(disorder_client *c, const char *username, const char *password, const char *email, char **confirmationp) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "register", username, password, email, (char *)NULL);
+ if(rc)
+ return rc;
+ *confirmationp = v[0];
+ v[0] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_reminder(disorder_client *c, const char *username) {
+ return disorder_simple(c, NULL, "reminder", username, (char *)NULL);
+}
+
+int disorder_remove(disorder_client *c, const char *id) {
+ return disorder_simple(c, NULL, "remove", id, (char *)NULL);
+}
+
+int disorder_rescan(disorder_client *c) {
+ return disorder_simple(c, NULL, "rescan", (char *)NULL);
+}
+
+int disorder_resolve(disorder_client *c, const char *track, char **resolvedp) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "resolve", track, (char *)NULL);
+ if(rc)
+ return rc;
+ *resolvedp = v[0];
+ v[0] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_resume(disorder_client *c) {
+ return disorder_simple(c, NULL, "resume", (char *)NULL);
+}
+
+int disorder_revoke(disorder_client *c) {
+ return disorder_simple(c, NULL, "revoke", (char *)NULL);
+}
+
+int disorder_rtp_address(disorder_client *c, char **addressp, char **portp) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 2, "rtp-address", (char *)NULL);
+ if(rc)
+ return rc;
+ *addressp = v[0];
+ v[0] = NULL;
+ *portp = v[1];
+ v[1] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_scratch(disorder_client *c, const char *id) {
+ return disorder_simple(c, NULL, "scratch", id, (char *)NULL);
+}
+
+int disorder_schedule_add_play(disorder_client *c, time_t when, const char *priority, const char *track) {
+ return disorder_simple(c, NULL, "schedule-add", disorder__time, when, priority, "play", track, (char *)NULL);
+}
+
+int disorder_schedule_add_set_global(disorder_client *c, time_t when, const char *priority, const char *pref, const char *value) {
+ return disorder_simple(c, NULL, "schedule-add", disorder__time, when, priority, "set-global", pref, value, (char *)NULL);
+}
+
+int disorder_schedule_add_unset_global(disorder_client *c, time_t when, const char *priority, const char *pref) {
+ return disorder_simple(c, NULL, "schedule-add", disorder__time, when, priority, "set-global", pref, (char *)NULL);
+}
+
+int disorder_schedule_del(disorder_client *c, const char *event) {
+ return disorder_simple(c, NULL, "schedule-del", event, (char *)NULL);
+}
+
+int disorder_schedule_get(disorder_client *c, const char *id, struct kvp **actiondatap) {
+ return pairlist(c, actiondatap, "schedule-get", id, (char *)NULL);
+}
+
+int disorder_schedule_list(disorder_client *c, char ***idsp, int *nidsp) {
+ int rc = disorder_simple(c, NULL, "schedule-list", (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, idsp, nidsp))
+ return -1;
+ return 0;
+}
+
+int disorder_search(disorder_client *c, const char *terms, char ***tracksp, int *ntracksp) {
+ int rc = disorder_simple(c, NULL, "search", terms, (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, tracksp, ntracksp))
+ return -1;
+ return 0;
+}
+
+int disorder_set(disorder_client *c, const char *track, const char *pref, const char *value) {
+ return disorder_simple(c, NULL, "set", track, pref, value, (char *)NULL);
+}
+
+int disorder_set_global(disorder_client *c, const char *pref, const char *value) {
+ return disorder_simple(c, NULL, "set-global", pref, value, (char *)NULL);
+}
+
+int disorder_shutdown(disorder_client *c) {
+ return disorder_simple(c, NULL, "shutdown", (char *)NULL);
+}
+
+int disorder_stats(disorder_client *c, char ***statsp, int *nstatsp) {
+ int rc = disorder_simple(c, NULL, "stats", (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, statsp, nstatsp))
+ return -1;
+ return 0;
+}
+
+int disorder_tags(disorder_client *c, char ***tagsp, int *ntagsp) {
+ int rc = disorder_simple(c, NULL, "tags", (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, tagsp, ntagsp))
+ return -1;
+ return 0;
+}
+
+int disorder_unset(disorder_client *c, const char *track, const char *pref) {
+ return disorder_simple(c, NULL, "unset", track, pref, (char *)NULL);
+}
+
+int disorder_unset_global(disorder_client *c, const char *pref) {
+ return disorder_simple(c, NULL, "unset-global", pref, (char *)NULL);
+}
+
+int disorder_userinfo(disorder_client *c, const char *username, const char *property, char **valuep) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "userinfo", username, property, (char *)NULL);
+ if(rc)
+ return rc;
+ *valuep = v[0];
+ v[0] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_users(disorder_client *c, char ***usersp, int *nusersp) {
+ int rc = disorder_simple(c, NULL, "users", (char *)NULL);
+ if(rc)
+ return rc;
+ if(readlist(c, usersp, nusersp))
+ return -1;
+ return 0;
+}
+
+int disorder_version(disorder_client *c, char **versionp) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 1, "version", (char *)NULL);
+ if(rc)
+ return rc;
+ *versionp = v[0];
+ v[0] = NULL;
+ free_strings(nv, v);
+ return 0;
+}
+
+int disorder_set_volume(disorder_client *c, long left, long right) {
+ return disorder_simple(c, NULL, "volume", disorder__integer, left, disorder__integer, right, (char *)NULL);
+}
+
+int disorder_get_volume(disorder_client *c, long *leftp, long *rightp) {
+ char **v;
+ int nv, rc = disorder_simple_split(c, &v, &nv, 2, "volume", (char *)NULL);
+ if(rc)
+ return rc;
+ *leftp = atol(v[0]);
+ *rightp = atol(v[1]);
+ free_strings(nv, v);
+ return 0;
+}
+
--- /dev/null
+/*
+ * Automatically generated file, see scripts/protocol
+ *
+ * DO NOT EDIT.
+ */
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2010-11 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef CLIENT_STUBS_H
+#define CLIENT_STUBS_H
+/** @file lib/client-stubs.h
+ * @brief Generated client API
+ *
+ * Don't include this file directly - use @ref client.h instead.
+ */
+
+/** @brief Adopt a track
+ *
+ * Makes the calling user owner of a randomly picked track.
+ *
+ * @param c Client
+ * @param id Track ID
+ * @return 0 on success, non-0 on error
+ */
+int disorder_adopt(disorder_client *c, const char *id);
+
+/** @brief Create a user
+ *
+ * Create a new user. Requires the 'admin' right. Email addresses etc must be filled in in separate commands.
+ *
+ * @param c Client
+ * @param user New username
+ * @param password Initial password
+ * @param rights Initial rights (optional)
+ * @return 0 on success, non-0 on error
+ */
+int disorder_adduser(disorder_client *c, const char *user, const char *password, const char *rights);
+
+/** @brief List files and directories in a directory
+ *
+ * See 'files' and 'dirs' for more specific lists.
+ *
+ * @param c Client
+ * @param dir Directory to list (optional)
+ * @param re Regexp that results must match (optional)
+ * @param filesp List of matching files and directories
+ * @param nfilesp Number of elements in filesp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_allfiles(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp);
+
+/** @brief Confirm registration
+ *
+ * The confirmation string must have been created with 'register'. The username is returned so the caller knows who they are.
+ *
+ * @param c Client
+ * @param confirmation Confirmation string
+ * @return 0 on success, non-0 on error
+ */
+int disorder_confirm(disorder_client *c, const char *confirmation);
+
+/** @brief Log in with a cookie
+ *
+ * The cookie must have been created with 'make-cookie'. The username is returned so the caller knows who they are.
+ *
+ * @param c Client
+ * @param cookie Cookie string
+ * @return 0 on success, non-0 on error
+ */
+int disorder_cookie(disorder_client *c, const char *cookie);
+
+/** @brief Delete user
+ *
+ * Requires the 'admin' right.
+ *
+ * @param c Client
+ * @param user User to delete
+ * @return 0 on success, non-0 on error
+ */
+int disorder_deluser(disorder_client *c, const char *user);
+
+/** @brief List directories in a directory
+ *
+ *
+ *
+ * @param c Client
+ * @param dir Directory to list (optional)
+ * @param re Regexp that results must match (optional)
+ * @param filesp List of matching directories
+ * @param nfilesp Number of elements in filesp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_dirs(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp);
+
+/** @brief Disable play
+ *
+ * Play will stop at the end of the current track, if one is playing. Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_disable(disorder_client *c);
+
+/** @brief Set a user property
+ *
+ * With the 'admin' right you can do anything. Otherwise you need the 'userinfo' right and can only set 'email' and 'password'.
+ *
+ * @param c Client
+ * @param username User to modify
+ * @param property Property name
+ * @param value New property value
+ * @return 0 on success, non-0 on error
+ */
+int disorder_edituser(disorder_client *c, const char *username, const char *property, const char *value);
+
+/** @brief Enable play
+ *
+ * Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_enable(disorder_client *c);
+
+/** @brief Detect whether play is enabled
+ *
+ *
+ *
+ * @param c Client
+ * @param enabledp 1 if play is enabled and 0 otherwise
+ * @return 0 on success, non-0 on error
+ */
+int disorder_enabled(disorder_client *c, int *enabledp);
+
+/** @brief Test whether a track exists
+ *
+ *
+ *
+ * @param c Client
+ * @param track Track name
+ * @param existsp 1 if the track exists and 0 otherwise
+ * @return 0 on success, non-0 on error
+ */
+int disorder_exists(disorder_client *c, const char *track, int *existsp);
+
+/** @brief List files in a directory
+ *
+ *
+ *
+ * @param c Client
+ * @param dir Directory to list (optional)
+ * @param re Regexp that results must match (optional)
+ * @param filesp List of matching files
+ * @param nfilesp Number of elements in filesp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_files(disorder_client *c, const char *dir, const char *re, char ***filesp, int *nfilesp);
+
+/** @brief Get a track preference
+ *
+ * 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.
+ *
+ * @param c Client
+ * @param track Track name
+ * @param pref Preference name
+ * @param valuep Preference value
+ * @return 0 on success, non-0 on error
+ */
+int disorder_get(disorder_client *c, const char *track, const char *pref, char **valuep);
+
+/** @brief Get a global preference
+ *
+ * If the preference does exist not then a null value is returned.
+ *
+ * @param c Client
+ * @param pref Global preference name
+ * @param valuep Preference value
+ * @return 0 on success, non-0 on error
+ */
+int disorder_get_global(disorder_client *c, const char *pref, char **valuep);
+
+/** @brief Get a track's length
+ *
+ * If the track does not exist an error is returned.
+ *
+ * @param c Client
+ * @param track Track name
+ * @param lengthp Track length in seconds
+ * @return 0 on success, non-0 on error
+ */
+int disorder_length(disorder_client *c, const char *track, long *lengthp);
+
+/** @brief Create a login cookie for this user
+ *
+ * The cookie may be redeemed via the 'cookie' command
+ *
+ * @param c Client
+ * @param cookiep Newly created cookie
+ * @return 0 on success, non-0 on error
+ */
+int disorder_make_cookie(disorder_client *c, char **cookiep);
+
+/** @brief Move a track
+ *
+ * Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.
+ *
+ * @param c Client
+ * @param track Track ID or name
+ * @param delta How far to move the track towards the head of the queue
+ * @return 0 on success, non-0 on error
+ */
+int disorder_move(disorder_client *c, const char *track, long delta);
+
+/** @brief Move multiple tracks
+ *
+ * Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.
+ *
+ * @param c Client
+ * @param target Move after this track, or to head if ""
+ * @param ids List of tracks to move by ID
+ * @param nids Length of ids
+ * @return 0 on success, non-0 on error
+ */
+int disorder_moveafter(disorder_client *c, const char *target, char **ids, int nids);
+
+/** @brief List recently added tracks
+ *
+ *
+ *
+ * @param c Client
+ * @param max Maximum tracks to fetch, or 0 for all available
+ * @param tracksp Recently added tracks
+ * @param ntracksp Number of elements in tracksp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_new_tracks(disorder_client *c, long max, char ***tracksp, int *ntracksp);
+
+/** @brief Do nothing
+ *
+ * Used as a keepalive. No authentication required.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_nop(disorder_client *c);
+
+/** @brief Get a track name part
+ *
+ * If the name part cannot be constructed an empty string is returned.
+ *
+ * @param c Client
+ * @param track Track name
+ * @param context Context ("sort" or "display")
+ * @param part Name part ("artist", "album" or "title")
+ * @param partp Value of name part
+ * @return 0 on success, non-0 on error
+ */
+int disorder_part(disorder_client *c, const char *track, const char *context, const char *part, char **partp);
+
+/** @brief Pause the currently playing track
+ *
+ * Requires the 'pause' right.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_pause(disorder_client *c);
+
+/** @brief Play a track
+ *
+ * Requires the 'play' right.
+ *
+ * @param c Client
+ * @param track Track to play
+ * @param idp Queue ID of new track
+ * @return 0 on success, non-0 on error
+ */
+int disorder_play(disorder_client *c, const char *track, char **idp);
+
+/** @brief Play multiple tracks
+ *
+ * Requires the 'play' right.
+ *
+ * @param c Client
+ * @param target Insert into queue after this track, or at head if ""
+ * @param tracks List of track names to play
+ * @param ntracks Length of tracks
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playafter(disorder_client *c, const char *target, char **tracks, int ntracks);
+
+/** @brief Retrieve the playing track
+ *
+ *
+ *
+ * @param c Client
+ * @param playingp Details of the playing track
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playing(disorder_client *c, struct queue_entry **playingp);
+
+/** @brief Delete a playlist
+ *
+ * Requires the 'play' right and permission to modify the playlist.
+ *
+ * @param c Client
+ * @param playlist Playlist to delete
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_delete(disorder_client *c, const char *playlist);
+
+/** @brief List the contents of a playlist
+ *
+ * Requires the 'read' right and oermission to read the playlist.
+ *
+ * @param c Client
+ * @param playlist Playlist name
+ * @param tracksp List of tracks in playlist
+ * @param ntracksp Number of elements in tracksp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_get(disorder_client *c, const char *playlist, char ***tracksp, int *ntracksp);
+
+/** @brief Get a playlist's sharing status
+ *
+ * Requires the 'read' right and permission to read the playlist.
+ *
+ * @param c Client
+ * @param playlist Playlist to read
+ * @param sharep Sharing status ("public", "private" or "shared")
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_get_share(disorder_client *c, const char *playlist, char **sharep);
+
+/** @brief Lock a playlist
+ *
+ * Requires the 'play' right and permission to modify the playlist. A given connection may lock at most one playlist.
+ *
+ * @param c Client
+ * @param playlist Playlist to delete
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_lock(disorder_client *c, const char *playlist);
+
+/** @brief Set the contents of a playlist
+ *
+ * Requires the 'play' right and permission to modify the playlist, which must be locked.
+ *
+ * @param c Client
+ * @param playlist Playlist to modify
+ * @param tracks New list of tracks for playlist
+ * @param ntracks Length of tracks
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_set(disorder_client *c, const char *playlist, char **tracks, int ntracks);
+
+/** @brief Set a playlist's sharing status
+ *
+ * Requires the 'play' right and permission to modify the playlist.
+ *
+ * @param c Client
+ * @param playlist Playlist to modify
+ * @param share New sharing status ("public", "private" or "shared")
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_set_share(disorder_client *c, const char *playlist, const char *share);
+
+/** @brief Unlock the locked playlist playlist
+ *
+ * The playlist to unlock is implicit in the connection.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlist_unlock(disorder_client *c);
+
+/** @brief List playlists
+ *
+ * Requires the 'read' right. Only playlists that you have permission to read are returned.
+ *
+ * @param c Client
+ * @param playlistsp Playlist names
+ * @param nplaylistsp Number of elements in playlistsp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_playlists(disorder_client *c, char ***playlistsp, int *nplaylistsp);
+
+/** @brief Get all the preferences for a track
+ *
+ *
+ *
+ * @param c Client
+ * @param track Track name
+ * @param prefsp Track preferences
+ * @return 0 on success, non-0 on error
+ */
+int disorder_prefs(disorder_client *c, const char *track, struct kvp **prefsp);
+
+/** @brief List the queue
+ *
+ *
+ *
+ * @param c Client
+ * @param queuep Current queue contents
+ * @return 0 on success, non-0 on error
+ */
+int disorder_queue(disorder_client *c, struct queue_entry **queuep);
+
+/** @brief Disable random play
+ *
+ * Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_random_disable(disorder_client *c);
+
+/** @brief Enable random play
+ *
+ * Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_random_enable(disorder_client *c);
+
+/** @brief Detect whether random play is enabled
+ *
+ * Random play counts as enabled even if play is disabled.
+ *
+ * @param c Client
+ * @param enabledp 1 if random play is enabled and 0 otherwise
+ * @return 0 on success, non-0 on error
+ */
+int disorder_random_enabled(disorder_client *c, int *enabledp);
+
+/** @brief List recently played tracks
+ *
+ *
+ *
+ * @param c Client
+ * @param recentp Recently played tracks
+ * @return 0 on success, non-0 on error
+ */
+int disorder_recent(disorder_client *c, struct queue_entry **recentp);
+
+/** @brief Re-read configuraiton file.
+ *
+ * Requires the 'admin' right.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_reconfigure(disorder_client *c);
+
+/** @brief Register a new user
+ *
+ * Requires the 'register' right which is usually only available to the 'guest' user. Redeem the confirmation string via 'confirm' to complete registration.
+ *
+ * @param c Client
+ * @param username Requested new username
+ * @param password Requested initial password
+ * @param email New user's email address
+ * @param confirmationp Confirmation string
+ * @return 0 on success, non-0 on error
+ */
+int disorder_register(disorder_client *c, const char *username, const char *password, const char *email, char **confirmationp);
+
+/** @brief Send a password reminder.
+ *
+ * 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.
+ *
+ * @param c Client
+ * @param username User to remind
+ * @return 0 on success, non-0 on error
+ */
+int disorder_reminder(disorder_client *c, const char *username);
+
+/** @brief Remove a track form the queue.
+ *
+ * Requires one of the 'remove mine', 'remove random' or 'remove any' rights depending on how the track came to be added to the queue.
+ *
+ * @param c Client
+ * @param id Track ID
+ * @return 0 on success, non-0 on error
+ */
+int disorder_remove(disorder_client *c, const char *id);
+
+/** @brief Rescan all collections for new or obsolete tracks.
+ *
+ * Requires the 'rescan' right.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_rescan(disorder_client *c);
+
+/** @brief Resolve a track name
+ *
+ * Converts aliases to non-alias track names
+ *
+ * @param c Client
+ * @param track Track name (might be an alias)
+ * @param resolvedp Resolve track name (definitely not an alias)
+ * @return 0 on success, non-0 on error
+ */
+int disorder_resolve(disorder_client *c, const char *track, char **resolvedp);
+
+/** @brief Resume the currently playing track
+ *
+ * Requires the 'pause' right.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_resume(disorder_client *c);
+
+/** @brief Revoke a cookie.
+ *
+ * It will not subsequently be possible to log in with the cookie.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_revoke(disorder_client *c);
+
+/** @brief Get the server's RTP address information
+ *
+ *
+ *
+ * @param c Client
+ * @param addressp Where to store hostname or address
+ * @param portp Where to store service name or port number
+ * @return 0 on success, non-0 on error
+ */
+int disorder_rtp_address(disorder_client *c, char **addressp, char **portp);
+
+/** @brief Terminate the playing track.
+ *
+ * Requires one of the 'scratch mine', 'scratch random' or 'scratch any' rights depending on how the track came to be added to the queue.
+ *
+ * @param c Client
+ * @param id Track ID (optional)
+ * @return 0 on success, non-0 on error
+ */
+int disorder_scratch(disorder_client *c, const char *id);
+
+/** @brief Schedule a track to play in the future
+ *
+ *
+ *
+ * @param c Client
+ * @param when When to play the track
+ * @param priority Event priority ("normal" or "junk")
+ * @param track Track to play
+ * @return 0 on success, non-0 on error
+ */
+int disorder_schedule_add_play(disorder_client *c, time_t when, const char *priority, const char *track);
+
+/** @brief Schedule a global setting to be changed in the future
+ *
+ *
+ *
+ * @param c Client
+ * @param when When to change the setting
+ * @param priority Event priority ("normal" or "junk")
+ * @param pref Global preference to set
+ * @param value New value of global preference
+ * @return 0 on success, non-0 on error
+ */
+int disorder_schedule_add_set_global(disorder_client *c, time_t when, const char *priority, const char *pref, const char *value);
+
+/** @brief Schedule a global setting to be unset in the future
+ *
+ *
+ *
+ * @param c Client
+ * @param when When to change the setting
+ * @param priority Event priority ("normal" or "junk")
+ * @param pref Global preference to set
+ * @return 0 on success, non-0 on error
+ */
+int disorder_schedule_add_unset_global(disorder_client *c, time_t when, const char *priority, const char *pref);
+
+/** @brief Delete a scheduled event.
+ *
+ * Users can always delete their own scheduled events; with the admin right you can delete any event.
+ *
+ * @param c Client
+ * @param event ID of event to delete
+ * @return 0 on success, non-0 on error
+ */
+int disorder_schedule_del(disorder_client *c, const char *event);
+
+/** @brief Get the details of scheduled event
+ *
+ *
+ *
+ * @param c Client
+ * @param id Event ID
+ * @param actiondatap Details of event
+ * @return 0 on success, non-0 on error
+ */
+int disorder_schedule_get(disorder_client *c, const char *id, struct kvp **actiondatap);
+
+/** @brief List scheduled events
+ *
+ * This just lists IDs. Use 'schedule-get' to retrieve more detail
+ *
+ * @param c Client
+ * @param idsp List of event IDs
+ * @param nidsp Number of elements in idsp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_schedule_list(disorder_client *c, char ***idsp, int *nidsp);
+
+/** @brief Search for tracks
+ *
+ * Terms are either keywords or tags formatted as 'tag:TAG-NAME'.
+ *
+ * @param c Client
+ * @param terms List of search terms
+ * @param tracksp List of matching tracks
+ * @param ntracksp Number of elements in tracksp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_search(disorder_client *c, const char *terms, char ***tracksp, int *ntracksp);
+
+/** @brief Set a track preference
+ *
+ * Requires the 'prefs' right.
+ *
+ * @param c Client
+ * @param track Track name
+ * @param pref Preference name
+ * @param value New value
+ * @return 0 on success, non-0 on error
+ */
+int disorder_set(disorder_client *c, const char *track, const char *pref, const char *value);
+
+/** @brief Set a global preference
+ *
+ * Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @param pref Preference name
+ * @param value New value
+ * @return 0 on success, non-0 on error
+ */
+int disorder_set_global(disorder_client *c, const char *pref, const char *value);
+
+/** @brief Request server shutdown
+ *
+ * Requires the 'admin' right.
+ *
+ * @param c Client
+ * @return 0 on success, non-0 on error
+ */
+int disorder_shutdown(disorder_client *c);
+
+/** @brief Get server statistics
+ *
+ * The details of what the server reports are not really defined. The returned strings are intended to be printed out one to a line.
+ *
+ * @param c Client
+ * @param statsp List of server information strings.
+ * @param nstatsp Number of elements in statsp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_stats(disorder_client *c, char ***statsp, int *nstatsp);
+
+/** @brief Get a list of known tags
+ *
+ * Only tags which apply to at least one track are returned.
+ *
+ * @param c Client
+ * @param tagsp List of tags
+ * @param ntagsp Number of elements in tagsp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_tags(disorder_client *c, char ***tagsp, int *ntagsp);
+
+/** @brief Unset a track preference
+ *
+ * Requires the 'prefs' right.
+ *
+ * @param c Client
+ * @param track Track name
+ * @param pref Preference name
+ * @return 0 on success, non-0 on error
+ */
+int disorder_unset(disorder_client *c, const char *track, const char *pref);
+
+/** @brief Set a global preference
+ *
+ * Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @param pref Preference name
+ * @return 0 on success, non-0 on error
+ */
+int disorder_unset_global(disorder_client *c, const char *pref);
+
+/** @brief Get a user property.
+ *
+ * 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.
+ *
+ * @param c Client
+ * @param username User to read
+ * @param property Property to read
+ * @param valuep Value of property
+ * @return 0 on success, non-0 on error
+ */
+int disorder_userinfo(disorder_client *c, const char *username, const char *property, char **valuep);
+
+/** @brief Get a list of users
+ *
+ *
+ *
+ * @param c Client
+ * @param usersp List of users
+ * @param nusersp Number of elements in usersp
+ * @return 0 on success, non-0 on error
+ */
+int disorder_users(disorder_client *c, char ***usersp, int *nusersp);
+
+/** @brief Get the server version
+ *
+ *
+ *
+ * @param c Client
+ * @param versionp Server version string
+ * @return 0 on success, non-0 on error
+ */
+int disorder_version(disorder_client *c, char **versionp);
+
+/** @brief Set the volume
+ *
+ *
+ *
+ * @param c Client
+ * @param left Left channel volume
+ * @param right Right channel volume
+ * @return 0 on success, non-0 on error
+ */
+int disorder_set_volume(disorder_client *c, long left, long right);
+
+/** @brief Get the volume
+ *
+ *
+ *
+ * @param c Client
+ * @param leftp Left channel volume
+ * @param rightp Right channel volume
+ * @return 0 on success, non-0 on error
+ */
+int disorder_get_volume(disorder_client *c, long *leftp, long *rightp);
+
+#endif
/*
* This file is part of DisOrder.
- * Copyright (C) 2004-2009 Richard Kettlewell
+ * Copyright (C) 2004-2010 Richard Kettlewell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* @param c Client
* @param rp Where to store result, or NULL
* @param cmd Command
- * @param body Body or NULL
- * @param nbody Length of body or -1
* @param ap Arguments (UTF-8), terminated by (char *)0
* @return 0 on success, non-0 on error
*
* NB that the response will NOT be converted to the local encoding
* nor will quotes be stripped. See dequote().
*
- * If @p body is not NULL then the body is sent immediately after the
- * command. @p nbody should be the number of lines or @c -1 to count
- * them if @p body is NULL-terminated.
+ * Put @ref disorder__body in the argument list followed by a char **
+ * and int giving the body to follow the command. If the int is @c -1
+ * then the list is assumed to be NULL-terminated. This may be used
+ * only once.
+ *
+ * Put @ref disorder__list in the argument list followed by a char **
+ * and int giving a list of arguments to include. If the int is @c -1
+ * then the list is assumed to be NULL-terminated. This may be used
+ * any number of times.
+ *
+ * Put @ref disorder__integer in the argument list followed by a long to
+ * send its value in decimal. This may be used any number of times.
+ *
+ * Put @ref disorder__time in the argument list followed by a time_t
+ * to send its value in decimal. This may be used any number of
+ * times.
*
* Usually you would call this via one of the following interfaces:
* - disorder_simple()
- * - disorder_simple_body()
- * - disorder_simple_list()
*/
static int disorder_simple_v(disorder_client *c,
char **rp,
const char *cmd,
- char **body, int nbody,
va_list ap) {
const char *arg;
struct dynstr d;
+ char **body = NULL;
+ int nbody = 0;
+ int has_body = 0;
if(!c->fpout) {
c->last = "not connected";
dynstr_init(&d);
dynstr_append_string(&d, cmd);
while((arg = va_arg(ap, const char *))) {
- dynstr_append(&d, ' ');
- dynstr_append_string(&d, quoteutf8(arg));
+ if(arg == disorder__body) {
+ body = va_arg(ap, char **);
+ nbody = va_arg(ap, int);
+ has_body = 1;
+ } else if(arg == disorder__list) {
+ char **list = va_arg(ap, char **);
+ int nlist = va_arg(ap, int);
+ if(nlist < 0) {
+ for(nlist = 0; list[nlist]; ++nlist)
+ ;
+ }
+ for(int n = 0; n < nlist; ++n) {
+ dynstr_append(&d, ' ');
+ dynstr_append_string(&d, quoteutf8(arg));
+ }
+ } else if(arg == disorder__integer) {
+ long n = va_arg(ap, long);
+ char buffer[16];
+ snprintf(buffer, sizeof buffer, "%ld", n);
+ dynstr_append(&d, ' ');
+ dynstr_append_string(&d, buffer);
+ } else if(arg == disorder__time) {
+ time_t n = va_arg(ap, time_t);
+ char buffer[16];
+ snprintf(buffer, sizeof buffer, "%lld", (long long)n);
+ dynstr_append(&d, ' ');
+ dynstr_append_string(&d, buffer);
+ } else {
+ dynstr_append(&d, ' ');
+ dynstr_append_string(&d, quoteutf8(arg));
+ }
}
dynstr_append(&d, '\n');
dynstr_terminate(&d);
if(fputs(d.vec, c->fpout) < 0)
goto write_error;
xfree(d.vec);
- if(body) {
+ if(has_body) {
if(nbody < 0)
for(nbody = 0; body[nbody]; ++nbody)
;
int ret;
va_start(ap, cmd);
- ret = disorder_simple_v(c, rp, cmd, 0, 0, ap);
+ ret = disorder_simple_v(c, rp, cmd, ap);
va_end(ap);
return ret;
}
-/** @brief Issue a command with a body and parse a simple response
+/** @brief Issue a command and split the response
* @param c Client
- * @param rp Where to store result, or NULL (UTF-8)
- * @param body Pointer to body
- * @param nbody Size of body
+ * @param vecp Where to store results
+ * @param nvecp Where to store count of results
+ * @param expected Expected count (or -1 to not check)
* @param cmd Command
* @return 0 on success, non-0 on error
*
- * See disorder_simple().
+ * The remaining arguments are command arguments, terminated by (char
+ * *)0. They should be in UTF-8.
+ *
+ * 5xx responses count as errors.
+ *
+ * @p rp will NOT be filled in for xx9 responses (where it is just
+ * commentary for a command where it would normally be meaningful).
+ *
+ * NB that the response will NOT be converted to the local encoding
+ * nor will quotes be stripped. See dequote().
*/
-static int disorder_simple_body(disorder_client *c,
- char **rp,
- char **body, int nbody,
- const char *cmd, ...) {
+static int disorder_simple_split(disorder_client *c,
+ char ***vecp,
+ int *nvecp,
+ int expected,
+ const char *cmd, ...) {
va_list ap;
int ret;
+ char *r;
+ char **vec;
+ int nvec;
va_start(ap, cmd);
- ret = disorder_simple_v(c, rp, cmd, body, nbody, ap);
+ ret = disorder_simple_v(c, &r, cmd, ap);
va_end(ap);
+ if(!ret) {
+ vec = split(r, &nvec, SPLIT_QUOTES, 0, 0);
+ xfree(r);
+ if(expected < 0 || nvec == expected) {
+ *vecp = vec;
+ *nvecp = nvec;
+ } else {
+ disorder_error(0, "malformed reply to %s", cmd);
+ c->last = "malformed reply";
+ ret = -1;
+ free_strings(nvec, vec);
+ }
+ }
+ if(ret) {
+ *vecp = NULL;
+ *nvecp = 0;
+ }
return ret;
}
return ret;
}
-/** @brief Play a track
- * @param c Client
- * @param track Track to play (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_play(disorder_client *c, const char *track) {
- return disorder_simple(c, 0, "play", track, (char *)0);
-}
-
-/** @brief Remove a track
- * @param c Client
- * @param track Track to remove (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_remove(disorder_client *c, const char *track) {
- return disorder_simple(c, 0, "remove", track, (char *)0);
-}
-
-/** @brief Move a track
- * @param c Client
- * @param track Track to move (UTF-8)
- * @param delta Distance to move by
- * @return 0 on success, non-0 on error
- */
-int disorder_move(disorder_client *c, const char *track, int delta) {
- char d[16];
-
- byte_snprintf(d, sizeof d, "%d", delta);
- return disorder_simple(c, 0, "move", track, d, (char *)0);
-}
-
-/** @brief Enable play
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_enable(disorder_client *c) {
- return disorder_simple(c, 0, "enable", (char *)0);
-}
-
-/** @brief Disable play
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_disable(disorder_client *c) {
- return disorder_simple(c, 0, "disable", (char *)0);
-}
-
-/** @brief Scratch the currently playing track
- * @param id Playing track ID or NULL (UTF-8)
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_scratch(disorder_client *c, const char *id) {
- return disorder_simple(c, 0, "scratch", id, (char *)0);
-}
-
-/** @brief Shut down the server
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_shutdown(disorder_client *c) {
- return disorder_simple(c, 0, "shutdown", (char *)0);
-}
-
-/** @brief Make the server re-read its configuration
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_reconfigure(disorder_client *c) {
- return disorder_simple(c, 0, "reconfigure", (char *)0);
-}
-
-/** @brief Rescan tracks
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_rescan(disorder_client *c) {
- return disorder_simple(c, 0, "rescan", (char *)0);
-}
-
-/** @brief Get server version number
- * @param c Client
- * @param rp Where to store version string (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_version(disorder_client *c, char **rp) {
- return dequote(disorder_simple(c, rp, "version", (char *)0), rp);
-}
-
static void client_error(const char *msg,
void attribute((unused)) *u) {
disorder_error(0, "error parsing reply: %s", msg);
}
-/** @brief Get currently playing track
+/** @brief Get a single queue entry
* @param c Client
+ * @param cmd Command
* @param qp Where to store track information
* @return 0 on success, non-0 on error
- *
- * @p qp gets NULL if no track is playing.
*/
-int disorder_playing(disorder_client *c, struct queue_entry **qp) {
+static int onequeue(disorder_client *c, const char *cmd,
+ struct queue_entry **qp) {
char *r;
struct queue_entry *q;
int rc;
- if((rc = disorder_simple(c, &r, "playing", (char *)0)))
+ if((rc = disorder_simple(c, &r, cmd, (char *)0)))
return rc;
if(r) {
q = xmalloc(sizeof *q);
}
/** @brief Fetch the queue, recent list, etc */
-static int disorder_somequeue(disorder_client *c,
- const char *cmd, struct queue_entry **qp) {
+static int readqueue(disorder_client *c,
+ struct queue_entry **qp) {
struct queue_entry *qh, **qt = &qh, *q;
char *l;
- int rc;
- if((rc = disorder_simple(c, 0, cmd, (char *)0)))
- return rc;
while(inputline(c->ident, c->fpin, &l, '\n') >= 0) {
if(!strcmp(l, ".")) {
*qt = 0;
byte_xasprintf((char **)&c->last, "input error: %s", strerror(errno));
disorder_error(errno, "error reading %s", c->ident);
} else {
- c->last = "input error: unexpxected EOF";
+ c->last = "input error: unexpected EOF";
disorder_error(0, "error reading %s: unexpected EOF", c->ident);
}
return -1;
}
-/** @brief Get recently played tracks
- * @param c Client
- * @param qp Where to store track information
- * @return 0 on success, non-0 on error
- *
- * The last entry in the list is the most recently played track.
- */
-int disorder_recent(disorder_client *c, struct queue_entry **qp) {
- return disorder_somequeue(c, "recent", qp);
-}
-
-/** @brief Get queue
- * @param c Client
- * @param qp Where to store track information
- * @return 0 on success, non-0 on error
- *
- * The first entry in the list will be played next.
- */
-int disorder_queue(disorder_client *c, struct queue_entry **qp) {
- return disorder_somequeue(c, "queue", qp);
-}
-
/** @brief Read a dot-stuffed list
* @param c Client
* @param vecp Where to store list (UTF-8)
return -1;
}
-/** @brief Issue a comamnd and get a list response
- * @param c Client
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @param cmd Command
- * @return 0 on success, non-0 on error
- *
- * The remaining arguments are command arguments, terminated by (char
- * *)0. They should be in UTF-8.
- *
- * 5xx responses count as errors.
- *
- * See disorder_simple().
- */
-static int disorder_simple_list(disorder_client *c,
- char ***vecp, int *nvecp,
- const char *cmd, ...) {
- va_list ap;
- int ret;
-
- va_start(ap, cmd);
- ret = disorder_simple_v(c, 0, cmd, 0, 0, ap);
- va_end(ap);
- if(ret) return ret;
- return readlist(c, vecp, nvecp);
-}
-
-/** @brief List directories below @p dir
- * @param c Client
- * @param dir Directory to list, or NULL for root (UTF-8)
- * @param re Regexp that results must match, or NULL (UTF-8)
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_directories(disorder_client *c, const char *dir, const char *re,
- char ***vecp, int *nvecp) {
- return disorder_simple_list(c, vecp, nvecp, "dirs", dir, re, (char *)0);
-}
-
-/** @brief List files below @p dir
- * @param c Client
- * @param dir Directory to list, or NULL for root (UTF-8)
- * @param re Regexp that results must match, or NULL (UTF-8)
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_files(disorder_client *c, const char *dir, const char *re,
- char ***vecp, int *nvecp) {
- return disorder_simple_list(c, vecp, nvecp, "files", dir, re, (char *)0);
-}
-
-/** @brief List files and directories below @p dir
- * @param c Client
- * @param dir Directory to list, or NULL for root (UTF-8)
- * @param re Regexp that results must match, or NULL (UTF-8)
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_allfiles(disorder_client *c, const char *dir, const char *re,
- char ***vecp, int *nvecp) {
- return disorder_simple_list(c, vecp, nvecp, "allfiles", dir, re, (char *)0);
-}
-
/** @brief Return the user we logged in with
* @param c Client
* @return User name (owned by @p c, don't modify)
return c->user;
}
-/** @brief Set a track preference
- * @param c Client
- * @param track Track name (UTF-8)
- * @param key Preference name (UTF-8)
- * @param value Preference value (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_set(disorder_client *c, const char *track,
- const char *key, const char *value) {
- return disorder_simple(c, 0, "set", track, key, value, (char *)0);
-}
-
-/** @brief Unset a track preference
- * @param c Client
- * @param track Track name (UTF-8)
- * @param key Preference name (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_unset(disorder_client *c, const char *track,
- const char *key) {
- return disorder_simple(c, 0, "unset", track, key, (char *)0);
-}
-
-/** @brief Get a track preference
- * @param c Client
- * @param track Track name (UTF-8)
- * @param key Preference name (UTF-8)
- * @param valuep Where to store preference value (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_get(disorder_client *c,
- const char *track, const char *key, char **valuep) {
- return dequote(disorder_simple(c, valuep, "get", track, key, (char *)0),
- valuep);
-}
-
-static void pref_error_handler(const char *msg,
+static void pairlist_error_handler(const char *msg,
void attribute((unused)) *u) {
- disorder_error(0, "error handling 'prefs' reply: %s", msg);
+ disorder_error(0, "error handling key-value pair reply: %s", msg);
}
-/** @brief Get all preferences for a trcak
+/** @brief Get a list of key-value pairs
* @param c Client
- * @param track Track name
* @param kp Where to store linked list of preferences
+ * @param cmd Command
+ * @param ... Arguments
* @return 0 on success, non-0 on error
*/
-int disorder_prefs(disorder_client *c, const char *track, struct kvp **kp) {
+static int pairlist(disorder_client *c, struct kvp **kp, const char *cmd, ...) {
char **vec, **pvec;
int nvec, npvec, n, rc;
struct kvp *k;
+ va_list ap;
- if((rc = disorder_simple_list(c, &vec, &nvec, "prefs", track, (char *)0)))
+ va_start(ap, cmd);
+ rc = disorder_simple_v(c, 0, cmd, ap);
+ va_end(ap);
+ if(rc)
return rc;
+ if((rc = readlist(c, &vec, &nvec)))
+ return rc;
for(n = 0; n < nvec; ++n) {
- if(!(pvec = split(vec[n], &npvec, SPLIT_QUOTES, pref_error_handler, 0)))
+ if(!(pvec = split(vec[n], &npvec, SPLIT_QUOTES, pairlist_error_handler, 0)))
return -1;
if(npvec != 2) {
- pref_error_handler("malformed response", 0);
+ pairlist_error_handler("malformed response", 0);
return -1;
}
*kp = k = xmalloc(sizeof *k);
return 0;
}
-/** @brief Test whether a track exists
- * @param c Client
- * @param track Track name (UTF-8)
- * @param existsp Where to store result (non-0 iff does exist)
- * @return 0 on success, non-0 on error
- */
-int disorder_exists(disorder_client *c, const char *track, int *existsp) {
- char *v;
- int rc;
-
- if((rc = disorder_simple(c, &v, "exists", track, (char *)0)))
- return rc;
- return boolean("exists", v, existsp);
-}
-
-/** @brief Test whether playing is enabled
- * @param c Client
- * @param enabledp Where to store result (non-0 iff enabled)
- * @return 0 on success, non-0 on error
- */
-int disorder_enabled(disorder_client *c, int *enabledp) {
- char *v;
- int rc;
-
- if((rc = disorder_simple(c, &v, "enabled", (char *)0)))
- return rc;
- return boolean("enabled", v, enabledp);
-}
-
-/** @brief Get the length of a track
- * @param c Client
- * @param track Track name (UTF-8)
- * @param valuep Where to store length in seconds
- * @return 0 on success, non-0 on error
- *
- * If the length is unknown 0 is returned.
- */
-int disorder_length(disorder_client *c, const char *track,
- long *valuep) {
- char *value;
- int rc;
-
- if((rc = disorder_simple(c, &value, "length", track, (char *)0)))
- return rc;
- *valuep = atol(value);
- return 0;
-}
-
-/** @brief Search for tracks
- * @param c Client
- * @param terms Search terms (UTF-8)
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_search(disorder_client *c, const char *terms,
- char ***vecp, int *nvecp) {
- return disorder_simple_list(c, vecp, nvecp, "search", terms, (char *)0);
-}
-
-/** @brief Enable random play
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_random_enable(disorder_client *c) {
- return disorder_simple(c, 0, "random-enable", (char *)0);
-}
-
-/** @brief Disable random play
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_random_disable(disorder_client *c) {
- return disorder_simple(c, 0, "random-disable", (char *)0);
-}
-
-/** @brief Test whether random play is enabled
- * @param c Client
- * @param enabledp Where to store result (non-0 iff enabled)
- * @return 0 on success, non-0 on error
- */
-int disorder_random_enabled(disorder_client *c, int *enabledp) {
- char *v;
- int rc;
-
- if((rc = disorder_simple(c, &v, "random-enabled", (char *)0)))
- return rc;
- return boolean("random-enabled", v, enabledp);
-}
-
-/** @brief Get server stats
- * @param c Client
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_stats(disorder_client *c,
- char ***vecp, int *nvecp) {
- return disorder_simple_list(c, vecp, nvecp, "stats", (char *)0);
-}
-
-/** @brief Set volume
- * @param c Client
- * @param left New left channel value
- * @param right New right channel value
- * @return 0 on success, non-0 on error
- */
-int disorder_set_volume(disorder_client *c, int left, int right) {
- char *ls, *rs;
-
- if(byte_asprintf(&ls, "%d", left) < 0
- || byte_asprintf(&rs, "%d", right) < 0)
- return -1;
- return disorder_simple(c, 0, "volume", ls, rs, (char *)0);
-}
-
-/** @brief Get volume
- * @param c Client
- * @param left Where to store left channel value
- * @param right Where to store right channel value
- * @return 0 on success, non-0 on error
- */
-int disorder_get_volume(disorder_client *c, int *left, int *right) {
- char *r;
- int rc;
-
- if((rc = disorder_simple(c, &r, "volume", (char *)0)))
- return rc;
- if(sscanf(r, "%d %d", left, right) != 2) {
- c->last = "malformed volume response";
- disorder_error(0, "error parsing response to 'volume': '%s'", r);
- return -1;
- }
- return 0;
-}
-
/** @brief Log to a sink
* @param c Client
* @param s Sink to write log lines to
return 0;
}
-/** @brief Look up a track name part
- * @param c Client
- * @param partp Where to store result (UTF-8)
- * @param track Track name (UTF-8)
- * @param context Context (usually "sort" or "display") (UTF-8)
- * @param part Track part (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_part(disorder_client *c, char **partp,
- const char *track, const char *context, const char *part) {
- return dequote(disorder_simple(c, partp, "part",
- track, context, part, (char *)0), partp);
-}
-
-/** @brief Resolve aliases
- * @param c Client
- * @param trackp Where to store canonical name (UTF-8)
- * @param track Track name (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_resolve(disorder_client *c, char **trackp, const char *track) {
- return dequote(disorder_simple(c, trackp, "resolve", track, (char *)0),
- trackp);
-}
-
-/** @brief Pause the current track
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_pause(disorder_client *c) {
- return disorder_simple(c, 0, "pause", (char *)0);
-}
-
-/** @brief Resume the current track
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_resume(disorder_client *c) {
- return disorder_simple(c, 0, "resume", (char *)0);
-}
-
-/** @brief List all known tags
- * @param c Client
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_tags(disorder_client *c,
- char ***vecp, int *nvecp) {
- return disorder_simple_list(c, vecp, nvecp, "tags", (char *)0);
-}
-
-/** @brief List all known users
- * @param c Client
- * @param vecp Where to store list (UTF-8)
- * @param nvecp Where to store number of items, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_users(disorder_client *c,
- char ***vecp, int *nvecp) {
- return disorder_simple_list(c, vecp, nvecp, "users", (char *)0);
-}
-
-/** @brief Get recently added tracks
- * @param c Client
- * @param vecp Where to store pointer to list (UTF-8)
- * @param nvecp Where to store count
- * @param max Maximum tracks to fetch, or 0 for all available
- * @return 0 on success, non-0 on error
- */
-int disorder_new_tracks(disorder_client *c,
- char ***vecp, int *nvecp,
- int max) {
- char limit[32];
-
- sprintf(limit, "%d", max);
- return disorder_simple_list(c, vecp, nvecp, "new", limit, (char *)0);
-}
-
-/** @brief Set a global preference
- * @param c Client
- * @param key Preference name (UTF-8)
- * @param value Preference value (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_set_global(disorder_client *c,
- const char *key, const char *value) {
- return disorder_simple(c, 0, "set-global", key, value, (char *)0);
-}
-
-/** @brief Unset a global preference
- * @param c Client
- * @param key Preference name (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_unset_global(disorder_client *c, const char *key) {
- return disorder_simple(c, 0, "unset-global", key, (char *)0);
-}
-
-/** @brief Get a global preference
- * @param c Client
- * @param key Preference name (UTF-8)
- * @param valuep Where to store preference value (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_get_global(disorder_client *c, const char *key, char **valuep) {
- return dequote(disorder_simple(c, valuep, "get-global", key, (char *)0),
- valuep);
-}
-
-/** @brief Get server's RTP address information
- * @param c Client
- * @param addressp Where to store address (UTF-8)
- * @param portp Where to store port (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_rtp_address(disorder_client *c, char **addressp, char **portp) {
- char *r;
- int rc, n;
- char **vec;
-
- if((rc = disorder_simple(c, &r, "rtp-address", (char *)0)))
- return rc;
- vec = split(r, &n, SPLIT_QUOTES, 0, 0);
- if(n != 2) {
- c->last = "malformed RTP address";
- disorder_error(0, "malformed rtp-address reply");
- return -1;
- }
- *addressp = vec[0];
- *portp = vec[1];
- return 0;
-}
-
-/** @brief Create a user
- * @param c Client
- * @param user Username
- * @param password Password
- * @param rights Initial rights or NULL to use default
- * @return 0 on success, non-0 on error
- */
-int disorder_adduser(disorder_client *c,
- const char *user, const char *password,
- const char *rights) {
- return disorder_simple(c, 0, "adduser", user, password, rights, (char *)0);
-}
-
-/** @brief Delete a user
- * @param c Client
- * @param user Username
- * @return 0 on success, non-0 on error
- */
-int disorder_deluser(disorder_client *c, const char *user) {
- return disorder_simple(c, 0, "deluser", user, (char *)0);
-}
-
-/** @brief Get user information
- * @param c Client
- * @param user Username
- * @param key Property name (UTF-8)
- * @param valuep Where to store value (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_userinfo(disorder_client *c, const char *user, const char *key,
- char **valuep) {
- return dequote(disorder_simple(c, valuep, "userinfo", user, key, (char *)0),
- valuep);
-}
-
-/** @brief Set user information
- * @param c Client
- * @param user Username
- * @param key Property name (UTF-8)
- * @param value New property value (UTF-8)
- * @return 0 on success, non-0 on error
- */
-int disorder_edituser(disorder_client *c, const char *user,
- const char *key, const char *value) {
- return disorder_simple(c, 0, "edituser", user, key, value, (char *)0);
-}
-
-/** @brief Register a user
- * @param c Client
- * @param user Username
- * @param password Password
- * @param email Email address (UTF-8)
- * @param confirmp Where to store confirmation string
- * @return 0 on success, non-0 on error
- */
-int disorder_register(disorder_client *c, const char *user,
- const char *password, const char *email,
- char **confirmp) {
- return dequote(disorder_simple(c, confirmp, "register",
- user, password, email, (char *)0),
- confirmp);
-}
-
-/** @brief Confirm a user
- * @param c Client
- * @param confirm Confirmation string
- * @return 0 on success, non-0 on error
- */
-int disorder_confirm(disorder_client *c, const char *confirm) {
- char *u;
- int rc;
-
- if(!(rc = dequote(disorder_simple(c, &u, "confirm", confirm, (char *)0),
- &u)))
- c->user = u;
- return rc;
-}
-
-/** @brief Make a cookie for this login
- * @param c Client
- * @param cookiep Where to store cookie string
- * @return 0 on success, non-0 on error
- */
-int disorder_make_cookie(disorder_client *c, char **cookiep) {
- return dequote(disorder_simple(c, cookiep, "make-cookie", (char *)0),
- cookiep);
-}
-
-/** @brief Revoke the cookie used by this session
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_revoke(disorder_client *c) {
- return disorder_simple(c, 0, "revoke", (char *)0);
-}
-
-/** @brief Request a password reminder email
- * @param c Client
- * @param user Username
- * @return 0 on success, non-0 on error
- */
-int disorder_reminder(disorder_client *c, const char *user) {
- return disorder_simple(c, 0, "reminder", user, (char *)0);
-}
-
-/** @brief List scheduled events
- * @param c Client
- * @param idsp Where to put list of event IDs
- * @param nidsp Where to put count of event IDs, or NULL
- * @return 0 on success, non-0 on error
- */
-int disorder_schedule_list(disorder_client *c, char ***idsp, int *nidsp) {
- return disorder_simple_list(c, idsp, nidsp, "schedule-list", (char *)0);
-}
-
-/** @brief Delete a scheduled event
- * @param c Client
- * @param id Event ID to delete
- * @return 0 on success, non-0 on error
- */
-int disorder_schedule_del(disorder_client *c, const char *id) {
- return disorder_simple(c, 0, "schedule-del", id, (char *)0);
-}
-
-/** @brief Get details of a scheduled event
- * @param c Client
- * @param id Event ID
- * @param actiondatap Where to put details
- * @return 0 on success, non-0 on error
- */
-int disorder_schedule_get(disorder_client *c, const char *id,
- struct kvp **actiondatap) {
- char **lines, **bits;
- int rc, nbits;
-
- *actiondatap = 0;
- if((rc = disorder_simple_list(c, &lines, NULL,
- "schedule-get", id, (char *)0)))
- return rc;
- while(*lines) {
- if(!(bits = split(*lines++, &nbits, SPLIT_QUOTES, 0, 0))) {
- disorder_error(0, "invalid schedule-get reply: cannot split line");
- return -1;
- }
- if(nbits != 2) {
- disorder_error(0, "invalid schedule-get reply: wrong number of fields");
- return -1;
- }
- kvp_set(actiondatap, bits[0], bits[1]);
- }
- return 0;
-}
-
-/** @brief Add a scheduled event
- * @param c Client
- * @param when When to trigger the event
- * @param priority Event priority ("normal" or "junk")
- * @param action What action to perform
- * @param ... Action-specific arguments
- * @return 0 on success, non-0 on error
- *
- * For action @c "play" the next argument is the track.
- *
- * For action @c "set-global" next argument is the global preference name
- * and the final argument the value to set it to, or (char *)0 to unset it.
- */
-int disorder_schedule_add(disorder_client *c,
- time_t when,
- const char *priority,
- const char *action,
- ...) {
- va_list ap;
- char when_str[64];
- int rc;
-
- snprintf(when_str, sizeof when_str, "%lld", (long long)when);
- va_start(ap, action);
- if(!strcmp(action, "play"))
- rc = disorder_simple(c, 0, "schedule-add", when_str, priority,
- action, va_arg(ap, char *),
- (char *)0);
- else if(!strcmp(action, "set-global")) {
- const char *key = va_arg(ap, char *);
- const char *value = va_arg(ap, char *);
- rc = disorder_simple(c, 0,"schedule-add", when_str, priority,
- action, key, value,
- (char *)0);
- } else
- disorder_fatal(0, "unknown action '%s'", action);
- va_end(ap);
- return rc;
-}
-
-/** @brief Adopt a track
- * @param c Client
- * @param id Track ID to adopt
- * @return 0 on success, non-0 on error
- */
-int disorder_adopt(disorder_client *c, const char *id) {
- return disorder_simple(c, 0, "adopt", id, (char *)0);
-}
-
-/** @brief Delete a playlist
- * @param c Client
- * @param playlist Playlist to delete
- * @return 0 on success, non-0 on error
- */
-int disorder_playlist_delete(disorder_client *c,
- const char *playlist) {
- return disorder_simple(c, 0, "playlist-delete", playlist, (char *)0);
-}
-
-/** @brief Get the contents of a playlist
- * @param c Client
- * @param playlist Playlist to get
- * @param tracksp Where to put list of tracks
- * @param ntracksp Where to put count of tracks
- * @return 0 on success, non-0 on error
- */
-int disorder_playlist_get(disorder_client *c, const char *playlist,
- char ***tracksp, int *ntracksp) {
- return disorder_simple_list(c, tracksp, ntracksp,
- "playlist-get", playlist, (char *)0);
-}
-
-/** @brief List all readable playlists
- * @param c Client
- * @param playlistsp Where to put list of playlists
- * @param nplaylistsp Where to put count of playlists
- * @return 0 on success, non-0 on error
- */
-int disorder_playlists(disorder_client *c,
- char ***playlistsp, int *nplaylistsp) {
- return disorder_simple_list(c, playlistsp, nplaylistsp,
- "playlists", (char *)0);
-}
-
-/** @brief Get the sharing status of a playlist
- * @param c Client
- * @param playlist Playlist to inspect
- * @param sharep Where to put sharing status
- * @return 0 on success, non-0 on error
- *
- * Possible @p sharep values are @c public, @c private and @c shared.
- */
-int disorder_playlist_get_share(disorder_client *c, const char *playlist,
- char **sharep) {
- return disorder_simple(c, sharep,
- "playlist-get-share", playlist, (char *)0);
-}
-
-/** @brief Get the sharing status of a playlist
- * @param c Client
- * @param playlist Playlist to modify
- * @param share New sharing status
- * @return 0 on success, non-0 on error
- *
- * Possible @p share values are @c public, @c private and @c shared.
- */
-int disorder_playlist_set_share(disorder_client *c, const char *playlist,
- const char *share) {
- return disorder_simple(c, 0,
- "playlist-set-share", playlist, share, (char *)0);
-}
-
-/** @brief Lock a playlist for modifications
- * @param c Client
- * @param playlist Playlist to lock
- * @return 0 on success, non-0 on error
- */
-int disorder_playlist_lock(disorder_client *c, const char *playlist) {
- return disorder_simple(c, 0,
- "playlist-lock", playlist, (char *)0);
-}
-
-/** @brief Unlock the locked playlist
- * @param c Client
- * @return 0 on success, non-0 on error
- */
-int disorder_playlist_unlock(disorder_client *c) {
- return disorder_simple(c, 0,
- "playlist-unlock", (char *)0);
-}
-
-/** @brief Set the contents of a playlst
- * @param c Client
- * @param playlist Playlist to modify
- * @param tracks List of tracks
- * @param ntracks Length of @p tracks (or -1 to count up to the first NULL)
- * @return 0 on success, non-0 on error
- */
-int disorder_playlist_set(disorder_client *c,
- const char *playlist,
- char **tracks,
- int ntracks) {
- return disorder_simple_body(c, 0, tracks, ntracks,
- "playlist-set", playlist, (char *)0);
-}
+#include "client-stubs.c"
/*
Local Variables:
/** @file lib/client.h
* @brief Simple C client
*
+ * See @ref lib/client-stubs.h for the (generated) per-command entry
+ * points.
+ *
* See @ref lib/eclient.h for an asynchronous-capable client
* implementation.
*/
const char *password,
const char *cookie);
int disorder_close(disorder_client *c);
-int disorder_version(disorder_client *c, char **versionp);
-int disorder_play(disorder_client *c, const char *track);
-int disorder_remove(disorder_client *c, const char *track);
-int disorder_move(disorder_client *c, const char *track, int delta);
-int disorder_enable(disorder_client *c);
-int disorder_disable(disorder_client *c);
-int disorder_scratch(disorder_client *c, const char *id);
-int disorder_shutdown(disorder_client *c);
-int disorder_reconfigure(disorder_client *c);
-int disorder_rescan(disorder_client *c);
-int disorder_playing(disorder_client *c, struct queue_entry **qp);
-int disorder_recent(disorder_client *c, struct queue_entry **qp);
-int disorder_queue(disorder_client *c, struct queue_entry **qp);
-int disorder_directories(disorder_client *c, const char *dir, const char *re,
- char ***vecp, int *nvecp);
-int disorder_files(disorder_client *c, const char *dir, const char *re,
- char ***vecp, int *nvecp);
-int disorder_allfiles(disorder_client *c, const char *dir, const char *re,
- char ***vecp, int *nvecp);
char *disorder_user(disorder_client *c);
-int disorder_exists(disorder_client *c, const char *track, int *existsp);
-int disorder_enabled(disorder_client *c, int *enabledp);
-int disorder_set(disorder_client *c, const char *track,
- const char *key, const char *value);
-int disorder_unset(disorder_client *c, const char *track,
- const char *key);
-int disorder_get(disorder_client *c, const char *track, const char *key,
- char **valuep);
-int disorder_prefs(disorder_client *c, const char *track,
- struct kvp **kp);
-int disorder_length(disorder_client *c, const char *track,
- long *valuep);
-int disorder_search(disorder_client *c, const char *terms,
- char ***vecp, int *nvecp);
-int disorder_random_enable(disorder_client *c);
-int disorder_random_disable(disorder_client *c);
-int disorder_random_enabled(disorder_client *c, int *enabledp);
-int disorder_stats(disorder_client *c,
- char ***vecp, int *nvecp);
-int disorder_set_volume(disorder_client *c, int left, int right);
-int disorder_get_volume(disorder_client *c, int *left, int *right);
int disorder_log(disorder_client *c, struct sink *s);
-int disorder_part(disorder_client *c, char **partp,
- const char *track, const char *context, const char *part);
-int disorder_resolve(disorder_client *c, char **trackp, const char *track);
-int disorder_pause(disorder_client *c);
-int disorder_resume(disorder_client *c);
-int disorder_tags(disorder_client *c,
- char ***vecp, int *nvecp);
-int disorder_set_global(disorder_client *c,
- const char *key, const char *value);
-int disorder_unset_global(disorder_client *c, const char *key);
-int disorder_get_global(disorder_client *c, const char *key, char **valuep);
-int disorder_new_tracks(disorder_client *c,
- char ***vecp, int *nvecp,
- int max);
-int disorder_rtp_address(disorder_client *c, char **addressp, char **portp);
-int disorder_adduser(disorder_client *c,
- const char *user, const char *password,
- const char *rights);
-int disorder_deluser(disorder_client *c, const char *user);
-int disorder_userinfo(disorder_client *c, const char *user, const char *key,
- char **valuep);
-int disorder_edituser(disorder_client *c, const char *user,
- const char *key, const char *value);
-int disorder_users(disorder_client *c,
- char ***vecp, int *nvecp);
-int disorder_register(disorder_client *c, const char *user,
- const char *password, const char *email,
- char **confirmp);
-int disorder_confirm(disorder_client *c, const char *confirm);
-int disorder_make_cookie(disorder_client *c, char **cookiep);
const char *disorder_last(disorder_client *c);
-int disorder_revoke(disorder_client *c);
-int disorder_reminder(disorder_client *c, const char *user);
-int disorder_schedule_list(disorder_client *c, char ***idsp, int *nidsp);
-int disorder_schedule_del(disorder_client *c, const char *id);
-int disorder_schedule_get(disorder_client *c, const char *id,
- struct kvp **actiondatap);
-int disorder_schedule_add(disorder_client *c,
- time_t when,
- const char *priority,
- const char *action,
- ...);
-int disorder_adopt(disorder_client *c, const char *id);
-int disorder_playlist_delete(disorder_client *c,
- const char *playlist);
-int disorder_playlist_get(disorder_client *c, const char *playlist,
- char ***tracksp, int *ntracksp);
-int disorder_playlists(disorder_client *c,
- char ***playlistsp, int *nplaylists);
-int disorder_playlist_get_share(disorder_client *c, const char *playlist,
- char **sharep);
-int disorder_playlist_set_share(disorder_client *c, const char *playlist,
- const char *share);
-int disorder_playlist_lock(disorder_client *c, const char *playlist);
-int disorder_playlist_unlock(disorder_client *c);
-int disorder_playlist_set(disorder_client *c,
- const char *playlist,
- char **tracks,
- int ntracks);
+
+#include "client-stubs.h"
#endif /* CLIENT_H */
--- /dev/null
+/*
+ * Automatically generated file, see scripts/protocol
+ *
+ * DO NOT EDIT.
+ */
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2010-11 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+/** @file lib/client-stubs.c
+ * @brief Generated asynchronous client API implementation
+ */
+
+int disorder_eclient_adopt(disorder_eclient *c, disorder_eclient_no_response *completed, const char *id, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "adopt", id, (char *)0);
+}
+
+int disorder_eclient_adduser(disorder_eclient *c, disorder_eclient_no_response *completed, const char *user, const char *password, const char *rights, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "adduser", user, password, rights, (char *)0);
+}
+
+int disorder_eclient_allfiles(disorder_eclient *c, disorder_eclient_list_response *completed, const char *dir, const char *re, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "allfiles", dir, re, (char *)0);
+}
+
+int disorder_eclient_deluser(disorder_eclient *c, disorder_eclient_no_response *completed, const char *user, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "deluser", user, (char *)0);
+}
+
+int disorder_eclient_dirs(disorder_eclient *c, disorder_eclient_list_response *completed, const char *dir, const char *re, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "dirs", dir, re, (char *)0);
+}
+
+int disorder_eclient_disable(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "disable", (char *)0);
+}
+
+int disorder_eclient_edituser(disorder_eclient *c, disorder_eclient_no_response *completed, const char *username, const char *property, const char *value, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "edituser", username, property, value, (char *)0);
+}
+
+int disorder_eclient_enable(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "enable", (char *)0);
+}
+
+int disorder_eclient_enabled(disorder_eclient *c, disorder_eclient_integer_response *completed, void *v) {
+ return simple(c, integer_response_opcallback, (void (*)())completed, v, "enabled", (char *)0);
+}
+
+int disorder_eclient_exists(disorder_eclient *c, disorder_eclient_integer_response *completed, const char *track, void *v) {
+ return simple(c, integer_response_opcallback, (void (*)())completed, v, "exists", track, (char *)0);
+}
+
+int disorder_eclient_files(disorder_eclient *c, disorder_eclient_list_response *completed, const char *dir, const char *re, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "files", dir, re, (char *)0);
+}
+
+int disorder_eclient_get(disorder_eclient *c, disorder_eclient_string_response *completed, const char *track, const char *pref, void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v, "get", track, pref, (char *)0);
+}
+
+int disorder_eclient_get_global(disorder_eclient *c, disorder_eclient_string_response *completed, const char *pref, void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v, "get-global", pref, (char *)0);
+}
+
+int disorder_eclient_length(disorder_eclient *c, disorder_eclient_integer_response *completed, const char *track, void *v) {
+ return simple(c, integer_response_opcallback, (void (*)())completed, v, "length", track, (char *)0);
+}
+
+int disorder_eclient_make_cookie(disorder_eclient *c, disorder_eclient_string_response *completed, void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v, "make-cookie", (char *)0);
+}
+
+int disorder_eclient_move(disorder_eclient *c, disorder_eclient_no_response *completed, const char *track, long delta, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "move", track, disorder__integer, delta, (char *)0);
+}
+
+int disorder_eclient_moveafter(disorder_eclient *c, disorder_eclient_no_response *completed, const char *target, char **ids, int nids, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "moveafter", target, disorder__list, ids, nids, (char *)0);
+}
+
+int disorder_eclient_new_tracks(disorder_eclient *c, disorder_eclient_list_response *completed, long max, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "new", disorder__integer, max, (char *)0);
+}
+
+int disorder_eclient_nop(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "nop", (char *)0);
+}
+
+int disorder_eclient_part(disorder_eclient *c, disorder_eclient_string_response *completed, const char *track, const char *context, const char *part, void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v, "part", track, context, part, (char *)0);
+}
+
+int disorder_eclient_pause(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "pause", (char *)0);
+}
+
+int disorder_eclient_play(disorder_eclient *c, disorder_eclient_string_response *completed, const char *track, void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v, "play", track, (char *)0);
+}
+
+int disorder_eclient_playafter(disorder_eclient *c, disorder_eclient_no_response *completed, const char *target, char **tracks, int ntracks, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "playafter", target, disorder__list, tracks, ntracks, (char *)0);
+}
+
+int disorder_eclient_playing(disorder_eclient *c, disorder_eclient_playing_response *completed, void *v) {
+ return simple(c, playing_response_opcallback, (void (*)())completed, v, "playing", (char *)0);
+}
+
+int disorder_eclient_playlist_delete(disorder_eclient *c, disorder_eclient_no_response *completed, const char *playlist, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "playlist-delete", playlist, (char *)0);
+}
+
+int disorder_eclient_playlist_get(disorder_eclient *c, disorder_eclient_list_response *completed, const char *playlist, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "playlist-get", playlist, (char *)0);
+}
+
+int disorder_eclient_playlist_get_share(disorder_eclient *c, disorder_eclient_string_response *completed, const char *playlist, void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v, "playlist-get-share", playlist, (char *)0);
+}
+
+int disorder_eclient_playlist_lock(disorder_eclient *c, disorder_eclient_no_response *completed, const char *playlist, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "playlist-lock", playlist, (char *)0);
+}
+
+int disorder_eclient_playlist_set(disorder_eclient *c, disorder_eclient_no_response *completed, const char *playlist, char **tracks, int ntracks, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "playlist-set", playlist, disorder__body, tracks, ntracks, (char *)0);
+}
+
+int disorder_eclient_playlist_set_share(disorder_eclient *c, disorder_eclient_no_response *completed, const char *playlist, const char *share, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "playlist-set-share", playlist, share, (char *)0);
+}
+
+int disorder_eclient_playlist_unlock(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "playlist-unlock", (char *)0);
+}
+
+int disorder_eclient_playlists(disorder_eclient *c, disorder_eclient_list_response *completed, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "playlists", (char *)0);
+}
+
+int disorder_eclient_queue(disorder_eclient *c, disorder_eclient_queue_response *completed, void *v) {
+ return simple(c, queue_response_opcallback, (void (*)())completed, v, "queue", (char *)0);
+}
+
+int disorder_eclient_random_disable(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "random-disable", (char *)0);
+}
+
+int disorder_eclient_random_enable(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "random-enable", (char *)0);
+}
+
+int disorder_eclient_random_enabled(disorder_eclient *c, disorder_eclient_integer_response *completed, void *v) {
+ return simple(c, integer_response_opcallback, (void (*)())completed, v, "random-enabled", (char *)0);
+}
+
+int disorder_eclient_recent(disorder_eclient *c, disorder_eclient_queue_response *completed, void *v) {
+ return simple(c, queue_response_opcallback, (void (*)())completed, v, "recent", (char *)0);
+}
+
+int disorder_eclient_reconfigure(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "reconfigure", (char *)0);
+}
+
+int disorder_eclient_register(disorder_eclient *c, disorder_eclient_string_response *completed, const char *username, const char *password, const char *email, void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v, "register", username, password, email, (char *)0);
+}
+
+int disorder_eclient_reminder(disorder_eclient *c, disorder_eclient_no_response *completed, const char *username, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "reminder", username, (char *)0);
+}
+
+int disorder_eclient_remove(disorder_eclient *c, disorder_eclient_no_response *completed, const char *id, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "remove", id, (char *)0);
+}
+
+int disorder_eclient_rescan(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "rescan", (char *)0);
+}
+
+int disorder_eclient_resolve(disorder_eclient *c, disorder_eclient_string_response *completed, const char *track, void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v, "resolve", track, (char *)0);
+}
+
+int disorder_eclient_resume(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "resume", (char *)0);
+}
+
+int disorder_eclient_revoke(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "revoke", (char *)0);
+}
+
+int disorder_eclient_scratch(disorder_eclient *c, disorder_eclient_no_response *completed, const char *id, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "scratch", id, (char *)0);
+}
+
+int disorder_eclient_schedule_add_play(disorder_eclient *c, disorder_eclient_no_response *completed, time_t when, const char *priority, const char *track, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "schedule-add", disorder__time, when, priority, "play", track, (char *)0);
+}
+
+int disorder_eclient_schedule_add_set_global(disorder_eclient *c, disorder_eclient_no_response *completed, time_t when, const char *priority, const char *pref, const char *value, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "schedule-add", disorder__time, when, priority, "set-global", pref, value, (char *)0);
+}
+
+int disorder_eclient_schedule_add_unset_global(disorder_eclient *c, disorder_eclient_no_response *completed, time_t when, const char *priority, const char *pref, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "schedule-add", disorder__time, when, priority, "set-global", pref, (char *)0);
+}
+
+int disorder_eclient_schedule_del(disorder_eclient *c, disorder_eclient_no_response *completed, const char *event, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "schedule-del", event, (char *)0);
+}
+
+int disorder_eclient_schedule_list(disorder_eclient *c, disorder_eclient_list_response *completed, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "schedule-list", (char *)0);
+}
+
+int disorder_eclient_search(disorder_eclient *c, disorder_eclient_list_response *completed, const char *terms, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "search", terms, (char *)0);
+}
+
+int disorder_eclient_set(disorder_eclient *c, disorder_eclient_no_response *completed, const char *track, const char *pref, const char *value, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "set", track, pref, value, (char *)0);
+}
+
+int disorder_eclient_set_global(disorder_eclient *c, disorder_eclient_no_response *completed, const char *pref, const char *value, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "set-global", pref, value, (char *)0);
+}
+
+int disorder_eclient_shutdown(disorder_eclient *c, disorder_eclient_no_response *completed, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "shutdown", (char *)0);
+}
+
+int disorder_eclient_stats(disorder_eclient *c, disorder_eclient_list_response *completed, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "stats", (char *)0);
+}
+
+int disorder_eclient_tags(disorder_eclient *c, disorder_eclient_list_response *completed, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "tags", (char *)0);
+}
+
+int disorder_eclient_unset(disorder_eclient *c, disorder_eclient_no_response *completed, const char *track, const char *pref, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "unset", track, pref, (char *)0);
+}
+
+int disorder_eclient_unset_global(disorder_eclient *c, disorder_eclient_no_response *completed, const char *pref, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "unset-global", pref, (char *)0);
+}
+
+int disorder_eclient_userinfo(disorder_eclient *c, disorder_eclient_string_response *completed, const char *username, const char *property, void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v, "userinfo", username, property, (char *)0);
+}
+
+int disorder_eclient_users(disorder_eclient *c, disorder_eclient_list_response *completed, void *v) {
+ return simple(c, list_response_opcallback, (void (*)())completed, v, "users", (char *)0);
+}
+
+int disorder_eclient_version(disorder_eclient *c, disorder_eclient_string_response *completed, void *v) {
+ return simple(c, string_response_opcallback, (void (*)())completed, v, "version", (char *)0);
+}
+
+int disorder_eclient_set_volume(disorder_eclient *c, disorder_eclient_no_response *completed, long left, long right, void *v) {
+ return simple(c, no_response_opcallback, (void (*)())completed, v, "volume", disorder__integer, left, disorder__integer, right, (char *)0);
+}
+
+int disorder_eclient_get_volume(disorder_eclient *c, disorder_eclient_pair_integer_response *completed, void *v) {
+ return simple(c, pair_integer_response_opcallback, (void (*)())completed, v, "volume", (char *)0);
+}
+
--- /dev/null
+/*
+ * Automatically generated file, see scripts/protocol
+ *
+ * DO NOT EDIT.
+ */
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2010-11 Richard Kettlewell
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef ECLIENT_STUBS_H
+#define ECLIENT_STUBS_H
+/** @file lib/client-stubs.h
+ * @brief Generated asynchronous client API
+ *
+ * Don't include this file directly - use @ref client.h instead.
+ */
+
+/** @brief Adopt a track
+ *
+ * Makes the calling user owner of a randomly picked track.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param id Track ID
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_adopt(disorder_eclient *c, disorder_eclient_no_response *completed, const char *id, void *v);
+
+/** @brief Create a user
+ *
+ * Create a new user. Requires the 'admin' right. Email addresses etc must be filled in in separate commands.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param user New username
+ * @param password Initial password
+ * @param rights Initial rights (optional)
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_adduser(disorder_eclient *c, disorder_eclient_no_response *completed, const char *user, const char *password, const char *rights, void *v);
+
+/** @brief List files and directories in a directory
+ *
+ * See 'files' and 'dirs' for more specific lists.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param dir Directory to list (optional)
+ * @param re Regexp that results must match (optional)
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_allfiles(disorder_eclient *c, disorder_eclient_list_response *completed, const char *dir, const char *re, void *v);
+
+/** @brief Delete user
+ *
+ * Requires the 'admin' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param user User to delete
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_deluser(disorder_eclient *c, disorder_eclient_no_response *completed, const char *user, void *v);
+
+/** @brief List directories in a directory
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param dir Directory to list (optional)
+ * @param re Regexp that results must match (optional)
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_dirs(disorder_eclient *c, disorder_eclient_list_response *completed, const char *dir, const char *re, void *v);
+
+/** @brief Disable play
+ *
+ * Play will stop at the end of the current track, if one is playing. Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_disable(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Set a user property
+ *
+ * With the 'admin' right you can do anything. Otherwise you need the 'userinfo' right and can only set 'email' and 'password'.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param username User to modify
+ * @param property Property name
+ * @param value New property value
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_edituser(disorder_eclient *c, disorder_eclient_no_response *completed, const char *username, const char *property, const char *value, void *v);
+
+/** @brief Enable play
+ *
+ * Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_enable(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Detect whether play is enabled
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_enabled(disorder_eclient *c, disorder_eclient_integer_response *completed, void *v);
+
+/** @brief Test whether a track exists
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param track Track name
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_exists(disorder_eclient *c, disorder_eclient_integer_response *completed, const char *track, void *v);
+
+/** @brief List files in a directory
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param dir Directory to list (optional)
+ * @param re Regexp that results must match (optional)
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_files(disorder_eclient *c, disorder_eclient_list_response *completed, const char *dir, const char *re, void *v);
+
+/** @brief Get a track preference
+ *
+ * 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.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param track Track name
+ * @param pref Preference name
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_get(disorder_eclient *c, disorder_eclient_string_response *completed, const char *track, const char *pref, void *v);
+
+/** @brief Get a global preference
+ *
+ * If the preference does exist not then a null value is returned.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param pref Global preference name
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_get_global(disorder_eclient *c, disorder_eclient_string_response *completed, const char *pref, void *v);
+
+/** @brief Get a track's length
+ *
+ * If the track does not exist an error is returned.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param track Track name
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_length(disorder_eclient *c, disorder_eclient_integer_response *completed, const char *track, void *v);
+
+/** @brief Create a login cookie for this user
+ *
+ * The cookie may be redeemed via the 'cookie' command
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_make_cookie(disorder_eclient *c, disorder_eclient_string_response *completed, void *v);
+
+/** @brief Move a track
+ *
+ * Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param track Track ID or name
+ * @param delta How far to move the track towards the head of the queue
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_move(disorder_eclient *c, disorder_eclient_no_response *completed, const char *track, long delta, void *v);
+
+/** @brief Move multiple tracks
+ *
+ * Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param target Move after this track, or to head if ""
+ * @param ids List of tracks to move by ID
+ * @param nids Length of ids
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_moveafter(disorder_eclient *c, disorder_eclient_no_response *completed, const char *target, char **ids, int nids, void *v);
+
+/** @brief List recently added tracks
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param max Maximum tracks to fetch, or 0 for all available
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_new_tracks(disorder_eclient *c, disorder_eclient_list_response *completed, long max, void *v);
+
+/** @brief Do nothing
+ *
+ * Used as a keepalive. No authentication required.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_nop(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Get a track name part
+ *
+ * If the name part cannot be constructed an empty string is returned.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param track Track name
+ * @param context Context ("sort" or "display")
+ * @param part Name part ("artist", "album" or "title")
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_part(disorder_eclient *c, disorder_eclient_string_response *completed, const char *track, const char *context, const char *part, void *v);
+
+/** @brief Pause the currently playing track
+ *
+ * Requires the 'pause' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_pause(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Play a track
+ *
+ * Requires the 'play' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param track Track to play
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_play(disorder_eclient *c, disorder_eclient_string_response *completed, const char *track, void *v);
+
+/** @brief Play multiple tracks
+ *
+ * Requires the 'play' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param target Insert into queue after this track, or at head if ""
+ * @param tracks List of track names to play
+ * @param ntracks Length of tracks
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_playafter(disorder_eclient *c, disorder_eclient_no_response *completed, const char *target, char **tracks, int ntracks, void *v);
+
+/** @brief Retrieve the playing track
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_playing(disorder_eclient *c, disorder_eclient_playing_response *completed, void *v);
+
+/** @brief Delete a playlist
+ *
+ * Requires the 'play' right and permission to modify the playlist.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param playlist Playlist to delete
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_playlist_delete(disorder_eclient *c, disorder_eclient_no_response *completed, const char *playlist, void *v);
+
+/** @brief List the contents of a playlist
+ *
+ * Requires the 'read' right and oermission to read the playlist.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param playlist Playlist name
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_playlist_get(disorder_eclient *c, disorder_eclient_list_response *completed, const char *playlist, void *v);
+
+/** @brief Get a playlist's sharing status
+ *
+ * Requires the 'read' right and permission to read the playlist.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param playlist Playlist to read
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_playlist_get_share(disorder_eclient *c, disorder_eclient_string_response *completed, const char *playlist, void *v);
+
+/** @brief Lock a playlist
+ *
+ * Requires the 'play' right and permission to modify the playlist. A given connection may lock at most one playlist.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param playlist Playlist to delete
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_playlist_lock(disorder_eclient *c, disorder_eclient_no_response *completed, const char *playlist, void *v);
+
+/** @brief Set the contents of a playlist
+ *
+ * Requires the 'play' right and permission to modify the playlist, which must be locked.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param playlist Playlist to modify
+ * @param tracks New list of tracks for playlist
+ * @param ntracks Length of tracks
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_playlist_set(disorder_eclient *c, disorder_eclient_no_response *completed, const char *playlist, char **tracks, int ntracks, void *v);
+
+/** @brief Set a playlist's sharing status
+ *
+ * Requires the 'play' right and permission to modify the playlist.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param playlist Playlist to modify
+ * @param share New sharing status ("public", "private" or "shared")
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_playlist_set_share(disorder_eclient *c, disorder_eclient_no_response *completed, const char *playlist, const char *share, void *v);
+
+/** @brief Unlock the locked playlist playlist
+ *
+ * The playlist to unlock is implicit in the connection.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_playlist_unlock(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief List playlists
+ *
+ * Requires the 'read' right. Only playlists that you have permission to read are returned.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_playlists(disorder_eclient *c, disorder_eclient_list_response *completed, void *v);
+
+/** @brief List the queue
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_queue(disorder_eclient *c, disorder_eclient_queue_response *completed, void *v);
+
+/** @brief Disable random play
+ *
+ * Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_random_disable(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Enable random play
+ *
+ * Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_random_enable(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Detect whether random play is enabled
+ *
+ * Random play counts as enabled even if play is disabled.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_random_enabled(disorder_eclient *c, disorder_eclient_integer_response *completed, void *v);
+
+/** @brief List recently played tracks
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_recent(disorder_eclient *c, disorder_eclient_queue_response *completed, void *v);
+
+/** @brief Re-read configuraiton file.
+ *
+ * Requires the 'admin' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_reconfigure(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Register a new user
+ *
+ * Requires the 'register' right which is usually only available to the 'guest' user. Redeem the confirmation string via 'confirm' to complete registration.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param username Requested new username
+ * @param password Requested initial password
+ * @param email New user's email address
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_register(disorder_eclient *c, disorder_eclient_string_response *completed, const char *username, const char *password, const char *email, void *v);
+
+/** @brief Send a password reminder.
+ *
+ * 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.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param username User to remind
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_reminder(disorder_eclient *c, disorder_eclient_no_response *completed, const char *username, void *v);
+
+/** @brief Remove a track form the queue.
+ *
+ * Requires one of the 'remove mine', 'remove random' or 'remove any' rights depending on how the track came to be added to the queue.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param id Track ID
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_remove(disorder_eclient *c, disorder_eclient_no_response *completed, const char *id, void *v);
+
+/** @brief Rescan all collections for new or obsolete tracks.
+ *
+ * Requires the 'rescan' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_rescan(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Resolve a track name
+ *
+ * Converts aliases to non-alias track names
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param track Track name (might be an alias)
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_resolve(disorder_eclient *c, disorder_eclient_string_response *completed, const char *track, void *v);
+
+/** @brief Resume the currently playing track
+ *
+ * Requires the 'pause' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_resume(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Revoke a cookie.
+ *
+ * It will not subsequently be possible to log in with the cookie.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_revoke(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Terminate the playing track.
+ *
+ * Requires one of the 'scratch mine', 'scratch random' or 'scratch any' rights depending on how the track came to be added to the queue.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param id Track ID (optional)
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_scratch(disorder_eclient *c, disorder_eclient_no_response *completed, const char *id, void *v);
+
+/** @brief Schedule a track to play in the future
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param when When to play the track
+ * @param priority Event priority ("normal" or "junk")
+ * @param track Track to play
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_schedule_add_play(disorder_eclient *c, disorder_eclient_no_response *completed, time_t when, const char *priority, const char *track, void *v);
+
+/** @brief Schedule a global setting to be changed in the future
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param when When to change the setting
+ * @param priority Event priority ("normal" or "junk")
+ * @param pref Global preference to set
+ * @param value New value of global preference
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_schedule_add_set_global(disorder_eclient *c, disorder_eclient_no_response *completed, time_t when, const char *priority, const char *pref, const char *value, void *v);
+
+/** @brief Schedule a global setting to be unset in the future
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param when When to change the setting
+ * @param priority Event priority ("normal" or "junk")
+ * @param pref Global preference to set
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_schedule_add_unset_global(disorder_eclient *c, disorder_eclient_no_response *completed, time_t when, const char *priority, const char *pref, void *v);
+
+/** @brief Delete a scheduled event.
+ *
+ * Users can always delete their own scheduled events; with the admin right you can delete any event.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param event ID of event to delete
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_schedule_del(disorder_eclient *c, disorder_eclient_no_response *completed, const char *event, void *v);
+
+/** @brief List scheduled events
+ *
+ * This just lists IDs. Use 'schedule-get' to retrieve more detail
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_schedule_list(disorder_eclient *c, disorder_eclient_list_response *completed, void *v);
+
+/** @brief Search for tracks
+ *
+ * Terms are either keywords or tags formatted as 'tag:TAG-NAME'.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param terms List of search terms
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_search(disorder_eclient *c, disorder_eclient_list_response *completed, const char *terms, void *v);
+
+/** @brief Set a track preference
+ *
+ * Requires the 'prefs' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param track Track name
+ * @param pref Preference name
+ * @param value New value
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_set(disorder_eclient *c, disorder_eclient_no_response *completed, const char *track, const char *pref, const char *value, void *v);
+
+/** @brief Set a global preference
+ *
+ * Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param pref Preference name
+ * @param value New value
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_set_global(disorder_eclient *c, disorder_eclient_no_response *completed, const char *pref, const char *value, void *v);
+
+/** @brief Request server shutdown
+ *
+ * Requires the 'admin' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_shutdown(disorder_eclient *c, disorder_eclient_no_response *completed, void *v);
+
+/** @brief Get server statistics
+ *
+ * The details of what the server reports are not really defined. The returned strings are intended to be printed out one to a line.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_stats(disorder_eclient *c, disorder_eclient_list_response *completed, void *v);
+
+/** @brief Get a list of known tags
+ *
+ * Only tags which apply to at least one track are returned.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_tags(disorder_eclient *c, disorder_eclient_list_response *completed, void *v);
+
+/** @brief Unset a track preference
+ *
+ * Requires the 'prefs' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param track Track name
+ * @param pref Preference name
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_unset(disorder_eclient *c, disorder_eclient_no_response *completed, const char *track, const char *pref, void *v);
+
+/** @brief Set a global preference
+ *
+ * Requires the 'global prefs' right.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param pref Preference name
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_unset_global(disorder_eclient *c, disorder_eclient_no_response *completed, const char *pref, void *v);
+
+/** @brief Get a user property.
+ *
+ * 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.
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param username User to read
+ * @param property Property to read
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_userinfo(disorder_eclient *c, disorder_eclient_string_response *completed, const char *username, const char *property, void *v);
+
+/** @brief Get a list of users
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_users(disorder_eclient *c, disorder_eclient_list_response *completed, void *v);
+
+/** @brief Get the server version
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_version(disorder_eclient *c, disorder_eclient_string_response *completed, void *v);
+
+/** @brief Set the volume
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param left Left channel volume
+ * @param right Right channel volume
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_set_volume(disorder_eclient *c, disorder_eclient_no_response *completed, long left, long right, void *v);
+
+/** @brief Get the volume
+ *
+ *
+ *
+ * @param c Client
+ * @param completed Called upon completion
+ * @param v Passed to @p completed
+ * @return 0 if the command was queued successfuly, non-0 on error
+ */
+int disorder_eclient_get_volume(disorder_eclient *c, disorder_eclient_pair_integer_response *completed, void *v);
+
+#endif
if(cmd) {
vector_init(&vec);
vector_append(&vec, (char *)cmd);
- while((arg = va_arg(ap, char *)))
- vector_append(&vec, arg);
+ while((arg = va_arg(ap, char *))) {
+ if(arg == disorder__list) {
+ char **list = va_arg(ap, char **);
+ int nlist = va_arg(ap, int);
+ if(nlist < 0) {
+ for(nlist = 0; list[nlist]; ++nlist)
+ ;
+ }
+ vector_append_many(&vec, list, nlist);
+ } else if(arg == disorder__body) {
+ body = va_arg(ap, char **);
+ nbody = va_arg(ap, int);
+ } else if(arg == disorder__integer) {
+ long n = va_arg(ap, long);
+ char buffer[16];
+ snprintf(buffer, sizeof buffer, "%ld", n);
+ vector_append(&vec, xstrdup(buffer));
+ } else if(arg == disorder__time) {
+ time_t n = va_arg(ap, time_t);
+ char buffer[16];
+ snprintf(buffer, sizeof buffer, "%lld", (long long)n);
+ vector_append(&vec, xstrdup(buffer));
+ } else
+ vector_append(&vec, arg);
+ }
stash_command_vector(c, queuejump, opcallback, completed, v,
nbody, body, vec.nvec, vec.vec);
} else
}
/* for volume */
-static void volume_response_opcallback(disorder_eclient *c,
- struct operation *op) {
- disorder_eclient_volume_response *completed
- = (disorder_eclient_volume_response *)op->completed;
- int l, r;
+static void pair_integer_response_opcallback(disorder_eclient *c,
+ struct operation *op) {
+ disorder_eclient_pair_integer_response *completed
+ = (disorder_eclient_pair_integer_response *)op->completed;
+ long l, r;
D(("volume_response_callback"));
if(c->rc / 100 == 2) {
if(op->completed) {
- if(sscanf(c->line + 4, "%d %d", &l, &r) != 2 || l < 0 || r < 0)
+ if(sscanf(c->line + 4, "%ld %ld", &l, &r) != 2 || l < 0 || r < 0)
completed(op->v, "cannot parse volume response", 0, 0);
else
completed(op->v, 0, l, r);
return 0;
}
-static int simple_body(disorder_eclient *c,
- operation_callback *opcallback,
- void (*completed)(),
- void *v,
- int nbody,
- char **body,
- const char *cmd, ...) {
- va_list ap;
-
- va_start(ap, cmd);
- vstash_command(c, 0/*queuejump*/, opcallback, completed, v, nbody, body, cmd, ap);
- va_end(ap);
- /* Give the state machine a kick, since we might be in state_idle */
- disorder_eclient_polled(c, 0);
- return 0;
-}
-
/* Commands ******************************************************************/
-
-int disorder_eclient_version(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- void *v) {
- return simple(c, string_response_opcallback, (void (*)())completed, v,
- "version", (char *)0);
-}
-
-int disorder_eclient_namepart(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *track,
- const char *context,
- const char *part,
- void *v) {
- return simple(c, string_response_opcallback, (void (*)())completed, v,
- "part", track, context, part, (char *)0);
-}
-
-int disorder_eclient_play(disorder_eclient *c,
- const char *track,
- disorder_eclient_no_response *completed,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "play", track, (char *)0);
-}
-
-int disorder_eclient_playafter(disorder_eclient *c,
- const char *target,
- int ntracks,
- const char **tracks,
- disorder_eclient_no_response *completed,
- void *v) {
- struct vector vec;
- int n;
-
- if(!target)
- target = "";
- vector_init(&vec);
- vector_append(&vec, (char *)"playafter");
- vector_append(&vec, (char *)target);
- for(n = 0; n < ntracks; ++n)
- vector_append(&vec, (char *)tracks[n]);
- stash_command_vector(c, 0/*queuejump*/, no_response_opcallback, completed, v,
- -1, 0, vec.nvec, vec.vec);
- disorder_eclient_polled(c, 0);
- return 0;
-}
-
-int disorder_eclient_pause(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "pause", (char *)0);
-}
-
-int disorder_eclient_resume(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "resume", (char *)0);
-}
-
-int disorder_eclient_scratch(disorder_eclient *c,
- const char *id,
- disorder_eclient_no_response *completed,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "scratch", id, (char *)0);
-}
int disorder_eclient_scratch_playing(disorder_eclient *c,
disorder_eclient_no_response *completed,
void *v) {
- return disorder_eclient_scratch(c, 0, completed, v);
-}
-
-int disorder_eclient_remove(disorder_eclient *c,
- const char *id,
- disorder_eclient_no_response *completed,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "remove", id, (char *)0);
-}
-
-int disorder_eclient_moveafter(disorder_eclient *c,
- const char *target,
- int nids,
- const char **ids,
- disorder_eclient_no_response *completed,
- void *v) {
- struct vector vec;
- int n;
-
- vector_init(&vec);
- vector_append(&vec, (char *)"moveafter");
- vector_append(&vec, (char *)target);
- for(n = 0; n < nids; ++n)
- vector_append(&vec, (char *)ids[n]);
- stash_command_vector(c, 0/*queuejump*/, no_response_opcallback, completed, v,
- -1, 0, vec.nvec, vec.vec);
- disorder_eclient_polled(c, 0);
- return 0;
-}
-
-int disorder_eclient_recent(disorder_eclient *c,
- disorder_eclient_queue_response *completed,
- void *v) {
- return simple(c, queue_response_opcallback, (void (*)())completed, v,
- "recent", (char *)0);
-}
-
-int disorder_eclient_queue(disorder_eclient *c,
- disorder_eclient_queue_response *completed,
- void *v) {
- return simple(c, queue_response_opcallback, (void (*)())completed, v,
- "queue", (char *)0);
-}
-
-int disorder_eclient_files(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- const char *dir,
- const char *re,
- void *v) {
- return simple(c, list_response_opcallback, (void (*)())completed, v,
- "files", dir, re, (char *)0);
-}
-
-int disorder_eclient_dirs(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- const char *dir,
- const char *re,
- void *v) {
- return simple(c, list_response_opcallback, (void (*)())completed, v,
- "dirs", dir, re, (char *)0);
-}
-
-int disorder_eclient_playing(disorder_eclient *c,
- disorder_eclient_queue_response *completed,
- void *v) {
- return simple(c, playing_response_opcallback, (void (*)())completed, v,
- "playing", (char *)0);
-}
-
-int disorder_eclient_length(disorder_eclient *c,
- disorder_eclient_integer_response *completed,
- const char *track,
- void *v) {
- return simple(c, integer_response_opcallback, (void (*)())completed, v,
- "length", track, (char *)0);
-}
-
-int disorder_eclient_volume(disorder_eclient *c,
- disorder_eclient_volume_response *completed,
- int l, int r,
- void *v) {
- char sl[64], sr[64];
-
- if(l < 0 && r < 0) {
- return simple(c, volume_response_opcallback, (void (*)())completed, v,
- "volume", (char *)0);
- } else if(l >= 0 && r >= 0) {
- assert(l <= 100);
- assert(r <= 100);
- byte_snprintf(sl, sizeof sl, "%d", l);
- byte_snprintf(sr, sizeof sr, "%d", r);
- return simple(c, volume_response_opcallback, (void (*)())completed, v,
- "volume", sl, sr, (char *)0);
- } else {
- assert(!"invalid arguments to disorder_eclient_volume");
- return -1; /* gcc is being dim */
- }
-}
-
-int disorder_eclient_enable(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "enable", (char *)0);
-}
-
-int disorder_eclient_disable(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v){
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "disable", (char *)0);
-}
-
-int disorder_eclient_random_enable(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v){
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "random-enable", (char *)0);
-}
-
-int disorder_eclient_random_disable(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v){
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "random-disable", (char *)0);
-}
-
-int disorder_eclient_get(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *track, const char *pref,
- void *v) {
- return simple(c, string_response_opcallback, (void (*)())completed, v,
- "get", track, pref, (char *)0);
-}
-
-int disorder_eclient_set(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *track, const char *pref,
- const char *value,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "set", track, pref, value, (char *)0);
-}
-
-int disorder_eclient_unset(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *track, const char *pref,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "unset", track, pref, (char *)0);
-}
-
-int disorder_eclient_resolve(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *track,
- void *v) {
- return simple(c, string_response_opcallback, (void (*)())completed, v,
- "resolve", track, (char *)0);
-}
-
-int disorder_eclient_search(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- const char *terms,
- void *v) {
- if(!split(terms, 0, SPLIT_QUOTES, 0, 0)) return -1;
- return simple(c, list_response_opcallback, (void (*)())completed, v,
- "search", terms, (char *)0);
-}
-
-int disorder_eclient_nop(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "nop", (char *)0);
-}
-
-int disorder_eclient_get_global(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *pref,
- void *v) {
- return simple(c, string_response_opcallback, (void (*)())completed, v,
- "get-global", pref, (char *)0);
-}
-
-int disorder_eclient_set_global(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *pref,
- const char *value,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "set-global", pref, value, (char *)0);
-}
-
-int disorder_eclient_unset_global(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *pref,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "unset-global", pref, (char *)0);
-}
-
-/** @brief Get the last @p max added tracks
- * @param c Client
- * @param completed Called with list
- * @param max Number of tracks to get, 0 for all
- * @param v Passed to @p completed
- *
- * The first track in the list is the most recently added.
- */
-int disorder_eclient_new_tracks(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- int max,
- void *v) {
- char limit[32];
-
- sprintf(limit, "%d", max);
- return simple(c, list_response_opcallback, (void (*)())completed, v,
- "new", limit, (char *)0);
+ return disorder_eclient_scratch(c, completed, 0, v);
}
static void rtp_response_opcallback(disorder_eclient *c,
"rtp-address", (char *)0);
}
-/** @brief Get the list of users
- * @param c Client
- * @param completed Called with list of users
- * @param v Passed to @p completed
- *
- * The user list is not sorted in any particular order.
- */
-int disorder_eclient_users(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- void *v) {
- return simple(c, list_response_opcallback, (void (*)())completed, v,
- "users", (char *)0);
-}
-
-/** @brief Delete a user
- * @param c Client
- * @param completed Called on completion
- * @param user User to delete
- * @param v Passed to @p completed
- */
-int disorder_eclient_deluser(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *user,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "deluser", user, (char *)0);
-}
-
-/** @brief Get a user property
- * @param c Client
- * @param completed Called on completion
- * @param user User to look up
- * @param property Property to look up
- * @param v Passed to @p completed
- */
-int disorder_eclient_userinfo(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *user,
- const char *property,
- void *v) {
- return simple(c, string_response_opcallback, (void (*)())completed, v,
- "userinfo", user, property, (char *)0);
-}
-
-/** @brief Modify a user property
- * @param c Client
- * @param completed Called on completion
- * @param user User to modify
- * @param property Property to modify
- * @param value New property value
- * @param v Passed to @p completed
- */
-int disorder_eclient_edituser(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *user,
- const char *property,
- const char *value,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "edituser", user, property, value, (char *)0);
-}
-
-/** @brief Create a new user
- * @param c Client
- * @param completed Called on completion
- * @param user User to create
- * @param password Initial password
- * @param rights Initial rights or NULL
- * @param v Passed to @p completed
- */
-int disorder_eclient_adduser(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *user,
- const char *password,
- const char *rights,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "adduser", user, password, rights, (char *)0);
-}
-
-/** @brief Adopt a track
- * @param c Client
- * @param completed Called on completion
- * @param id Track ID
- * @param v Passed to @p completed
- */
-int disorder_eclient_adopt(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *id,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "adopt", id, (char *)0);
-}
-
-/** @brief Get the list of playlists
- * @param c Client
- * @param completed Called with list of playlists
- * @param v Passed to @p completed
- *
- * The playlist list is not sorted in any particular order.
- */
-int disorder_eclient_playlists(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- void *v) {
- return simple(c, list_response_opcallback, (void (*)())completed, v,
- "playlists", (char *)0);
-}
-
-/** @brief Delete a playlist
- * @param c Client
- * @param completed Called on completion
- * @param playlist Playlist to delete
- * @param v Passed to @p completed
- */
-int disorder_eclient_playlist_delete(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *playlist,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "playlist-delete", playlist, (char *)0);
-}
-
-/** @brief Lock a playlist
- * @param c Client
- * @param completed Called on completion
- * @param playlist Playlist to lock
- * @param v Passed to @p completed
- */
-int disorder_eclient_playlist_lock(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *playlist,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "playlist-lock", playlist, (char *)0);
-}
-
-/** @brief Unlock the locked a playlist
- * @param c Client
- * @param completed Called on completion
- * @param v Passed to @p completed
- */
-int disorder_eclient_playlist_unlock(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "playlist-unlock", (char *)0);
-}
-
-/** @brief Set a playlist's sharing
- * @param c Client
- * @param completed Called on completion
- * @param playlist Playlist to modify
- * @param sharing @c "public" or @c "private"
- * @param v Passed to @p completed
- */
-int disorder_eclient_playlist_set_share(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *playlist,
- const char *sharing,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "playlist-set-share", playlist, sharing, (char *)0);
-}
-
-/** @brief Get a playlist's sharing
- * @param c Client
- * @param completed Called with sharing status
- * @param playlist Playlist to inspect
- * @param v Passed to @p completed
- */
-int disorder_eclient_playlist_get_share(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *playlist,
- void *v) {
- return simple(c, string_response_opcallback, (void (*)())completed, v,
- "playlist-get-share", playlist, (char *)0);
-}
-
-/** @brief Set a playlist
- * @param c Client
- * @param completed Called on completion
- * @param playlist Playlist to modify
- * @param tracks List of tracks
- * @param ntracks Number of tracks
- * @param v Passed to @p completed
- */
-int disorder_eclient_playlist_set(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *playlist,
- char **tracks,
- int ntracks,
- void *v) {
- return simple_body(c, no_response_opcallback, (void (*)())completed, v,
- ntracks, tracks,
- "playlist-set", playlist, (char *)0);
-}
-
-/** @brief Get a playlist's contents
- * @param c Client
- * @param completed Called with playlist contents
- * @param playlist Playlist to inspect
- * @param v Passed to @p completed
- */
-int disorder_eclient_playlist_get(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- const char *playlist,
- void *v) {
- return simple(c, list_response_opcallback, (void (*)())completed, v,
- "playlist-get", playlist, (char *)0);
-}
-
/* Log clients ***************************************************************/
/** @brief Monitor the server log
c->log_callbacks->global_pref(c->log_v, vec[0], nvec > 1 ? vec[1] : 0);
}
+#include "eclient-stubs.c"
+
/*
Local Variables:
c-basic-offset:2
*/
/** @file lib/eclient.h
* @brief Client code for event-driven programs
+ *
+ * See @ref lib/eclient-stubs.h for the (generated) per-command entry points.
*/
#ifndef ECLIENT_H
const char *err,
const char *value);
-/** @brief String result completion callback
+/** @brief Integer result completion callback
* @param v User data
* @param err Error string or NULL on succes
* @param value Result or 0
* @p error will be non-NULL on failure. In this case @p l and @p r are always
* 0.
*/
-typedef void disorder_eclient_volume_response(void *v,
- const char *err,
- int l, int r);
+typedef void disorder_eclient_pair_integer_response(void *v,
+ const char *err,
+ long l, long r);
/** @brief Queue request completion callback
* @param v User data
const char *err,
struct queue_entry *q);
+#define disorder_eclient_playing_response disorder_eclient_queue_response
+
/** @brief List request completion callback
* @param v User data
* @param err Error string or NULL on success
/* Should be called when c's FD is readable and/or writable, and in any case
* from time to time (so that retries work). */
-int disorder_eclient_version(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- void *v);
-/* fetch the server version */
-
-int disorder_eclient_play(disorder_eclient *c,
- const char *track,
- disorder_eclient_no_response *completed,
- void *v);
-/* add a track to the queue */
-
-int disorder_eclient_playafter(disorder_eclient *c,
- const char *target,
- int ntracks,
- const char **tracks,
- disorder_eclient_no_response *completed,
- void *v);
-/* insert multiple tracks to an arbitrary point in the queue */
-
-int disorder_eclient_pause(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v);
-/* add a track to the queue */
-
-int disorder_eclient_resume(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v);
-/* add a track to the queue */
-
-int disorder_eclient_scratch(disorder_eclient *c,
- const char *id,
- disorder_eclient_no_response *completed,
- void *v);
-/* scratch a track by ID */
-
int disorder_eclient_scratch_playing(disorder_eclient *c,
disorder_eclient_no_response *completed,
void *v);
/* scratch the playing track whatever it is */
-int disorder_eclient_remove(disorder_eclient *c,
- const char *id,
- disorder_eclient_no_response *completed,
- void *v);
-/* remove a track from the queue */
-
-int disorder_eclient_moveafter(disorder_eclient *c,
- const char *target,
- int nids,
- const char **ids,
- disorder_eclient_no_response *completed,
- void *v);
-/* move tracks within the queue */
-
-int disorder_eclient_playing(disorder_eclient *c,
- disorder_eclient_queue_response *completed,
- void *v);
-/* find the currently playing track (0 for none) */
-
-int disorder_eclient_queue(disorder_eclient *c,
- disorder_eclient_queue_response *completed,
- void *v);
-/* list recently played tracks */
-
-int disorder_eclient_recent(disorder_eclient *c,
- disorder_eclient_queue_response *completed,
- void *v);
-/* list recently played tracks */
-
-int disorder_eclient_files(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- const char *dir,
- const char *re,
- void *v);
-/* list files in a directory, matching RE if not a null pointer */
-
-int disorder_eclient_dirs(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- const char *dir,
- const char *re,
- void *v);
-/* list directories in a directory, matching RE if not a null pointer */
-
-int disorder_eclient_namepart(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *track,
- const char *context,
- const char *part,
- void *v);
-/* look up a track name part */
-
-int disorder_eclient_length(disorder_eclient *c,
- disorder_eclient_integer_response *completed,
- const char *track,
- void *v);
-/* look up a track name length */
-
-int disorder_eclient_volume(disorder_eclient *c,
- disorder_eclient_volume_response *callback,
- int l, int r,
- void *v);
-/* If L and R are both -ve gets the volume.
- * If neither are -ve then sets the volume.
- * Otherwise asserts!
- */
-
-int disorder_eclient_enable(disorder_eclient *c,
- disorder_eclient_no_response *callback,
- void *v);
-int disorder_eclient_disable(disorder_eclient *c,
- disorder_eclient_no_response *callback,
- void *v);
-int disorder_eclient_random_enable(disorder_eclient *c,
- disorder_eclient_no_response *callback,
- void *v);
-int disorder_eclient_random_disable(disorder_eclient *c,
- disorder_eclient_no_response *callback,
- void *v);
-/* Enable/disable play/random play */
-
-int disorder_eclient_resolve(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *track,
- void *v);
-/* Resolve aliases */
-
int disorder_eclient_log(disorder_eclient *c,
const disorder_eclient_log_callbacks *callbacks,
void *v);
/* Make this a log client (forever - it automatically becomes one again upon
* reconnection) */
-int disorder_eclient_get(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *track, const char *pref,
- void *v);
-int disorder_eclient_set(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *track, const char *pref,
- const char *value,
- void *v);
-int disorder_eclient_unset(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *track, const char *pref,
- void *v);
-/* Get/set preference values */
-
-int disorder_eclient_get_global(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *pref,
- void *v);
-int disorder_eclient_set_global(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *pref,
- const char *value,
- void *v);
-int disorder_eclient_unset_global(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *pref,
- void *v);
-/* Get/set global prefs */
-
-int disorder_eclient_search(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- const char *terms,
- void *v);
-
-int disorder_eclient_nop(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v);
-
-int disorder_eclient_new_tracks(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- int max,
- void *v);
-
int disorder_eclient_rtp_address(disorder_eclient *c,
disorder_eclient_list_response *completed,
void *v);
-int disorder_eclient_users(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- void *v);
-int disorder_eclient_deluser(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *user,
- void *v);
-int disorder_eclient_userinfo(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *user,
- const char *property,
- void *v);
-int disorder_eclient_edituser(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *user,
- const char *property,
- const char *value,
- void *v);
-int disorder_eclient_adduser(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *user,
- const char *password,
- const char *rights,
- void *v);
void disorder_eclient_enable_connect(disorder_eclient *c);
void disorder_eclient_disable_connect(disorder_eclient *c);
-int disorder_eclient_adopt(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *id,
- void *v);
-int disorder_eclient_playlists(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- void *v);
-int disorder_eclient_playlist_delete(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *playlist,
- void *v);
-int disorder_eclient_playlist_lock(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *playlist,
- void *v);
-int disorder_eclient_playlist_unlock(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- void *v);
-int disorder_eclient_playlist_set_share(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *playlist,
- const char *sharing,
- void *v);
-int disorder_eclient_playlist_get_share(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *playlist,
- void *v);
-int disorder_eclient_playlist_set(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *playlist,
- char **tracks,
- int ntracks,
- void *v);
-int disorder_eclient_playlist_get(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- const char *playlist,
- void *v);
+
+#include "eclient-stubs.h"
#endif
EXTRA_DIST=htmlman sedfiles.make text2c oggrename make-unidata fix-names \
format-gcov-report make-version-string setup.in teardown.in macro-docs \
- setversion
+ setversion protocol
CLEANFILES=$(SEDFILES)
--- /dev/null
+#! /usr/bin/perl -w
+#
+# This file is part of DisOrder.
+# Copyright (C) 2010-11 Richard Kettlewell
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+use strict;
+
+# This file contains the definition of the disorder protocol, plus
+# code to generates stubs for it in the various supported languages.
+#
+# At the time of writing it is a work in progress!
+
+#
+# Types:
+#
+# string A (Unicode) string.
+# string-raw A string that is not subject to de-quoting (return only)
+# integer An integer. Decimal on the wire.
+# time A timestamp. Decimal on the wire.
+# boolean True or false. "yes" or "no" on the wire.
+# list In commands: a list of strings in the command.
+# In returns: a list of lines in the response.
+# pair-list In returns: a list of key-value pairs in a response body.
+# body In commands: a list of strings as a command body.
+# In returns: a list of strings as a response body.
+# queue In returns: a list of queue entries in a response body.
+# queue-one In returns: a queue entry in the response.
+# literal Constant string sent in sequence
+#
+
+# Variables and utilities -----------------------------------------------------
+
+our @h = ();
+our @c = ();
+our @ah = ();
+our @ac = ();
+our @missing = ();
+
+# Mapping of return type sequences to eclient callbacks
+our @eclient_return = (
+ ["no_response" => []],
+ ["string_response" => ["string"]],
+ ["string_response" => ["string-raw"]],
+ ["integer_response" => ["integer"]],
+ ["integer_response" => ["boolean"]],
+ ["time_response" => ["time"]],
+ ["pair_integer_response" => ["integer", "integer"]],
+ ["queue_response" => ["queue"]],
+ ["playing_response" => ["queue-one"]],
+ ["list_response" => ["body"]],
+ );
+
+# eclient_response_matces(RETURNS, VARIANT)
+#
+# Return true if VARIANT matches RETURNS
+sub eclient_response_matches {
+ my $returns = shift;
+ my $variant = shift;
+ my $types = $variant->[1];
+ if(scalar @$returns != scalar @$types) { return 0; }
+ for my $n (0 .. $#$returns) {
+ my $return = $returns->[$n];
+ my $type = $return->[0];
+ if($type ne $types->[$n]) { return 0; }
+ }
+ return 1;
+}
+
+# find_eclient_type(RETURNS)
+#
+# Find the result type for an eclient call
+sub find_eclient_response {
+ my $returns = shift;
+ if(!defined $returns) {
+ $returns = [];
+ }
+ for my $variant (@eclient_return) {
+ if(eclient_response_matches($returns, $variant)) {
+ return $variant->[0];
+ }
+ }
+ return undef;
+}
+
+# Write(PATH, LINES)
+#
+# Write array ref LINES to file PATH.
+sub Write {
+ my $path = shift;
+ my $lines = shift;
+
+ (open(F, ">$path")
+ and print F @$lines
+ and close F)
+ or die "$0: $path: $!\n";
+}
+
+# Command classes -------------------------------------------------------------
+
+# c_in_decl([TYPE, NAME])
+#
+# Return the C declaration for an input parameter of type TYPE with
+# name NAME.
+sub c_in_decl {
+ my $arg = shift;
+
+ my $type = $arg->[0];
+ my $name = $arg->[1];
+ if($type eq 'string') {
+ return "const char *$name";
+ } elsif($type eq 'integer') {
+ return "long $name";
+ } elsif($type eq 'time') {
+ return "time_t $name";
+ } elsif($type eq 'list' or $type eq 'body') {
+ return ("char **$name",
+ "int n$name");
+ } elsif($type eq 'literal') {
+ return ();
+ } else {
+ die "$0: c_in_decl: unknown type '$type'\n";
+ }
+}
+
+# c_out_decl([TYPE, NAME])
+#
+# Return the C declaration for an output (reference) parameter of type
+# TYPE with name NAME.
+sub c_out_decl {
+ my $arg = shift;
+
+ return () unless defined $arg;
+ my $type = $arg->[0];
+ my $name = $arg->[1];
+ if($type eq 'string' or $type eq 'string-raw') {
+ return ("char **${name}p");
+ } elsif($type eq 'integer') {
+ return ("long *${name}p");
+ } elsif($type eq 'time') {
+ return ("time_t *${name}p");
+ } elsif($type eq 'boolean') {
+ return ("int *${name}p");
+ } elsif($type eq 'list' or $type eq 'body') {
+ return ("char ***${name}p",
+ "int *n${name}p");
+ } elsif($type eq 'pair-list') {
+ return ("struct kvp **${name}p");
+ } elsif($type eq 'queue' or $type eq 'queue-one') {
+ return ("struct queue_entry **${name}p");
+ } elsif($type eq 'user') {
+ return ();
+ } else {
+ die "$0: c_out_decl: unknown type '$type'\n";
+ }
+}
+
+# c_param_docs([TYPE, NAME})
+#
+# Return the doc string for a C input parameter.
+sub c_param_docs {
+ my $args = shift;
+ my @d = ();
+ for my $arg (@$args) {
+ my $type = $arg->[0];
+ my $name = $arg->[1];
+ my $description = $arg->[2];
+ if($type eq 'body' or $type eq 'list') {
+ push(@d,
+ " * \@param $name $description\n",
+ " * \@param n$name Length of $name\n");
+ } elsif($type ne 'literal') {
+ push(@d, " * \@param $name $description\n");
+ }
+ }
+ return @d;
+}
+
+# c_param_docs([TYPE, NAME})
+#
+# Return the doc string for a C output parameter.
+sub c_return_docs {
+ my $returns = shift;
+ return () unless defined $returns;
+ my @docs = ();
+ for my $return (@$returns) {
+ my $type = $return->[0];
+ my $name = $return->[1];
+ my $descr = $return->[2];
+ if($type eq 'string'
+ or $type eq 'string-raw'
+ or $type eq 'integer'
+ or $type eq 'time'
+ or $type eq 'boolean') {
+ push(@docs,
+ " * \@param ${name}p $descr\n");
+ } elsif($type eq 'list' or $type eq 'body') {
+ push(@docs,
+ " * \@param ${name}p $descr\n",
+ " * \@param n${name}p Number of elements in ${name}p\n");
+ } elsif($type eq 'pair-list') {
+ push(@docs,
+ " * \@param ${name}p $descr\n");
+ } elsif($type eq 'queue' or $type eq 'queue-one') {
+ push(@docs,
+ " * \@param ${name}p $descr\n");
+ } elsif($type eq 'user') {
+ # nothing
+ } else {
+ die "$0: c_return_docs: unknown type '$type'\n";
+ }
+ }
+ return @docs;
+}
+
+# simple(CMD, SUMMARY, DETAIL,
+# [[TYPE,NAME,DESCR], [TYPE,NAME,DESCR], ...],
+# [[RETURN-TYPE, RETURN-NAME, RETURN_DESCR]])
+#
+# CMD is normally just the name of the command, but can
+# be [COMMAND,FUNCTION] if the function name should differ
+# from the protocol command.
+sub simple {
+ my $cmd = shift;
+ my $summary = shift;
+ my $detail = shift;
+ my $args = shift;
+ my $returns = shift;
+
+ my $cmdc;
+ if(ref $cmd eq 'ARRAY') {
+ $cmdc = $$cmd[1];
+ $cmd = $$cmd[0];
+ } else {
+ $cmdc = $cmd;
+ $cmdc =~ s/-/_/g;
+ }
+ print STDERR "Processing $cmd... ";
+ # C argument types
+ my @cargs = ();
+ for my $arg (@$args) {
+ if($arg->[0] eq 'body' or $arg->[0] eq 'list') {
+ push(@cargs, "disorder__$arg->[0]", $arg->[1], "n$arg->[1]");
+ } elsif($arg->[0] eq 'string') {
+ push(@cargs, $arg->[1]);
+ } elsif($arg->[0] eq 'integer'
+ or $arg->[0] eq 'time') {
+ push(@cargs, "disorder__$arg->[0]", "$arg->[1]");
+ } elsif($arg->[0] eq 'literal') {
+ push(@cargs, "\"$arg->[1]\"");
+ } else {
+ die "$0: unsupported arg type '$arg->[0]' for '$cmd'\n";
+ }
+ }
+ # Synchronous C API
+ print STDERR "H ";
+ push(@h, "/** \@brief $summary\n",
+ " *\n",
+ " * $detail\n",
+ " *\n",
+ " * \@param c Client\n",
+ c_param_docs($args),
+ c_return_docs($returns),
+ " * \@return 0 on success, non-0 on error\n",
+ " */\n",
+ "int disorder_$cmdc(",
+ join(", ", "disorder_client *c",
+ map(c_in_decl($_), @$args),
+ map(c_out_decl($_), @$returns)),
+ ");\n\n");
+ print STDERR "C ";
+ push(@c, "int disorder_$cmdc(",
+ join(", ", "disorder_client *c",
+ map(c_in_decl($_), @$args),
+ map(c_out_decl($_), @$returns)),
+ ") {\n");
+ if(!defined $returns or scalar @$returns == 0) {
+ # Simple case
+ push(@c, " return disorder_simple(",
+ join(", ", "c", "NULL", "\"$cmd\"", @cargs, "(char *)NULL"),
+ ");\n");
+ } elsif(scalar @$returns == 1
+ and $returns->[0]->[0] eq 'queue-one') {
+ # Special case
+ my $return = $$returns[0];
+ push(@c, " return onequeue(c, \"$cmd\", $return->[1]p);\n");
+ } elsif(scalar @$returns == 1
+ and $returns->[0]->[0] eq 'string-raw') {
+ # Special case
+ my $return = $$returns[0];
+ push(@c, " return disorder_simple(",
+ join(", ", "c", "$return->[1]p", "\"$cmd\"", @cargs, "(char *)NULL"),
+ ");\n");
+ } elsif(scalar @$returns == 1
+ and $returns->[0]->[0] eq 'pair-list') {
+ # Special case
+ my $return = $$returns[0];
+ push(@c, " return pairlist(",
+ join(", ", "c", "$return->[1]p", "\"$cmd\"",
+ @cargs,
+ "(char *)NULL"),
+ ");\n");
+ } else {
+ my $expected = 0;
+ for(my $n = 0; $n < scalar @$returns; ++$n) {
+ my $return = $returns->[$n];
+ my $type = $return->[0];
+ my $name = $return->[1];
+ if($type eq 'string'
+ or $type eq 'boolean'
+ or $type eq 'integer'
+ or $type eq 'time'
+ or $type eq 'user') {
+ ++$expected;
+ }
+ }
+ if($expected) {
+ push(@c, " char **v;\n",
+ " int nv, rc = disorder_simple_split(",
+ join(", ",
+ "c",
+ "&v",
+ "&nv",
+ $expected,
+ "\"$cmd\"",
+ @cargs,
+ "(char *)NULL"),
+ ");\n",
+ " if(rc)\n",
+ " return rc;\n");
+ } else {
+ push(@c,
+ " int rc = disorder_simple(",
+ join(", ",
+ "c",
+ "NULL",
+ "\"$cmd\"",
+ @cargs,
+ "(char *)NULL"),
+ ");\n",
+ " if(rc)\n",
+ " return rc;\n");
+ }
+ for(my $n = 0; $n < scalar @$returns; ++$n) {
+ my $return = $returns->[$n];
+ my $type = $return->[0];
+ my $name = $return->[1];
+ if($type eq 'string') {
+ push(@c,
+ " *${name}p = v[$n];\n",
+ " v[$n] = NULL;\n");
+ } elsif($type eq 'boolean') {
+ push(@c,
+ " if(boolean(\"$cmd\", v[$n], ${name}p))\n",
+ " return -1;\n");
+ } elsif($type eq 'integer') {
+ push(@c,
+ " *${name}p = atol(v[$n]);\n");
+ } elsif($type eq 'time') {
+ push(@c,
+ " *${name}p = atoll(v[$n]);\n");
+ } elsif($type eq 'user') {
+ push(@c,
+ " c->user = v[$n];\n",
+ " v[$n] = NULL;\n");
+ } elsif($type eq 'body') {
+ push(@c,
+ " if(readlist(c, ${name}p, n${name}p))\n",
+ " return -1;\n");
+ } elsif($type eq 'queue') {
+ push(@c,
+ " if(readqueue(c, ${name}p))\n",
+ " return -1;\n");
+ } else {
+ die "$0: C API: unknown return type '$type' for '$name'\n";
+ }
+ }
+ if($expected) {
+ push(@c,
+ " free_strings(nv, v);\n");
+ }
+ push(@c, " return 0;\n");
+ }
+ push(@c, "}\n\n");
+
+ # Asynchronous C API
+ my $variant = find_eclient_response($returns);
+ if(defined $variant) {
+ print STDERR "AH ";
+ push(@ah,
+ "/** \@brief $summary\n",
+ " *\n",
+ " * $detail\n",
+ " *\n",
+ " * \@param c Client\n",
+ " * \@param completed Called upon completion\n",
+ c_param_docs($args),
+ " * \@param v Passed to \@p completed\n",
+ " * \@return 0 if the command was queued successfuly, non-0 on error\n",
+ " */\n",
+ "int disorder_eclient_$cmdc(",
+ join(", ", "disorder_eclient *c",
+ "disorder_eclient_$variant *completed",
+ map(c_in_decl($_), @$args),
+ "void *v"),
+ ");\n\n");
+
+ print STDERR "AC ";
+ push(@ac,
+ "int disorder_eclient_$cmdc(",
+ join(", ", "disorder_eclient *c",
+ "disorder_eclient_$variant *completed",
+ map(c_in_decl($_), @$args),
+ "void *v"),
+ ") {\n");
+ push(@ac, " return simple(",
+ join(", ",
+ "c",
+ "${variant}_opcallback",
+ "(void (*)())completed",
+ "v",
+ "\"$cmd\"",
+ @cargs,
+ "(char *)0"),
+ ");\n");
+ push(@ac, "}\n\n");
+ } else {
+ push(@missing, "disorder_eclient_$cmdc");
+ }
+
+ # Python API
+ # TODO
+
+ # Java API
+ # TODO
+ print STDERR "\n";
+}
+
+# TODO other command classes
+
+# Front matter ----------------------------------------------------------------
+
+our @generated = ("/*\n",
+ " * Automatically generated file, see scripts/protocol\n",
+ " *\n",
+ " * DO NOT EDIT.\n",
+ " */\n");
+
+our @gpl = ("/*\n",
+ " * This file is part of DisOrder.\n",
+ " * Copyright (C) 2010-11 Richard Kettlewell\n",
+ " *\n",
+ " * This program is free software: you can redistribute it and/or modify\n",
+ " * it under the terms of the GNU General Public License as published by\n",
+ " * the Free Software Foundation, either version 3 of the License, or\n",
+ " * (at your option) any later version.\n",
+ " *\n",
+ " * This program is distributed in the hope that it will be useful,\n",
+ " * but WITHOUT ANY WARRANTY; without even the implied warranty of\n",
+ " * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n",
+ " * GNU General Public License for more details.\n",
+ " *\n",
+ " * You should have received a copy of the GNU General Public License\n",
+ " * along with this program. If not, see <http://www.gnu.org/licenses/>.\n",
+ " */\n");
+
+
+push(@h, @generated, @gpl,
+ "#ifndef CLIENT_STUBS_H\n",
+ "#define CLIENT_STUBS_H\n",
+ "/** \@file lib/client-stubs.h\n",
+ " * \@brief Generated client API\n",
+ " *\n",
+ " * Don't include this file directly - use \@ref lib/client.h instead.\n",
+ " */\n",
+ "\n");
+
+push(@c, @generated, @gpl,
+ "/** \@file lib/client-stubs.c\n",
+ " * \@brief Generated client API implementation\n",
+ " */\n",
+ "\n");
+
+push(@ah, @generated, @gpl,
+ "#ifndef ECLIENT_STUBS_H\n",
+ "#define ECLIENT_STUBS_H\n",
+ "/** \@file lib/client-stubs.h\n",
+ " * \@brief Generated asynchronous client API\n",
+ " *\n",
+ " * Don't include this file directly - use \@ref lib/eclient.h instead.\n",
+ " */\n",
+ "\n");
+
+push(@ac, @generated, @gpl,
+ "/** \@file lib/client-stubs.c\n",
+ " * \@brief Generated asynchronous client API implementation\n",
+ " */\n",
+ "\n");
+
+# The protocol ----------------------------------------------------------------
+
+simple("adopt",
+ "Adopt a track",
+ "Makes the calling user owner of a randomly picked track.",
+ [["string", "id", "Track ID"]]);
+
+simple("adduser",
+ "Create a user",
+ "Create a new user. Requires the 'admin' right. Email addresses etc must be filled in in separate commands.",
+ [["string", "user", "New username"],
+ ["string", "password", "Initial password"],
+ ["string", "rights", "Initial rights (optional)"]]);
+
+simple("allfiles",
+ "List files and directories in a directory",
+ "See 'files' and 'dirs' for more specific lists.",
+ [["string", "dir", "Directory to list (optional)"],
+ ["string", "re", "Regexp that results must match (optional)"]],
+ [["body", "files", "List of matching files and directories"]]);
+
+simple("confirm",
+ "Confirm registration",
+ "The confirmation string must have been created with 'register'. The username is returned so the caller knows who they are.",
+ [["string", "confirmation", "Confirmation string"]],
+ [["user"]]);
+
+simple("cookie",
+ "Log in with a cookie",
+ "The cookie must have been created with 'make-cookie'. The username is returned so the caller knows who they are.",
+ [["string", "cookie", "Cookie string"]],
+ [["user"]]);
+
+simple("deluser",
+ "Delete user",
+ "Requires the 'admin' right.",
+ [["string", "user", "User to delete"]]);
+
+simple("dirs",
+ "List directories in a directory",
+ "",
+ [["string", "dir", "Directory to list (optional)"],
+ ["string", "re", "Regexp that results must match (optional)"]],
+ [["body", "files", "List of matching directories"]]);
+
+simple("disable",
+ "Disable play",
+ "Play will stop at the end of the current track, if one is playing. Requires the 'global prefs' right.",
+ []);
+
+simple("edituser",
+ "Set a user property",
+ "With the 'admin' right you can do anything. Otherwise you need the 'userinfo' right and can only set 'email' and 'password'.",
+ [["string", "username", "User to modify"],
+ ["string", "property", "Property name"],
+ ["string", "value", "New property value"]]);
+
+simple("enable",
+ "Enable play",
+ "Requires the 'global prefs' right.",
+ []);
+
+simple("enabled",
+ "Detect whether play is enabled",
+ "",
+ [],
+ [["boolean", "enabled", "1 if play is enabled and 0 otherwise"]]);
+
+simple("exists",
+ "Test whether a track exists",
+ "",
+ [["string", "track", "Track name"]],
+ [["boolean", "exists", "1 if the track exists and 0 otherwise"]]);
+
+simple("files",
+ "List files in a directory",
+ "",
+ [["string", "dir", "Directory to list (optional)"],
+ ["string", "re", "Regexp that results must match (optional)"]],
+ [["body", "files", "List of matching files"]]);
+
+simple("get",
+ "Get a track preference",
+ "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.",
+ [["string", "track", "Track name"],
+ ["string", "pref", "Preference name"]],
+ [["string", "value", "Preference value"]]);
+
+simple("get-global",
+ "Get a global preference",
+ "If the preference does exist not then a null value is returned.",
+ [["string", "pref", "Global preference name"]],
+ [["string", "value", "Preference value"]]);
+
+simple("length",
+ "Get a track's length",
+ "If the track does not exist an error is returned.",
+ [["string", "track", "Track name"]],
+ [["integer", "length", "Track length in seconds"]]);
+
+# TODO log
+
+simple("make-cookie",
+ "Create a login cookie for this user",
+ "The cookie may be redeemed via the 'cookie' command",
+ [],
+ [["string", "cookie", "Newly created cookie"]]);
+
+simple("move",
+ "Move a track",
+ "Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.",
+ [["string", "track", "Track ID or name"],
+ ["integer", "delta", "How far to move the track towards the head of the queue"]]);
+
+simple("moveafter",
+ "Move multiple tracks",
+ "Requires one of the 'move mine', 'move random' or 'move any' rights depending on how the track came to be added to the queue.",
+ [["string", "target", "Move after this track, or to head if \"\""],
+ ["list", "ids", "List of tracks to move by ID"]]);
+
+simple(["new", "new_tracks"],
+ "List recently added tracks",
+ "",
+ [["integer", "max", "Maximum tracks to fetch, or 0 for all available"]],
+ [["body", "tracks", "Recently added tracks"]]);
+
+simple("nop",
+ "Do nothing",
+ "Used as a keepalive. No authentication required.",
+ []);
+
+simple("part",
+ "Get a track name part",
+ "If the name part cannot be constructed an empty string is returned.",
+ [["string", "track", "Track name"],
+ ["string", "context", "Context (\"sort\" or \"display\")"],
+ ["string", "part", "Name part (\"artist\", \"album\" or \"title\")"]],
+ [["string", "part", "Value of name part"]]);
+
+simple("pause",
+ "Pause the currently playing track",
+ "Requires the 'pause' right.",
+ []);
+
+simple("play",
+ "Play a track",
+ "Requires the 'play' right.",
+ [["string", "track", "Track to play"]],
+ [["string-raw", "id", "Queue ID of new track"]]);
+
+simple("playafter",
+ "Play multiple tracks",
+ "Requires the 'play' right.",
+ [["string", "target", "Insert into queue after this track, or at head if \"\""],
+ ["list", "tracks", "List of track names to play"]]);
+
+simple("playing",
+ "Retrieve the playing track",
+ "",
+ [],
+ [["queue-one", "playing", "Details of the playing track"]]);
+
+simple("playlist-delete",
+ "Delete a playlist",
+ "Requires the 'play' right and permission to modify the playlist.",
+ [["string", "playlist", "Playlist to delete"]]);
+
+simple("playlist-get",
+ "List the contents of a playlist",
+ "Requires the 'read' right and oermission to read the playlist.",
+ [["string", "playlist", "Playlist name"]],
+ [["body", "tracks", "List of tracks in playlist"]]);
+
+simple("playlist-get-share",
+ "Get a playlist's sharing status",
+ "Requires the 'read' right and permission to read the playlist.",
+ [["string", "playlist", "Playlist to read"]],
+ [["string-raw", "share", "Sharing status (\"public\", \"private\" or \"shared\")"]]);
+
+simple("playlist-lock",
+ "Lock a playlist",
+ "Requires the 'play' right and permission to modify the playlist. A given connection may lock at most one playlist.",
+ [["string", "playlist", "Playlist to delete"]]);
+
+simple("playlist-set",
+ "Set the contents of a playlist",
+ "Requires the 'play' right and permission to modify the playlist, which must be locked.",
+ [["string", "playlist", "Playlist to modify"],
+ ["body", "tracks", "New list of tracks for playlist"]]);
+
+simple("playlist-set-share",
+ "Set a playlist's sharing status",
+ "Requires the 'play' right and permission to modify the playlist.",
+ [["string", "playlist", "Playlist to modify"],
+ ["string", "share", "New sharing status (\"public\", \"private\" or \"shared\")"]]);
+
+simple("playlist-unlock",
+ "Unlock the locked playlist playlist",
+ "The playlist to unlock is implicit in the connection.",
+ []);
+
+simple("playlists",
+ "List playlists",
+ "Requires the 'read' right. Only playlists that you have permission to read are returned.",
+ [],
+ [["body", "playlists", "Playlist names"]]);
+
+simple("prefs",
+ "Get all the preferences for a track",
+ "",
+ [["string", "track", "Track name"]],
+ [["pair-list", "prefs", "Track preferences"]]);
+
+simple("queue",
+ "List the queue",
+ "",
+ [],
+ [["queue", "queue", "Current queue contents"]]);
+
+simple("random-disable",
+ "Disable random play",
+ "Requires the 'global prefs' right.",
+ []);
+
+simple("random-enable",
+ "Enable random play",
+ "Requires the 'global prefs' right.",
+ []);
+
+simple("random-enabled",
+ "Detect whether random play is enabled",
+ "Random play counts as enabled even if play is disabled.",
+ [],
+ [["boolean", "enabled", "1 if random play is enabled and 0 otherwise"]]);
+
+simple("recent",
+ "List recently played tracks",
+ "",
+ [],
+ [["queue", "recent", "Recently played tracks"]]);
+
+simple("reconfigure",
+ "Re-read configuraiton file.",
+ "Requires the 'admin' right.",
+ []);
+
+simple("register",
+ "Register a new user",
+ "Requires the 'register' right which is usually only available to the 'guest' user. Redeem the confirmation string via 'confirm' to complete registration.",
+ [["string", "username", "Requested new username"],
+ ["string", "password", "Requested initial password"],
+ ["string", "email", "New user's email address"]],
+ [["string", "confirmation", "Confirmation string"]]);
+
+simple("reminder",
+ "Send a password reminder.",
+ "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.",
+ [["string", "username", "User to remind"]]);
+
+simple("remove",
+ "Remove a track form the queue.",
+ "Requires one of the 'remove mine', 'remove random' or 'remove any' rights depending on how the track came to be added to the queue.",
+ [["string", "id", "Track ID"]]);
+
+simple("rescan",
+ "Rescan all collections for new or obsolete tracks.",
+ "Requires the 'rescan' right.",
+ []); # TODO wait/fresh flags
+
+simple("resolve",
+ "Resolve a track name",
+ "Converts aliases to non-alias track names",
+ [["string", "track", "Track name (might be an alias)"]],
+ [["string", "resolved", "Resolve track name (definitely not an alias)"]]);
+
+simple("resume",
+ "Resume the currently playing track",
+ "Requires the 'pause' right.",
+ []);
+
+simple("revoke",
+ "Revoke a cookie.",
+ "It will not subsequently be possible to log in with the cookie.",
+ []);
+
+simple("rtp-address",
+ "Get the server's RTP address information",
+ "",
+ [],
+ [["string", "address", "Where to store hostname or address"],
+ ["string", "port", "Where to store service name or port number"]]);
+
+simple("scratch",
+ "Terminate the playing track.",
+ "Requires one of the 'scratch mine', 'scratch random' or 'scratch any' rights depending on how the track came to be added to the queue.",
+ [["string", "id", "Track ID (optional)"]]);
+
+simple(["schedule-add", "schedule_add_play"],
+ "Schedule a track to play in the future",
+ "",
+ [["time", "when", "When to play the track"],
+ ["string", "priority", "Event priority (\"normal\" or \"junk\")"],
+ ["literal", "play", ""],
+ ["string", "track", "Track to play"]]);
+
+simple(["schedule-add", "schedule_add_set_global"],
+ "Schedule a global setting to be changed in the future",
+ "",
+ [["time", "when", "When to change the setting"],
+ ["string", "priority", "Event priority (\"normal\" or \"junk\")"],
+ ["literal", "set-global", ""],
+ ["string", "pref", "Global preference to set"],
+ ["string", "value", "New value of global preference"]]);
+
+simple(["schedule-add", "schedule_add_unset_global"],
+ "Schedule a global setting to be unset in the future",
+ "",
+ [["time", "when", "When to change the setting"],
+ ["string", "priority", "Event priority (\"normal\" or \"junk\")"],
+ ["literal", "set-global", ""],
+ ["string", "pref", "Global preference to set"]]);
+
+simple("schedule-del",
+ "Delete a scheduled event.",
+ "Users can always delete their own scheduled events; with the admin right you can delete any event.",
+ [["string", "event", "ID of event to delete"]]);
+
+simple("schedule-get",
+ "Get the details of scheduled event",
+ "",
+ [["string", "id", "Event ID"]],
+ [["pair-list", "actiondata", "Details of event"]]);
+
+simple("schedule-list",
+ "List scheduled events",
+ "This just lists IDs. Use 'schedule-get' to retrieve more detail",
+ [],
+ [["body", "ids", "List of event IDs"]]);
+
+simple("search",
+ "Search for tracks",
+ "Terms are either keywords or tags formatted as 'tag:TAG-NAME'.",
+ [["string", "terms", "List of search terms"]],
+ [["body", "tracks", "List of matching tracks"]]);
+
+simple("set",
+ "Set a track preference",
+ "Requires the 'prefs' right.",
+ [["string", "track", "Track name"],
+ ["string", "pref", "Preference name"],
+ ["string", "value", "New value"]]);
+
+simple("set-global",
+ "Set a global preference",
+ "Requires the 'global prefs' right.",
+ [["string", "pref", "Preference name"],
+ ["string", "value", "New value"]]);
+
+simple("shutdown",
+ "Request server shutdown",
+ "Requires the 'admin' right.",
+ []);
+
+simple("stats",
+ "Get server statistics",
+ "The details of what the server reports are not really defined. The returned strings are intended to be printed out one to a line.",
+ [],
+ [["body", "stats", "List of server information strings."]]);
+
+simple("tags",
+ "Get a list of known tags",
+ "Only tags which apply to at least one track are returned.",
+ [],
+ [["body", "tags", "List of tags"]]);
+
+simple("unset",
+ "Unset a track preference",
+ "Requires the 'prefs' right.",
+ [["string", "track", "Track name"],
+ ["string", "pref", "Preference name"]]);
+
+simple("unset-global",
+ "Set a global preference",
+ "Requires the 'global prefs' right.",
+ [["string", "pref", "Preference name"]]);
+
+# 'user' only used for authentication
+
+simple("userinfo",
+ "Get a user property.",
+ "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.",
+ [["string", "username", "User to read"],
+ ["string", "property", "Property to read"]],
+ [["string", "value", "Value of property"]]);
+
+simple("users",
+ "Get a list of users",
+ "",
+ [],
+ [["body", "users", "List of users"]]);
+
+simple("version",
+ "Get the server version",
+ "",
+ [],
+ [["string", "version", "Server version string"]]);
+
+simple(["volume", "set_volume"],
+ "Set the volume",
+ "",
+ [["integer", "left", "Left channel volume"],
+ ["integer", "right", "Right channel volume"]]);
+
+simple(["volume", "get_volume"],
+ "Get the volume",
+ "",
+ [],
+ [["integer", "left", "Left channel volume"],
+ ["integer", "right", "Right channel volume"]]);
+
+# End matter ------------------------------------------------------------------
+
+push(@h, "#endif\n");
+
+push(@ah, "#endif\n");
+
+# Write it all out ------------------------------------------------------------
+
+Write("lib/client-stubs.h", \@h);
+Write("lib/client-stubs.c", \@c);
+
+Write("lib/eclient-stubs.h", \@ah);
+Write("lib/eclient-stubs.c", \@ac);
+
+if(scalar @missing) {
+ print "Missing:\n";
+ print map(" $_\n", @missing);
+}