X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/7378b5081fde6f48140327d5c072a8231934f027..b88fd7912fc173b50b4638fb1e25155999568990:/lib/eclient.c diff --git a/lib/eclient.c b/lib/eclient.c index a8ca322..676aa06 100644 --- a/lib/eclient.c +++ b/lib/eclient.c @@ -2,39 +2,32 @@ * 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 . */ /** @file lib/eclient.c * @brief Client code for event-driven programs */ -#include -#include "types.h" +#include "common.h" #include #include #include #include -#include -#include #include #include #include -#include -#include #include #include #include @@ -99,6 +92,7 @@ typedef void operation_callback(disorder_eclient *c, struct operation *op); struct operation { struct operation *next; /**< @brief next operation */ char *cmd; /**< @brief command to send or 0 */ + char **body; /**< @brief command body */ operation_callback *opcallback; /**< @brief internal completion callback */ void (*completed)(); /**< @brief user completion callback or 0 */ void *v; /**< @brief data for COMPLETED */ @@ -149,6 +143,9 @@ struct disorder_eclient { /** @brief Protocol version */ int protocol; + + /** @brief True if enabled */ + int enabled; }; /* Forward declarations ******************************************************/ @@ -169,6 +166,8 @@ static void stash_command(disorder_eclient *c, operation_callback *opcallback, void (*completed)(), void *v, + int nbody, + char **body, const char *cmd, ...); static void log_opcallback(disorder_eclient *c, struct operation *op); @@ -185,6 +184,15 @@ static void logentry_scratched(disorder_eclient *c, int nvec, char **vec); static void logentry_state(disorder_eclient *c, int nvec, char **vec); static void logentry_volume(disorder_eclient *c, int nvec, char **vec); static void logentry_rescanned(disorder_eclient *c, int nvec, char **vec); +static void logentry_user_add(disorder_eclient *c, int nvec, char **vec); +static void logentry_user_confirm(disorder_eclient *c, int nvec, char **vec); +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); /* Tables ********************************************************************/ @@ -201,17 +209,26 @@ struct logentry_handler { /** @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(moved, 1, 1), LE(playing, 1, 2), + LE(playlist_created, 2, 2), + LE(playlist_deleted, 1, 1), + LE(playlist_modified, 2, 2), LE(queue, 2, INT_MAX), LE(recent_added, 2, INT_MAX), LE(recent_removed, 1, 1), LE(removed, 1, 2), LE(rescanned, 0, 0), + LE(rights_changed, 1, 1), LE(scratched, 2, 2), LE(state, 1, 1), + LE(user_add, 1, 1), + LE(user_confirm, 1, 1), + LE(user_delete, 1, 1), + LE(user_edit, 2, 2), LE(volume, 2, 2) }; @@ -229,6 +246,7 @@ disorder_eclient *disorder_eclient_new(const disorder_eclient_callbacks *cb, c->callbacks = cb; c->u = u; c->opstail = &c->ops; + c->enabled = 1; vector_init(&c->vec); dynstr_init(&c->input); dynstr_init(&c->output); @@ -266,6 +284,16 @@ void disorder_eclient_close(disorder_eclient *c) { c->log_callbacks->state(c->log_v, c->statebits); } +/** @brief Permit new connection activity */ +void disorder_eclient_enable_connect(disorder_eclient *c) { + c->enabled = 1; +} + +/** @brief Suppress new connection activity */ +void disorder_eclient_disable_connect(disorder_eclient *c) { + c->enabled = 0; +} + /** @brief Return current state */ unsigned long disorder_eclient_state(const disorder_eclient *c) { return c->statebits | (c->state > state_connected ? DISORDER_CONNECTED : 0); @@ -307,6 +335,24 @@ static int protocol_error(disorder_eclient *c, struct operation *op, /* State machine *************************************************************/ +/** @brief Send an operation (into the output buffer) + * @param op Operation to send + */ +static void op_send(struct operation *op) { + disorder_eclient *const c = op->client; + put(c, op->cmd, strlen(op->cmd)); + if(op->body) { + for(int n = 0; op->body[n]; ++n) { + if(op->body[n][0] == '.') + put(c, ".", 1); + put(c, op->body[n], strlen(op->body[n])); + put(c, "\n", 1); + } + put(c, ".\n", 2); + } + op->sent = 1; +} + /** @brief Called when there's something to do * @param c Client * @param mode bitmap of @ref DISORDER_POLL_READ and/or @ref DISORDER_POLL_WRITE. @@ -335,9 +381,12 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode) { /* If there is no password yet then we cannot connect */ if(!config->password) { comms_error(c, "no password is configured"); + c->enabled = 0; return; } - start_connect(c); + /* Only try to connect if enabled */ + if(c->enabled) + start_connect(c); /* might now be state_disconnected (on error), state_connecting (slow * connect) or state_connected (fast connect). If state_disconnected then * we just rely on a periodic callback from the event loop sometime. */ @@ -357,7 +406,7 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode) { D(("state_connected")); /* We just connected. Initiate the authentication protocol. */ stash_command(c, 1/*queuejump*/, authbanner_opcallback, - 0/*completed*/, 0/*v*/, 0/*cmd*/); + 0/*completed*/, 0/*v*/, -1/*nbody*/, 0/*body*/, 0/*cmd*/); /* We never stay is state_connected very long. We could in principle jump * straight to state_cmdresponse since there's actually no command to * send, but that would arguably be cheating. */ @@ -373,17 +422,13 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode) { if(c->authenticated) { /* Transmit all unsent operations */ for(op = c->ops; op; op = op->next) { - if(!op->sent) { - put(c, op->cmd, strlen(op->cmd)); - op->sent = 1; - } + if(!op->sent) + op_send(op); } } else { /* Just send the head operation */ - if(c->ops->cmd && !c->ops->sent) { - put(c, c->ops->cmd, strlen(c->ops->cmd)); - c->ops->sent = 1; - } + if(c->ops->cmd && !c->ops->sent) + op_send(c->ops); } /* Awaiting response for the operation at the head of the list */ c->state = state_cmdresponse; @@ -468,7 +513,7 @@ static int start_connect(disorder_eclient *c) { socklen_t len; D(("start_connect")); - if((len = find_server(&sa, &c->ident)) == (socklen_t)-1) + if((len = find_server(config, &sa, &c->ident)) == (socklen_t)-1) return comms_error(c, "cannot look up server"); /* TODO better error */ if(c->fd != -1) { xclose(c->fd); @@ -524,6 +569,7 @@ static void maybe_connected(disorder_eclient *c) { /* Authentication ************************************************************/ +/** @brief Called with the greeting from the server */ static void authbanner_opcallback(disorder_eclient *c, struct operation *op) { size_t nonce_len; @@ -578,10 +624,12 @@ static void authbanner_opcallback(disorder_eclient *c, return; } stash_command(c, 1/*queuejump*/, authuser_opcallback, 0/*completed*/, 0/*v*/, + -1/*nbody*/, 0/*body*/, "user", quoteutf8(config->username), quoteutf8(res), (char *)0); } +/** @brief Called with the response to the @c user command */ static void authuser_opcallback(disorder_eclient *c, struct operation *op) { char *r; @@ -590,6 +638,7 @@ static void authuser_opcallback(disorder_eclient *c, if(c->rc / 100 != 2) { /* Wrong password or something. We cannot proceed. */ protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); + c->enabled = 0; disorder_eclient_close(c); return; } @@ -600,6 +649,7 @@ static void authuser_opcallback(disorder_eclient *c, if(c->log_callbacks && !(c->ops && c->ops->opcallback == log_opcallback)) /* We are a log client, switch to logging mode */ stash_command(c, 0/*queuejump*/, log_opcallback, 0/*completed*/, c->log_v, + -1/*nbody*/, 0/*body*/, "log", (char *)0); } @@ -762,6 +812,8 @@ static void stash_command_vector(disorder_eclient *c, operation_callback *opcallback, void (*completed)(), void *v, + int nbody, + char **body, int ncmd, char **cmd) { struct operation *op = xmalloc(sizeof *op); @@ -780,6 +832,13 @@ static void stash_command_vector(disorder_eclient *c, op->cmd = d.vec; } else op->cmd = 0; /* usually, awaiting challenge */ + if(nbody >= 0) { + op->body = xcalloc(nbody + 1, sizeof (char *)); + for(n = 0; n < nbody; ++n) + op->body[n] = xstrdup(body[n]); + op->body[n] = 0; + } else + op->body = NULL; op->opcallback = opcallback; op->completed = completed; op->v = v; @@ -805,6 +864,8 @@ static void vstash_command(disorder_eclient *c, operation_callback *opcallback, void (*completed)(), void *v, + int nbody, + char **body, const char *cmd, va_list ap) { char *arg; struct vector vec; @@ -816,9 +877,11 @@ static void vstash_command(disorder_eclient *c, while((arg = va_arg(ap, char *))) vector_append(&vec, arg); stash_command_vector(c, queuejump, opcallback, completed, v, - vec.nvec, vec.vec); + nbody, body, vec.nvec, vec.vec); } else - stash_command_vector(c, queuejump, opcallback, completed, v, 0, 0); + stash_command_vector(c, queuejump, opcallback, completed, v, + nbody, body, + 0, 0); } static void stash_command(disorder_eclient *c, @@ -826,61 +889,84 @@ static void stash_command(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, queuejump, opcallback, completed, v, cmd, ap); + vstash_command(c, queuejump, opcallback, completed, v, nbody, body, cmd, ap); va_end(ap); } /* Command support ***********************************************************/ +static const char *errorstring(disorder_eclient *c) { + char *s; + + byte_xasprintf(&s, "%s: %s: %d", c->ident, c->line, c->rc); + return s; +} + /* for commands with a quoted string response */ static void string_response_opcallback(disorder_eclient *c, struct operation *op) { + disorder_eclient_string_response *completed + = (disorder_eclient_string_response *)op->completed; + D(("string_response_callback")); if(c->rc / 100 == 2 || c->rc == 555) { if(op->completed) { if(c->rc == 555) - ((disorder_eclient_string_response *)op->completed)(op->v, NULL); + completed(op->v, NULL, NULL); else if(c->protocol >= 2) { char **rr = split(c->line + 4, 0, SPLIT_QUOTES, 0, 0); if(rr && *rr) - ((disorder_eclient_string_response *)op->completed)(op->v, *rr); + completed(op->v, NULL, *rr); else - protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); + /* TODO error message a is bit lame but generally indicates a server + * bug rather than anything the user can address */ + completed(op->v, "error parsing response", NULL); } else - ((disorder_eclient_string_response *)op->completed)(op->v, - c->line + 4); + completed(op->v, NULL, c->line + 4); } } else - protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); + completed(op->v, errorstring(c), NULL); } /* for commands with a simple integer response */ static void integer_response_opcallback(disorder_eclient *c, struct operation *op) { + disorder_eclient_integer_response *completed + = (disorder_eclient_integer_response *)op->completed; + D(("string_response_callback")); if(c->rc / 100 == 2) { - if(op->completed) - ((disorder_eclient_integer_response *)op->completed) - (op->v, strtol(c->line + 4, 0, 10)); + long n; + int e; + + e = xstrtol(&n, c->line + 4, 0, 10); + if(e) + completed(op->v, strerror(e), 0); + else + completed(op->v, 0, n); } else - protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); + completed(op->v, errorstring(c), 0); } /* for commands with no response */ static void no_response_opcallback(disorder_eclient *c, struct operation *op) { + disorder_eclient_no_response *completed + = (disorder_eclient_no_response *)op->completed; + D(("no_response_callback")); - if(c->rc / 100 == 2) { - if(op->completed) - ((disorder_eclient_no_response *)op->completed)(op->v); - } else - protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); + if(c->rc / 100 == 2) + completed(op->v, NULL); + else + completed(op->v, errorstring(c)); } /* error callback for queue_unmarshall */ @@ -888,13 +974,17 @@ static void eclient_queue_error(const char *msg, void *u) { struct operation *op = u; + /* TODO don't use protocol_error here */ protocol_error(op->client, op, -1, "error parsing queue entry: %s", msg); } /* for commands that expect a queue dump */ static void queue_response_opcallback(disorder_eclient *c, struct operation *op) { + disorder_eclient_queue_response *const completed + = (disorder_eclient_queue_response *)op->completed; int n; + int parse_failed = 0; struct queue_entry *q, *qh = 0, **qtail = &qh, *qlast = 0; D(("queue_response_callback")); @@ -903,22 +993,29 @@ static void queue_response_opcallback(disorder_eclient *c, for(n = 0; n < c->vec.nvec; ++n) { q = xmalloc(sizeof *q); D(("queue_unmarshall %s", c->vec.vec[n])); - if(!queue_unmarshall(q, c->vec.vec[n], eclient_queue_error, op)) { + if(!queue_unmarshall(q, c->vec.vec[n], NULL, op)) { q->prev = qlast; *qtail = q; qtail = &q->next; qlast = q; - } + } else + parse_failed = 1; } - if(op->completed) - ((disorder_eclient_queue_response *)op->completed)(op->v, qh); + /* Currently we pass the partial queue to the callback along with the + * error. This might not be very useful in practice... */ + if(parse_failed) + completed(op->v, "cannot parse result", qh); + else + completed(op->v, 0, qh); } else - protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); + completed(op->v, errorstring(c), 0); } /* for 'playing' */ static void playing_response_opcallback(disorder_eclient *c, struct operation *op) { + disorder_eclient_queue_response *const completed + = (disorder_eclient_queue_response *)op->completed; struct queue_entry *q; D(("playing_response_callback")); @@ -926,51 +1023,54 @@ static void playing_response_opcallback(disorder_eclient *c, switch(c->rc % 10) { case 2: if(queue_unmarshall(q = xmalloc(sizeof *q), c->line + 4, - eclient_queue_error, c)) - return; + NULL, c)) + completed(op->v, "cannot parse result", 0); + else + completed(op->v, 0, q); break; case 9: - q = 0; + completed(op->v, 0, 0); break; default: - protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); - return; + completed(op->v, errorstring(c), 0); + break; } - if(op->completed) - ((disorder_eclient_queue_response *)op->completed)(op->v, q); } else - protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); + completed(op->v, errorstring(c), 0); } /* for commands that expect a list of some sort */ static void list_response_opcallback(disorder_eclient *c, struct operation *op) { + disorder_eclient_list_response *const completed = + (disorder_eclient_list_response *)op->completed; + D(("list_response_callback")); - if(c->rc / 100 == 2) { - if(op->completed) - ((disorder_eclient_list_response *)op->completed)(op->v, - c->vec.nvec, - c->vec.vec); - } else - protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); + if(c->rc / 100 == 2) + completed(op->v, NULL, c->vec.nvec, c->vec.vec); + else if(c->rc == 555) + completed(op->v, NULL, -1, NULL); + else + completed(op->v, errorstring(c), 0, 0); } /* 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; 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) - protocol_error(c, op, -1, "%s: invalid volume response: %s", - c->ident, c->line); + completed(op->v, "cannot parse volume response", 0, 0); else - ((disorder_eclient_volume_response *)op->completed)(op->v, l, r); + completed(op->v, 0, l, r); } } else - protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); + completed(op->v, errorstring(c), 0, 0); } static int simple(disorder_eclient *c, @@ -981,7 +1081,24 @@ static int simple(disorder_eclient *c, va_list ap; va_start(ap, cmd); - vstash_command(c, 0/*queuejump*/, opcallback, completed, v, cmd, ap); + vstash_command(c, 0/*queuejump*/, opcallback, completed, v, -1, 0, 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; +} + +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); @@ -1066,7 +1183,7 @@ int disorder_eclient_moveafter(disorder_eclient *c, for(n = 0; n < nids; ++n) vector_append(&vec, (char *)ids[n]); stash_command_vector(c, 0/*queuejump*/, no_response_opcallback, completed, v, - vec.nvec, vec.vec); + -1, 0, vec.nvec, vec.vec); disorder_eclient_polled(c, 0); return 0; } @@ -1238,16 +1355,19 @@ int disorder_eclient_new_tracks(disorder_eclient *c, static void rtp_response_opcallback(disorder_eclient *c, struct operation *op) { + disorder_eclient_list_response *const completed = + (disorder_eclient_list_response *)op->completed; D(("rtp_response_opcallback")); if(c->rc / 100 == 2) { - if(op->completed) { - int nvec; - char **vec = split(c->line + 4, &nvec, SPLIT_QUOTES, 0, 0); + int nvec; + char **vec = split(c->line + 4, &nvec, SPLIT_QUOTES, 0, 0); - ((disorder_eclient_list_response *)op->completed)(op->v, nvec, vec); - } + if(vec) + completed(op->v, NULL, nvec, vec); + else + completed(op->v, "error parsing response", 0, 0); } else - protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line); + completed(op->v, errorstring(c), 0, 0); } /** @brief Determine the RTP target address @@ -1345,6 +1465,137 @@ int disorder_eclient_adduser(disorder_eclient *c, "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 @@ -1369,7 +1620,7 @@ int disorder_eclient_log(disorder_eclient *c, if(c->log_callbacks->state) c->log_callbacks->state(c->log_v, c->statebits); stash_command(c, 0/*queuejump*/, log_opcallback, 0/*completed*/, v, - "log", (char *)0); + -1, 0, "log", (char *)0); disorder_eclient_polled(c, 0); return 0; } @@ -1385,6 +1636,7 @@ static void log_opcallback(disorder_eclient *c, /* error callback for log line parsing */ static void logline_error(const char *msg, void *u) { disorder_eclient *c = u; + /* TODO don't use protocol_error here */ protocol_error(c, c->ops, -1, "error parsing log line: %s", msg); } @@ -1400,16 +1652,23 @@ static void logline(disorder_eclient *c, const char *line) { * reported */ if(sscanf(vec[0], "%"SCNxMAX, &when) != 1) { /* probably the wrong side of a format change */ + /* TODO don't use protocol_error here */ protocol_error(c, c->ops, -1, "invalid log timestamp '%s'", vec[0]); return; } /* TODO: do something with the time */ - n = TABLE_FIND(logentry_handlers, struct logentry_handler, name, vec[1]); - if(n < 0) return; /* probably a future command */ + //fprintf(stderr, "log key: %s\n", vec[1]); + n = TABLE_FIND(logentry_handlers, name, vec[1]); + if(n < 0) { + //fprintf(stderr, "...not found\n"); + return; /* probably a future command */ + } vec += 2; nvec -= 2; - if(nvec < logentry_handlers[n].min || nvec > logentry_handlers[n].max) + if(nvec < logentry_handlers[n].min || nvec > logentry_handlers[n].max) { + //fprintf(stderr, "...wrong # args\n"); return; + } logentry_handlers[n].handler(c, nvec, vec); } @@ -1496,6 +1755,60 @@ static void logentry_scratched(disorder_eclient *c, c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED); } +static void logentry_user_add(disorder_eclient *c, + int attribute((unused)) nvec, char **vec) { + if(c->log_callbacks->user_add) + c->log_callbacks->user_add(c->log_v, vec[0]); +} + +static void logentry_user_confirm(disorder_eclient *c, + int attribute((unused)) nvec, char **vec) { + if(c->log_callbacks->user_confirm) + c->log_callbacks->user_confirm(c->log_v, vec[0]); +} + +static void logentry_user_delete(disorder_eclient *c, + int attribute((unused)) nvec, char **vec) { + if(c->log_callbacks->user_delete) + c->log_callbacks->user_delete(c->log_v, vec[0]); +} + +static void logentry_user_edit(disorder_eclient *c, + int attribute((unused)) nvec, char **vec) { + if(c->log_callbacks->user_edit) + c->log_callbacks->user_edit(c->log_v, vec[0], vec[1]); +} + +static void logentry_rights_changed(disorder_eclient *c, + int attribute((unused)) nvec, char **vec) { + if(c->log_callbacks->rights_changed) { + rights_type r; + if(!parse_rights(vec[0], &r, 0/*report*/)) + c->log_callbacks->rights_changed(c->log_v, r); + } +} + +static void logentry_playlist_created(disorder_eclient *c, + int attribute((unused)) nvec, + char **vec) { + if(c->log_callbacks->playlist_created) + c->log_callbacks->playlist_created(c->log_v, vec[0], vec[1]); +} + +static void logentry_playlist_deleted(disorder_eclient *c, + int attribute((unused)) nvec, + char **vec) { + if(c->log_callbacks->playlist_deleted) + c->log_callbacks->playlist_deleted(c->log_v, vec[0]); +} + +static void logentry_playlist_modified(disorder_eclient *c, + int attribute((unused)) nvec, + char **vec) { + if(c->log_callbacks->playlist_modified) + c->log_callbacks->playlist_modified(c->log_v, vec[0], vec[1]); +} + static const struct { unsigned long bit; const char *enable; @@ -1578,6 +1891,12 @@ char *disorder_eclient_interpret_state(unsigned long statebits) { 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]); +} + /* Local Variables: c-basic-offset:2