X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/8f763f1bcd527d7f2bb27a3bf3677571c8ffd2d9..146e86fbdea5a7a4c846e7b4a210ee179e1943d5:/lib/eclient.c diff --git a/lib/eclient.c b/lib/eclient.c index d0cf661..e175c22 100644 --- a/lib/eclient.c +++ b/lib/eclient.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder. - * Copyright (C) 2006 Richard Kettlewell + * Copyright (C) 2006, 2007 Richard Kettlewell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -170,6 +170,7 @@ static void logentry_removed(disorder_eclient *c, int nvec, char **vec); 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); /* Tables ********************************************************************/ @@ -194,6 +195,7 @@ static const struct logentry_handler logentry_handlers[] = { LE(recent_added, 2, INT_MAX), LE(recent_removed, 1, 1), LE(removed, 1, 2), + LE(rescanned, 0, 0), LE(scratched, 2, 2), LE(state, 1, 1), LE(volume, 2, 2) @@ -249,6 +251,9 @@ void disorder_eclient_close(disorder_eclient *c) { /* We'll need to resend all operations */ for(op = c->ops; op; op = op->next) op->sent = 0; + /* Drop our use a hint that we're disconnected */ + if(c->log_callbacks && c->log_callbacks->state) + c->log_callbacks->state(c->log_v, c->statebits); } /** @brief Return current state */ @@ -488,6 +493,8 @@ static void maybe_connected(disorder_eclient *c) { c->state = state_connected; byte_xasprintf(&r, "connected to %s", c->ident); c->callbacks->report(c->u, r); + /* If this is a log client we expect to get a bunch of updates from the + * server straight away */ } } @@ -498,16 +505,31 @@ static void authbanner_opcallback(disorder_eclient *c, size_t nonce_len; const unsigned char *nonce; const char *res; + char **rvec; + int nrvec; + const char *algo = "SHA1"; D(("authbanner_opcallback")); - if(c->rc / 100 != 2) { - /* Banner told us to go away. We cannot proceed. */ + if(c->rc / 100 != 2 + || !(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); disorder_eclient_close(c); return; } - nonce = unhex(c->line + 4, &nonce_len); - res = authhash(nonce, nonce_len, config->password); + if(nrvec > 1) { + algo = *rvec++; + --nrvec; + } + nonce = unhex(rvec[0], &nonce_len); + res = authhash(nonce, nonce_len, config->password, algo); + if(!res) { + protocol_error(c, op, c->rc, "%s: unknown authentication algorithm '%s'", + c->ident, algo); + disorder_eclient_close(c); + return; + } stash_command(c, 1/*queuejump*/, authuser_opcallback, 0/*completed*/, 0/*v*/, "user", quoteutf8(config->username), quoteutf8(res), (char *)0); @@ -629,7 +651,7 @@ static void process_line(disorder_eclient *c, char *line) { case 3: /* We need to collect the body. */ c->state = state_body; - c->vec.nvec = 0; + vector_init(&c->vec); break; case 4: assert(c->log_callbacks != 0); @@ -1136,6 +1158,25 @@ int disorder_eclient_nop(disorder_eclient *c, "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); +} + /* Log clients ***************************************************************/ /** @brief Monitor the server log @@ -1146,6 +1187,9 @@ int disorder_eclient_nop(disorder_eclient *c, * Once a client is being used for logging it cannot be used for anything else. * There is magic in authuser_opcallback() to re-submit the @c log command * after reconnection. + * + * NB that the @c state callback may be called from within this function, + * i.e. not solely later on from the event loop callback. */ int disorder_eclient_log(disorder_eclient *c, const disorder_eclient_log_callbacks *callbacks, @@ -1153,6 +1197,9 @@ int disorder_eclient_log(disorder_eclient *c, if(c->log_callbacks) return -1; c->log_callbacks = callbacks; c->log_v = v; + /* Repoort initial state */ + 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); return 0; @@ -1178,7 +1225,7 @@ static void logline(disorder_eclient *c, const char *line) { char **vec; uintmax_t when; - D(("log_opcallback [%s]", line)); + D(("logline [%s]", line)); vec = split(line, &nvec, SPLIT_QUOTES, logline_error, c); if(nvec < 2) return; /* probably an error, already * reported */ @@ -1200,7 +1247,7 @@ static void logline(disorder_eclient *c, const char *line) { static void logentry_completed(disorder_eclient *c, int attribute((unused)) nvec, char **vec) { if(!c->log_callbacks->completed) return; - c->state &= ~DISORDER_PLAYING; + c->statebits &= ~DISORDER_PLAYING; c->log_callbacks->completed(c->log_v, vec[0]); if(c->log_callbacks->state) c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED); @@ -1209,7 +1256,7 @@ static void logentry_completed(disorder_eclient *c, static void logentry_failed(disorder_eclient *c, int attribute((unused)) nvec, char **vec) { if(!c->log_callbacks->failed)return; - c->state &= ~DISORDER_PLAYING; + c->statebits &= ~DISORDER_PLAYING; c->log_callbacks->failed(c->log_v, vec[0], vec[1]); if(c->log_callbacks->state) c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED); @@ -1224,7 +1271,7 @@ static void logentry_moved(disorder_eclient *c, static void logentry_playing(disorder_eclient *c, int attribute((unused)) nvec, char **vec) { if(!c->log_callbacks->playing) return; - c->state |= DISORDER_PLAYING; + c->statebits |= DISORDER_PLAYING; c->log_callbacks->playing(c->log_v, vec[0], vec[1]); if(c->log_callbacks->state) c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED); @@ -1264,10 +1311,17 @@ static void logentry_removed(disorder_eclient *c, c->log_callbacks->removed(c->log_v, vec[0], vec[1]); } +static void logentry_rescanned(disorder_eclient *c, + int attribute((unused)) nvec, + char attribute((unused)) **vec) { + if(!c->log_callbacks->rescanned) return; + c->log_callbacks->rescanned(c->log_v); +} + static void logentry_scratched(disorder_eclient *c, int attribute((unused)) nvec, char **vec) { if(!c->log_callbacks->scratched) return; - c->state &= ~DISORDER_PLAYING; + c->statebits &= ~DISORDER_PLAYING; c->log_callbacks->scratched(c->log_v, vec[0], vec[1]); if(c->log_callbacks->state) c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED); @@ -1281,18 +1335,21 @@ static const struct { { DISORDER_PLAYING_ENABLED, "enable_play", "disable_play" }, { DISORDER_RANDOM_ENABLED, "enable_random", "disable_random" }, { DISORDER_TRACK_PAUSED, "pause", "resume" }, + { DISORDER_PLAYING, "playing", "completed" }, + { DISORDER_PLAYING, 0, "scratched" }, + { DISORDER_PLAYING, 0, "failed" }, }; -#define NSTATES (int)(sizeof states / sizeof *states) +#define NSTATES (int)(sizeof statestrings / sizeof *statestrings) static void logentry_state(disorder_eclient *c, int attribute((unused)) nvec, char **vec) { int n; for(n = 0; n < NSTATES; ++n) - if(!strcmp(vec[0], statestrings[n].enable)) { + if(statestrings[n].enable && !strcmp(vec[0], statestrings[n].enable)) { c->statebits |= statestrings[n].bit; break; - } else if(!strcmp(vec[0], statestrings[n].disable)) { + } else if(statestrings[n].disable && !strcmp(vec[0], statestrings[n].disable)) { c->statebits &= ~statestrings[n].bit; break; } @@ -1313,6 +1370,45 @@ static void logentry_volume(disorder_eclient *c, c->log_callbacks->volume(c->log_v, (int)l, (int)r); } +/** @brief Convert @p statebits to a string */ +char *disorder_eclient_interpret_state(unsigned long statebits) { + struct dynstr d[1]; + size_t n; + + static const struct { + unsigned long bit; + const char *name; + } bits[] = { + { DISORDER_PLAYING_ENABLED, "playing_enabled" }, + { DISORDER_RANDOM_ENABLED, "random_enabled" }, + { DISORDER_TRACK_PAUSED, "track_paused" }, + { DISORDER_PLAYING, "playing" }, + { DISORDER_CONNECTED, "connected" }, + }; +#define NBITS (sizeof bits / sizeof *bits) + + dynstr_init(d); + if(!statebits) + dynstr_append(d, '0'); + for(n = 0; n < NBITS; ++n) + if(statebits & bits[n].bit) { + if(d->nvec) + dynstr_append(d, '|'); + dynstr_append_string(d, bits[n].name); + statebits ^= bits[n].bit; + } + if(statebits) { + char s[20]; + + if(d->nvec) + dynstr_append(d, '|'); + sprintf(s, "%#lx", statebits); + dynstr_append_string(d, s); + } + dynstr_terminate(d); + return d->vec; +} + /* Local Variables: c-basic-offset:2