From: Richard Kettlewell Date: Sun, 7 Aug 2011 16:45:36 +0000 (+0100) Subject: Merge branch 'protogen' X-Git-Tag: branchpoint-5.1~22 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/6961095b797229842e1465ff3670625d11e95167?hp=1a164e639307f5050638cc01ead9d4657e26e7a5 Merge branch 'protogen' --- diff --git a/README.developers b/README.developers index 20cd7d9..692dd12 100644 --- a/README.developers +++ b/README.developers @@ -9,13 +9,12 @@ Dependencies: * 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 @@ -107,14 +106,11 @@ The Server: * 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. @@ -127,10 +123,13 @@ The Server: 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. @@ -148,7 +147,7 @@ Disobedience: 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: diff --git a/cgi/actions.c b/cgi/actions.c index f6755b0..584da76 100644 --- a/cgi/actions.c +++ b/cgi/actions.c @@ -240,16 +240,17 @@ static void act_play(void) { 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); diff --git a/cgi/disorder-cgi.h b/cgi/disorder-cgi.h index 7788578..e9deab2 100644 --- a/cgi/disorder-cgi.h +++ b/cgi/disorder-cgi.h @@ -91,8 +91,8 @@ extern struct queue_entry *dcgi_queue; 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; diff --git a/cgi/lookup.c b/cgi/lookup.c index fd94900..2b6d594 100644 --- a/cgi/lookup.c +++ b/cgi/lookup.c @@ -33,8 +33,8 @@ struct queue_entry *dcgi_queue; 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; @@ -68,7 +68,7 @@ void dcgi_lookup(unsigned want) { 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); diff --git a/cgi/macros-disorder.c b/cgi/macros-disorder.c index 29835bb..1ef1ee1 100644 --- a/cgi/macros-disorder.c +++ b/cgi/macros-disorder.c @@ -159,10 +159,11 @@ static int exp_part(int nargs, 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; @@ -457,7 +458,7 @@ static int exp_volume(int attribute((unused)) nargs, 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; } @@ -625,7 +626,7 @@ static int exp_trackstate(int attribute((unused)) nargs, 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)) @@ -661,7 +662,7 @@ static int exp_resolve(int attribute((unused)) nargs, 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; } @@ -930,7 +931,7 @@ static int exp_dirs(int nargs, 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, diff --git a/clients/disorder.c b/clients/disorder.c index 64694ef..b401c62 100644 --- a/clients/disorder.c +++ b/clients/disorder.c @@ -138,8 +138,9 @@ static void cf_playing(char attribute((unused)) **argv) { } 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) { @@ -244,7 +245,7 @@ static int isarg_regexp(const char *s) { } static void cf_dirs(char **argv) { - cf_somelist(argv, disorder_directories); + cf_somelist(argv, disorder_dirs); } static void cf_files(char **argv) { @@ -316,10 +317,10 @@ static void cf_stats(char attribute((unused)) **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) { @@ -344,7 +345,7 @@ static void cf_move(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))); } @@ -359,7 +360,7 @@ static void cf_authorize(char **argv) { 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))); } @@ -415,7 +416,7 @@ static int isarg_integer(const char *s) { 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++))); @@ -586,31 +587,27 @@ static void cf_schedule_del(char **argv) { } 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); } diff --git a/disobedience/choose-menu.c b/disobedience/choose-menu.c index b0f59b5..b4aa0ce 100644 --- a/disobedience/choose-menu.c +++ b/disobedience/choose-menu.c @@ -29,7 +29,9 @@ static void choose_playchildren_callback(GtkTreeModel *model, 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; @@ -116,7 +118,7 @@ static void choose_play_activate(GtkMenuItem attribute((unused)) *item, 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) { @@ -250,11 +252,12 @@ static void choose_playchildren_received(void attribute((unused)) *v, 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; diff --git a/disobedience/choose.c b/disobedience/choose.c index a1d50c1..13a1da0 100644 --- a/disobedience/choose.c +++ b/disobedience/choose.c @@ -364,7 +364,8 @@ static void choose_files_completed(void *v, } 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); } @@ -386,7 +387,7 @@ static void choose_state_toggled 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); } diff --git a/disobedience/choose.h b/disobedience/choose.h index 5213332..9496abd 100644 --- a/disobedience/choose.h +++ b/disobedience/choose.h @@ -62,8 +62,9 @@ struct choosedata *choose_path_to_data(GtkTreePath *path); 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); diff --git a/disobedience/control.c b/disobedience/control.c index f45492f..1cdc190 100644 --- a/disobedience/control.c +++ b/disobedience/control.c @@ -468,9 +468,7 @@ static void toggled_menu(GtkCheckMenuItem attribute((unused)) *menuitem, /** @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 @@ -498,10 +496,10 @@ static void volume_adjusted(GtkAdjustment attribute((unused)) *a, 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 */ diff --git a/disobedience/globals.c b/disobedience/globals.c index 2734c32..a6b4ca4 100644 --- a/disobedience/globals.c +++ b/disobedience/globals.c @@ -217,9 +217,9 @@ void popup_globals(void) { } /** @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 */ diff --git a/disobedience/lookup.c b/disobedience/lookup.c index 68939ae..21bacc4 100644 --- a/disobedience/lookup.c +++ b/disobedience/lookup.c @@ -74,8 +74,8 @@ static void namepart_fill(const char *track, 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 diff --git a/disobedience/playlists.c b/disobedience/playlists.c index 3b38bc7..c5273ae 100644 --- a/disobedience/playlists.c +++ b/disobedience/playlists.c @@ -253,7 +253,8 @@ static int playlistcmp(const void *ap, const void *bp) { /* 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); } @@ -270,7 +271,7 @@ static void playlist_menu_received_content(void attribute((unused)) *v, 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 diff --git a/disobedience/properties.c b/disobedience/properties.c index fb39ea4..ded862b 100644 --- a/disobedience/properties.c +++ b/disobedience/properties.c @@ -338,8 +338,8 @@ static void kickoff_namepart(struct prefdata *f) { * 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) { diff --git a/disobedience/queue-menu.c b/disobedience/queue-menu.c index ebf8555..faf16cd 100644 --- a/disobedience/queue-menu.c +++ b/disobedience/queue-menu.c @@ -129,7 +129,7 @@ static void ql_remove_activate_callback(GtkTreeModel *model, 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, @@ -148,7 +148,8 @@ int ql_play_sensitive(void *extra) { && 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); } @@ -159,7 +160,7 @@ static void ql_play_activate_callback(GtkTreeModel *model, 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, diff --git a/disobedience/queue.c b/disobedience/queue.c index c495bd8..95ff0d5 100644 --- a/disobedience/queue.c +++ b/disobedience/queue.c @@ -205,9 +205,10 @@ static void queue_drop(struct queuelike attribute((unused)) *ql, /* 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 "". */ @@ -215,9 +216,10 @@ static void queue_drop(struct queuelike attribute((unused)) *ql, 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); } } diff --git a/doc/disorder_protocol.5.in b/doc/disorder_protocol.5.in index b9ed984..58a7e8f 100644 --- a/doc/disorder_protocol.5.in +++ b/doc/disorder_protocol.5.in @@ -75,7 +75,8 @@ If \fIREGEXP\fR is present only matching files and directories are returned. .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. @@ -340,9 +341,9 @@ Resolve a track name, i.e. if this is an alias then return the real track name. 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 @@ -436,6 +437,10 @@ Requires the \fBprefs\fR right. 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 diff --git a/lib/Makefile.am b/lib/Makefile.am index eea6220..c8ac6a9 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,17 +1,17 @@ # # 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 . # @@ -35,7 +35,7 @@ libdisorder_a_SOURCES=charset.c charsetf.c charset.h \ 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 \ @@ -134,10 +134,12 @@ definitions.h: Makefile 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 diff --git a/lib/client-common.c b/lib/client-common.c index d5c61ce..476117c 100644 --- a/lib/client-common.c +++ b/lib/client-common.c @@ -74,6 +74,11 @@ socklen_t find_server(struct config *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 diff --git a/lib/client-common.h b/lib/client-common.h index 736ac6f..26db715 100644 --- a/lib/client-common.h +++ b/lib/client-common.h @@ -27,6 +27,18 @@ 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 */ /* diff --git a/lib/client-stubs.c b/lib/client-stubs.c new file mode 100644 index 0000000..10a1fb2 --- /dev/null +++ b/lib/client-stubs.c @@ -0,0 +1,480 @@ +/* + * 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 . + */ +/** @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; +} + diff --git a/lib/client-stubs.h b/lib/client-stubs.h new file mode 100644 index 0000000..1962df2 --- /dev/null +++ b/lib/client-stubs.h @@ -0,0 +1,773 @@ +/* + * 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 . + */ +#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 diff --git a/lib/client.c b/lib/client.c index 2cbcfa7..987ab01 100644 --- a/lib/client.c +++ b/lib/client.c @@ -1,6 +1,6 @@ /* * 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 @@ -155,8 +155,6 @@ static int check_response(disorder_client *c, char **rp) { * @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 * @@ -168,22 +166,35 @@ static int check_response(disorder_client *c, char **rp) { * 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"; @@ -194,8 +205,37 @@ static int disorder_simple_v(disorder_client *c, 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); @@ -203,7 +243,7 @@ static int disorder_simple_v(disorder_client *c, 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) ; @@ -253,31 +293,61 @@ static int disorder_simple(disorder_client *c, 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; } @@ -522,113 +592,24 @@ int disorder_close(disorder_client *c) { 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); @@ -641,14 +622,11 @@ int disorder_playing(disorder_client *c, struct queue_entry **qp) { } /** @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; @@ -667,34 +645,12 @@ static int disorder_somequeue(disorder_client *c, 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) @@ -730,72 +686,6 @@ static int readlist(disorder_client *c, char ***vecp, int *nvecp) { 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) @@ -804,65 +694,36 @@ char *disorder_user(disorder_client *c) { 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); @@ -893,142 +754,6 @@ static int boolean(const char *cmd, const char *value, 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 @@ -1050,438 +775,7 @@ int disorder_log(disorder_client *c, struct sink *s) { 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: diff --git a/lib/client.h b/lib/client.h index f7ff728..2513a89 100644 --- a/lib/client.h +++ b/lib/client.h @@ -18,6 +18,9 @@ /** @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. */ @@ -47,107 +50,11 @@ int disorder_connect_generic(struct config *conf, 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 */ diff --git a/lib/eclient-stubs.c b/lib/eclient-stubs.c new file mode 100644 index 0000000..63f5163 --- /dev/null +++ b/lib/eclient-stubs.c @@ -0,0 +1,282 @@ +/* + * 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 . + */ +/** @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); +} + diff --git a/lib/eclient-stubs.h b/lib/eclient-stubs.h new file mode 100644 index 0000000..af3b389 --- /dev/null +++ b/lib/eclient-stubs.h @@ -0,0 +1,807 @@ +/* + * 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 . + */ +#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 diff --git a/lib/eclient.c b/lib/eclient.c index d03306e..d768f57 100644 --- a/lib/eclient.c +++ b/lib/eclient.c @@ -876,8 +876,31 @@ static void vstash_command(disorder_eclient *c, 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 @@ -1059,16 +1082,16 @@ static void list_response_opcallback(disorder_eclient *c, } /* 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); @@ -1092,316 +1115,12 @@ static int simple(disorder_eclient *c, 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, @@ -1436,217 +1155,6 @@ int disorder_eclient_rtp_address(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 @@ -1954,6 +1462,8 @@ static void logentry_global_pref(disorder_eclient *c, 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 diff --git a/lib/eclient.h b/lib/eclient.h index c44c5dc..876e2c6 100644 --- a/lib/eclient.h +++ b/lib/eclient.h @@ -17,6 +17,8 @@ */ /** @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 @@ -243,7 +245,7 @@ typedef void disorder_eclient_string_response(void *v, 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 @@ -267,9 +269,9 @@ typedef void disorder_eclient_integer_response(void *v, * @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 @@ -287,6 +289,8 @@ typedef void disorder_eclient_queue_response(void *v, 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 @@ -317,249 +321,25 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode); /* 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 diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 3d9e633..d5d8c42 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -25,6 +25,6 @@ include ${top_srcdir}/scripts/sedfiles.make 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) diff --git a/scripts/protocol b/scripts/protocol new file mode 100755 index 0000000..dec33e2 --- /dev/null +++ b/scripts/protocol @@ -0,0 +1,949 @@ +#! /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 . +# +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 .\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); +}