X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/ffc4dbaf1ee655263fd979d6c554ead000bef8e1..5f624baebe43889bb2810720cb9cc6d0812f49cb:/lib/eclient.c
diff --git a/lib/eclient.c b/lib/eclient.c
index 7023187..fad416a 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,16 @@ 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);
+static void logentry_global_pref(disorder_eclient *c, int nvec, char **vec);
/* Tables ********************************************************************/
@@ -201,17 +210,27 @@ 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(global_pref, 1, 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 +248,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 +286,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 +337,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 +383,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 +408,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 +424,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;
@@ -395,7 +442,7 @@ void disorder_eclient_polled(disorder_eclient *c, unsigned mode) {
/* 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;
}
@@ -468,7 +515,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 +571,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;
@@ -538,7 +586,7 @@ static void authbanner_opcallback(disorder_eclient *c,
|| !(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;
}
@@ -559,29 +607,31 @@ static void authbanner_opcallback(disorder_eclient *c,
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;
}
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;
@@ -589,7 +639,8 @@ static void authuser_opcallback(disorder_eclient *c,
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;
}
@@ -600,6 +651,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);
}
@@ -691,7 +743,7 @@ static void process_line(disorder_eclient *c, char *line) {
&& 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) {
@@ -762,6 +814,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 +834,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 +866,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;
@@ -813,12 +876,37 @@ 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,
- 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,59 +914,86 @@ 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]", c->line, c->ident);
+ 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) {
- if(op->completed) {
- if(c->protocol >= 2) {
+ if(completed) {
+ if(c->rc / 100 == 2 || c->rc == 555) {
+ if(c->rc == 555)
+ 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);
- }
- } else
- protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ completed(op->v, NULL, c->line + 4);
+ } else
+ 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(completed) {
+ if(c->rc / 100 == 2)
+ completed(op->v, NULL);
+ else
+ completed(op->v, errorstring(c));
+ }
}
/* error callback for queue_unmarshall */
@@ -886,13 +1001,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"));
@@ -901,22 +1020,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"));
@@ -924,51 +1050,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) {
- 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)
- protocol_error(c, op, -1, "%s: invalid volume response: %s",
- c->ident, c->line);
+ if(sscanf(c->line + 4, "%ld %ld", &l, &r) != 2 || l < 0 || r < 0)
+ 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,
@@ -979,7 +1108,7 @@ 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);
@@ -987,265 +1116,28 @@ static int simple(disorder_eclient *c,
}
/* 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,
- 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,
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
@@ -1263,20 +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);
-}
-
/* Log clients ***************************************************************/
/** @brief Monitor the server log
@@ -1301,7 +1179,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;
}
@@ -1317,6 +1195,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);
}
@@ -1332,16 +1211,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);
}
@@ -1428,6 +1314,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;
@@ -1510,6 +1450,20 @@ 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]);
+}
+
+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