* This file is part of DisOrder.
* Copyright (C) 2006-2008 Richard Kettlewell
*
- * This program is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * 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.
- *
+ *
+ * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
- * USA
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file lib/eclient.c
* @brief Client code for event-driven programs
static void logentry_user_delete(disorder_eclient *c, int nvec, char **vec);
static void logentry_user_edit(disorder_eclient *c, int nvec, char **vec);
static void logentry_rights_changed(disorder_eclient *c, int nvec, char **vec);
+static void logentry_adopted(disorder_eclient *c, int nvec, char **vec);
static void logentry_playlist_created(disorder_eclient *c, int nvec, char **vec);
static void logentry_playlist_deleted(disorder_eclient *c, int nvec, char **vec);
static void logentry_playlist_modified(disorder_eclient *c, int nvec, char **vec);
+static void logentry_global_pref(disorder_eclient *c, int nvec, char **vec);
/* Tables ********************************************************************/
/** @brief Table for parsing log entries */
static const struct logentry_handler logentry_handlers[] = {
#define LE(X, MIN, MAX) { #X, MIN, MAX, logentry_##X }
+ LE(adopted, 2, 2),
LE(completed, 1, 1),
LE(failed, 2, 2),
+ LE(global_pref, 1, 2),
LE(moved, 1, 1),
LE(playing, 1, 2),
LE(playlist_created, 2, 2),
/* Queue up a byte to send */
if(c->state == state_log
&& c->output.nvec == 0
- && time(&now) - c->last_prod > LOG_PROD_INTERVAL) {
+ && xtime(&now) - c->last_prod > LOG_PROD_INTERVAL) {
put(c, "x", 1);
c->last_prod = now;
}
|| !(rvec = split(c->line + 4, &nrvec, SPLIT_QUOTES, 0, 0))
|| nrvec < 1) {
/* Banner told us to go away, or was malformed. We cannot proceed. */
- protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ protocol_error(c, op, c->rc, "%s [%s]", c->line, c->ident);
disorder_eclient_close(c);
return;
}
challenge = *rvec++;
break;
default:
- protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ protocol_error(c, op, c->rc, "%s [%s]", c->line, c->ident);
disorder_eclient_close(c);
return;
}
c->protocol = atoi(protocol);
if(c->protocol < 1 || c->protocol > 2) {
- protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ protocol_error(c, op, c->rc, "%s [%s]", c->line, c->ident);
disorder_eclient_close(c);
return;
}
nonce = unhex(challenge, &nonce_len);
res = authhash(nonce, nonce_len, config->password, algorithm);
if(!res) {
- protocol_error(c, op, c->rc, "%s: unknown authentication algorithm '%s'",
- c->ident, algorithm);
+ protocol_error(c, op, c->rc, "unknown authentication algorithm '%s' [%s]",
+ algorithm, c->ident);
disorder_eclient_close(c);
return;
}
D(("authuser_opcallback"));
if(c->rc / 100 != 2) {
/* Wrong password or something. We cannot proceed. */
- protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ protocol_error(c, op, c->rc, "%s [%s]", c->line, c->ident);
c->enabled = 0;
disorder_eclient_close(c);
return;
&& line[1] >= '0' && line[1] <= '9'
&& line[2] >= '0' && line[2] <= '9'
&& line[3] == ' '))
- fatal(0, "invalid response from server: %s", line);
+ disorder_fatal(0, "invalid response from server: %s", line);
c->rc = (line[0] * 10 + line[1]) * 10 + line[2] - 111 * '0';
c->line = line;
switch(c->rc % 10) {
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
static const char *errorstring(disorder_eclient *c) {
char *s;
- byte_xasprintf(&s, "%s: %s: %d", c->ident, c->line, c->rc);
+ byte_xasprintf(&s, "%s [%s]", c->line, c->ident);
return s;
}
= (disorder_eclient_string_response *)op->completed;
D(("string_response_callback"));
- if(c->rc / 100 == 2 || c->rc == 555) {
- if(op->completed) {
+ if(completed) {
+ if(c->rc / 100 == 2 || c->rc == 555) {
if(c->rc == 555)
completed(op->v, NULL, NULL);
else if(c->protocol >= 2) {
completed(op->v, "error parsing response", NULL);
} else
completed(op->v, NULL, c->line + 4);
- }
- } else
- completed(op->v, errorstring(c), NULL);
+ } else
+ completed(op->v, errorstring(c), NULL);
+ }
}
/* for commands with a simple integer response */
= (disorder_eclient_no_response *)op->completed;
D(("no_response_callback"));
- if(c->rc / 100 == 2)
- completed(op->v, NULL);
- else
- completed(op->v, errorstring(c));
+ if(completed) {
+ if(c->rc / 100 == 2)
+ completed(op->v, NULL);
+ else
+ completed(op->v, errorstring(c));
+ }
}
/* error callback for queue_unmarshall */
}
/* for volume */
-static void volume_response_opcallback(disorder_eclient *c,
- struct operation *op) {
- disorder_eclient_volume_response *completed
- = (disorder_eclient_volume_response *)op->completed;
- int l, r;
+static void pair_integer_response_opcallback(disorder_eclient *c,
+ struct operation *op) {
+ disorder_eclient_pair_integer_response *completed
+ = (disorder_eclient_pair_integer_response *)op->completed;
+ long l, r;
D(("volume_response_callback"));
if(c->rc / 100 == 2) {
if(op->completed) {
- if(sscanf(c->line + 4, "%d %d", &l, &r) != 2 || l < 0 || r < 0)
+ if(sscanf(c->line + 4, "%ld %ld", &l, &r) != 2 || l < 0 || r < 0)
completed(op->v, "cannot parse volume response", 0, 0);
else
completed(op->v, 0, l, r);
return 0;
}
-static int simple_body(disorder_eclient *c,
- operation_callback *opcallback,
- void (*completed)(),
- void *v,
- int nbody,
- char **body,
- const char *cmd, ...) {
- va_list ap;
-
- va_start(ap, cmd);
- vstash_command(c, 0/*queuejump*/, opcallback, completed, v, nbody, body, cmd, ap);
- va_end(ap);
- /* Give the state machine a kick, since we might be in state_idle */
- disorder_eclient_polled(c, 0);
- return 0;
-}
-
/* Commands ******************************************************************/
-
-int disorder_eclient_version(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- void *v) {
- return simple(c, string_response_opcallback, (void (*)())completed, v,
- "version", (char *)0);
-}
-
-int disorder_eclient_namepart(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *track,
- const char *context,
- const char *part,
- void *v) {
- return simple(c, string_response_opcallback, (void (*)())completed, v,
- "part", track, context, part, (char *)0);
-}
-
-int disorder_eclient_play(disorder_eclient *c,
- const char *track,
- disorder_eclient_no_response *completed,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "play", track, (char *)0);
-}
-
-int disorder_eclient_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);
-}
-
-/** @brief Get the last @p max added tracks
- * @param c Client
- * @param completed Called with list
- * @param max Number of tracks to get, 0 for all
- * @param v Passed to @p completed
- *
- * The first track in the list is the most recently added.
- */
-int disorder_eclient_new_tracks(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- int max,
- void *v) {
- char limit[32];
-
- sprintf(limit, "%d", max);
- return simple(c, list_response_opcallback, (void (*)())completed, v,
- "new", limit, (char *)0);
+ return disorder_eclient_scratch(c, completed, 0, v);
}
static void rtp_response_opcallback(disorder_eclient *c,
"rtp-address", (char *)0);
}
-/** @brief Get the list of users
- * @param c Client
- * @param completed Called with list of users
- * @param v Passed to @p completed
- *
- * The user list is not sorted in any particular order.
- */
-int disorder_eclient_users(disorder_eclient *c,
- disorder_eclient_list_response *completed,
- void *v) {
- return simple(c, list_response_opcallback, (void (*)())completed, v,
- "users", (char *)0);
-}
-
-/** @brief Delete a user
- * @param c Client
- * @param completed Called on completion
- * @param user User to delete
- * @param v Passed to @p completed
- */
-int disorder_eclient_deluser(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *user,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "deluser", user, (char *)0);
-}
-
-/** @brief Get a user property
- * @param c Client
- * @param completed Called on completion
- * @param user User to look up
- * @param property Property to look up
- * @param v Passed to @p completed
- */
-int disorder_eclient_userinfo(disorder_eclient *c,
- disorder_eclient_string_response *completed,
- const char *user,
- const char *property,
- void *v) {
- return simple(c, string_response_opcallback, (void (*)())completed, v,
- "userinfo", user, property, (char *)0);
-}
-
-/** @brief Modify a user property
- * @param c Client
- * @param completed Called on completion
- * @param user User to modify
- * @param property Property to modify
- * @param value New property value
- * @param v Passed to @p completed
- */
-int disorder_eclient_edituser(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *user,
- const char *property,
- const char *value,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "edituser", user, property, value, (char *)0);
-}
-
-/** @brief Create a new user
- * @param c Client
- * @param completed Called on completion
- * @param user User to create
- * @param password Initial password
- * @param rights Initial rights or NULL
- * @param v Passed to @p completed
- */
-int disorder_eclient_adduser(disorder_eclient *c,
- disorder_eclient_no_response *completed,
- const char *user,
- const char *password,
- const char *rights,
- void *v) {
- return simple(c, no_response_opcallback, (void (*)())completed, v,
- "adduser", user, password, rights, (char *)0);
-}
-
-/** @brief 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
return d->vec;
}
+static void logentry_adopted(disorder_eclient *c,
+ int attribute((unused)) nvec, char **vec) {
+ if(c->log_callbacks->adopted)
+ c->log_callbacks->adopted(c->log_v, vec[0], vec[1]);
+}
+
+static void logentry_global_pref(disorder_eclient *c,
+ int nvec, char **vec) {
+ if(c->log_callbacks->global_pref)
+ 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