X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/658c8a359409a339d71910f68452bfaa751734c0..d42e98caaaf4f07c8d1252236f03eb68b8be4619:/lib/eclient.c diff --git a/lib/eclient.c b/lib/eclient.c index 1fc0bfe..b7b502b 100644 --- a/lib/eclient.c +++ b/lib/eclient.c @@ -2,20 +2,18 @@ * 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 @@ -144,6 +142,9 @@ struct disorder_eclient { /** @brief Protocol version */ int protocol; + + /** @brief True if enabled */ + int enabled; }; /* Forward declarations ******************************************************/ @@ -180,6 +181,11 @@ 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); /* Tables ********************************************************************/ @@ -205,8 +211,13 @@ static const struct logentry_handler logentry_handlers[] = { 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) }; @@ -224,6 +235,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); @@ -261,6 +273,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); @@ -330,9 +352,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. */ @@ -463,7 +488,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); @@ -587,6 +612,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; } @@ -913,7 +939,10 @@ static void eclient_queue_error(const char *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")); @@ -922,23 +951,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 - /* TODO don't use protocol_error here */ - 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")); @@ -946,56 +981,52 @@ 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: - /* TODO don't use protocol_error here */ - 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 - /* TODO don't use protocol_error here */ - 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 - /* TODO don't use protocol_error here */ - 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 + 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) - /* TODO don't use protocol_error here */ - 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 - /* TODO don't use protocol_error here */ - 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, @@ -1263,17 +1294,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 - /* TODO don't use protocol_error here */ - 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 @@ -1371,6 +1404,20 @@ 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); +} + /* Log clients ***************************************************************/ /** @brief Monitor the server log @@ -1432,12 +1479,18 @@ static void logline(disorder_eclient *c, const char *line) { return; } /* TODO: do something with the time */ + //fprintf(stderr, "log key: %s\n", vec[1]); n = TABLE_FIND(logentry_handlers, name, vec[1]); - if(n < 0) return; /* probably a future command */ + 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); } @@ -1524,6 +1577,39 @@ 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 const struct { unsigned long bit; const char *enable;