chiark / gitweb /
Merge playlist branch against trunk to date.
[disorder] / lib / eclient.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2006-2008 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file lib/eclient.c
19  * @brief Client code for event-driven programs
20  */
21
22 #include "common.h"
23
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <sys/un.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <netdb.h>
31 #include <inttypes.h>
32 #include <stddef.h>
33 #include <time.h>
34
35 #include "log.h"
36 #include "mem.h"
37 #include "configuration.h"
38 #include "queue.h"
39 #include "eclient.h"
40 #include "charset.h"
41 #include "hex.h"
42 #include "split.h"
43 #include "vector.h"
44 #include "inputline.h"
45 #include "kvp.h"
46 #include "syscalls.h"
47 #include "printf.h"
48 #include "addr.h"
49 #include "authhash.h"
50 #include "table.h"
51 #include "client-common.h"
52
53 /* TODO: more commands */
54
55 /** @brief How often to send data to the server when receiving logs */
56 #define LOG_PROD_INTERVAL 10
57
58 /* Types *********************************************************************/
59
60 /** @brief Client state */
61 enum client_state {
62   state_disconnected,          /**< @brief not connected */
63   state_connecting,            /**< @brief waiting for connect() */
64   state_connected,             /**< @brief connected but not authenticated */
65   state_idle,                  /**< @brief not doing anything */
66   state_cmdresponse,           /**< @brief waiting for command resonse */
67   state_body,                  /**< @brief accumulating body */
68   state_log,                   /**< @brief monitoring log */
69 };
70
71 /** @brief Names for @ref client_state */
72 static const char *const states[] = {
73   "disconnected",
74   "connecting",
75   "connected",
76   "idle",
77   "cmdresponse",
78   "body",
79   "log"
80 };
81
82 struct operation;                       /* forward decl */
83
84 /** @brief Type of an operation callback */
85 typedef void operation_callback(disorder_eclient *c, struct operation *op);
86
87 /** @brief A pending operation.
88  *
89  * This can be either a command or part of the authentication protocol.  In the
90  * former case new commands are appended to the list, in the latter case they
91  * are inserted at the front. */
92 struct operation {
93   struct operation *next;          /**< @brief next operation */
94   char *cmd;                       /**< @brief command to send or 0 */
95   char **body;                     /**< @brief command body */
96   operation_callback *opcallback;  /**< @brief internal completion callback */
97   void (*completed)();             /**< @brief user completion callback or 0 */
98   void *v;                         /**< @brief data for COMPLETED */
99   disorder_eclient *client;        /**< @brief owning client */
100
101   /** @brief true if sent to server
102    *
103    * This is cleared by disorder_eclient_close(), forcing all queued
104    * commands to be transparently resent.
105    */
106   int sent;
107 };
108
109 /** @brief Client structure */
110 struct disorder_eclient {
111   char *ident;
112   int fd;                               /**< @brief connection to server */
113   enum client_state state;              /**< @brief current state */
114   int authenticated;                    /**< @brief true when authenicated */
115   struct dynstr output;                 /**< @brief output buffer */
116   struct dynstr input;                  /**< @brief input buffer */
117   int eof;                              /**< @brief input buffer is at EOF */
118   const disorder_eclient_callbacks *callbacks; /**< @brief error callbacks */
119   void *u;                              /**< @brief user data */
120   struct operation *ops;                /**< @brief queue of operations */
121   struct operation **opstail;           /**< @brief queue tail */
122   /* accumulated response */
123   int rc;                               /**< @brief response code */
124   char *line;                           /**< @brief complete line */
125   struct vector vec;                    /**< @brief body */
126
127   const disorder_eclient_log_callbacks *log_callbacks;
128   /**< @brief log callbacks
129    *
130    * Once disorder_eclient_log() has been issued this is always set.  When we
131    * re-connect it is checked to re-issue the log command.
132    */
133   void *log_v;                          /**< @brief user data */
134   unsigned long statebits;              /**< @brief latest state */
135
136   time_t last_prod;
137   /**< @brief last time we sent a prod
138    *
139    * When we are receiving log data we send a "prod" byte to the server from
140    * time to time so that we detect broken connections reasonably quickly.  The
141    * server just ignores these bytes.
142    */
143
144   /** @brief Protocol version */
145   int protocol;
146
147   /** @brief True if enabled */
148   int enabled;
149 };
150
151 /* Forward declarations ******************************************************/
152
153 static int start_connect(disorder_eclient *c);
154 static void process_line(disorder_eclient *c, char *line);
155 static void maybe_connected(disorder_eclient *c);
156 static void authbanner_opcallback(disorder_eclient *c,
157                                   struct operation *op);
158 static void authuser_opcallback(disorder_eclient *c,
159                                 struct operation *op);
160 static void complete(disorder_eclient *c);
161 static void send_output(disorder_eclient *c);
162 static void put(disorder_eclient *c, const char *s, size_t n);
163 static void read_input(disorder_eclient *c);
164 static void stash_command(disorder_eclient *c,
165                           int queuejump,
166                           operation_callback *opcallback,
167                           void (*completed)(),
168                           void *v,
169                           int nbody,
170                           char **body,
171                           const char *cmd,
172                           ...);
173 static void log_opcallback(disorder_eclient *c, struct operation *op);
174 static void logline(disorder_eclient *c, const char *line);
175 static void logentry_completed(disorder_eclient *c, int nvec, char **vec);
176 static void logentry_failed(disorder_eclient *c, int nvec, char **vec);
177 static void logentry_moved(disorder_eclient *c, int nvec, char **vec);
178 static void logentry_playing(disorder_eclient *c, int nvec, char **vec);
179 static void logentry_queue(disorder_eclient *c, int nvec, char **vec);
180 static void logentry_recent_added(disorder_eclient *c, int nvec, char **vec);
181 static void logentry_recent_removed(disorder_eclient *c, int nvec, char **vec);
182 static void logentry_removed(disorder_eclient *c, int nvec, char **vec);
183 static void logentry_scratched(disorder_eclient *c, int nvec, char **vec);
184 static void logentry_state(disorder_eclient *c, int nvec, char **vec);
185 static void logentry_volume(disorder_eclient *c, int nvec, char **vec);
186 static void logentry_rescanned(disorder_eclient *c, int nvec, char **vec);
187 static void logentry_user_add(disorder_eclient *c, int nvec, char **vec);
188 static void logentry_user_confirm(disorder_eclient *c, int nvec, char **vec);
189 static void logentry_user_delete(disorder_eclient *c, int nvec, char **vec);
190 static void logentry_user_edit(disorder_eclient *c, int nvec, char **vec);
191 static void logentry_rights_changed(disorder_eclient *c, int nvec, char **vec);
192 static void logentry_adopted(disorder_eclient *c, int nvec, char **vec);
193 static void logentry_playlist_created(disorder_eclient *c, int nvec, char **vec);
194 static void logentry_playlist_deleted(disorder_eclient *c, int nvec, char **vec);
195 static void logentry_playlist_modified(disorder_eclient *c, int nvec, char **vec);
196
197 /* Tables ********************************************************************/
198
199 /** @brief One possible log entry */
200 struct logentry_handler {
201   const char *name;                     /**< @brief Entry name */
202   int min;                              /**< @brief Minimum arguments */
203   int max;                              /**< @brief Maximum arguments */
204   void (*handler)(disorder_eclient *c,
205                   int nvec,
206                   char **vec);          /**< @brief Handler function */
207 };
208
209 /** @brief Table for parsing log entries */
210 static const struct logentry_handler logentry_handlers[] = {
211 #define LE(X, MIN, MAX) { #X, MIN, MAX, logentry_##X }
212   LE(adopted, 2, 2),
213   LE(completed, 1, 1),
214   LE(failed, 2, 2),
215   LE(moved, 1, 1),
216   LE(playing, 1, 2),
217   LE(playlist_created, 2, 2),
218   LE(playlist_deleted, 1, 1),
219   LE(playlist_modified, 2, 2),
220   LE(queue, 2, INT_MAX),
221   LE(recent_added, 2, INT_MAX),
222   LE(recent_removed, 1, 1),
223   LE(removed, 1, 2),
224   LE(rescanned, 0, 0),
225   LE(rights_changed, 1, 1),
226   LE(scratched, 2, 2),
227   LE(state, 1, 1),
228   LE(user_add, 1, 1),
229   LE(user_confirm, 1, 1),
230   LE(user_delete, 1, 1),
231   LE(user_edit, 2, 2),
232   LE(volume, 2, 2)
233 };
234
235 /* Setup and teardown ********************************************************/
236
237 /** @brief Create a new client
238  *
239  * Does NOT connect the client - connections are made (and re-made) on demand.
240  */
241 disorder_eclient *disorder_eclient_new(const disorder_eclient_callbacks *cb,
242                                        void *u) {
243   disorder_eclient *c = xmalloc(sizeof *c);
244   D(("disorder_eclient_new"));
245   c->fd = -1;
246   c->callbacks = cb;
247   c->u = u;
248   c->opstail = &c->ops;
249   c->enabled = 1;
250   vector_init(&c->vec);
251   dynstr_init(&c->input);
252   dynstr_init(&c->output);
253   return c;
254 }
255
256 /** @brief Disconnect a client
257  * @param c Client to disconnect
258  *
259  * NB that this routine just disconnnects the TCP connection.  It does not
260  * destroy the client!  If you continue to use it then it will attempt to
261  * reconnect.
262  */
263 void disorder_eclient_close(disorder_eclient *c) {
264   struct operation *op;
265
266   D(("disorder_eclient_close"));
267   if(c->fd != -1) {
268     D(("disorder_eclient_close closing fd %d", c->fd));
269     c->callbacks->poll(c->u, c, c->fd, 0);
270     xclose(c->fd);
271     c->fd = -1;
272     c->state = state_disconnected;
273     c->statebits = 0;
274   }
275   c->output.nvec = 0;
276   c->input.nvec = 0;
277   c->eof = 0;
278   c->authenticated = 0;
279   /* We'll need to resend all operations */
280   for(op = c->ops; op; op = op->next)
281     op->sent = 0;
282   /* Drop our use a hint that we're disconnected */
283   if(c->log_callbacks && c->log_callbacks->state)
284     c->log_callbacks->state(c->log_v, c->statebits);
285 }
286
287 /** @brief Permit new connection activity */
288 void disorder_eclient_enable_connect(disorder_eclient *c) {
289   c->enabled = 1;
290 }
291
292 /** @brief Suppress new connection activity */
293 void disorder_eclient_disable_connect(disorder_eclient *c) {
294   c->enabled = 0;
295 }
296
297 /** @brief Return current state */
298 unsigned long disorder_eclient_state(const disorder_eclient *c) {
299   return c->statebits | (c->state > state_connected ? DISORDER_CONNECTED : 0);
300 }
301
302 /* Error reporting ***********************************************************/
303
304 /** @brief called when a connection error occurs
305  *
306  * After this called we will be disconnected (by disorder_eclient_close()),
307  * so there will be a reconnection before any commands can be sent.
308  */
309 static int comms_error(disorder_eclient *c, const char *fmt, ...) {
310   va_list ap;
311   char *s;
312
313   D(("comms_error"));
314   va_start(ap, fmt);
315   byte_xvasprintf(&s, fmt, ap);
316   va_end(ap);
317   disorder_eclient_close(c);
318   c->callbacks->comms_error(c->u, s);
319   return -1;
320 }
321
322 /** @brief called when the server reports an error */
323 static int protocol_error(disorder_eclient *c, struct operation *op,
324                           int code, const char *fmt, ...) {
325   va_list ap;
326   char *s;
327
328   D(("protocol_error"));
329   va_start(ap, fmt);
330   byte_xvasprintf(&s, fmt, ap);
331   va_end(ap);
332   c->callbacks->protocol_error(c->u, op->v, code, s);
333   return -1;
334 }
335
336 /* State machine *************************************************************/
337
338 /** @brief Send an operation (into the output buffer)
339  * @param op Operation to send
340  */
341 static void op_send(struct operation *op) {
342   disorder_eclient *const c = op->client;
343   put(c, op->cmd, strlen(op->cmd));
344   if(op->body) {
345     for(int n = 0; op->body[n]; ++n) {
346       if(op->body[n][0] == '.')
347         put(c, ".", 1);
348       put(c, op->body[n], strlen(op->body[n]));
349       put(c, "\n", 1);
350     }
351     put(c, ".\n", 2);
352   }
353   op->sent = 1;
354 }
355
356 /** @brief Called when there's something to do
357  * @param c Client
358  * @param mode bitmap of @ref DISORDER_POLL_READ and/or @ref DISORDER_POLL_WRITE.
359  *
360  * This should be called from by your code when the file descriptor is readable
361  * or writable (as requested by the @c poll callback, see @ref
362  * disorder_eclient_callbacks) and in any case from time to time (with @p mode
363  * = 0) to allow for retries to work.
364  */
365 void disorder_eclient_polled(disorder_eclient *c, unsigned mode) {
366   struct operation *op;
367   time_t now;
368   
369   D(("disorder_eclient_polled fd=%d state=%s mode=[%s %s]",
370      c->fd, states[c->state],
371      mode & DISORDER_POLL_READ ? "READ" : "",
372      mode & DISORDER_POLL_WRITE ? "WRITE" : ""));
373   /* The pattern here is to check each possible state in turn and try to
374    * advance (though on error we might go back).  If we advance we leave open
375    * the possibility of falling through to the next state, but we set the mode
376    * bits to 0, to avoid false positives (which matter more in some cases than
377    * others). */
378
379   if(c->state == state_disconnected) {
380     D(("state_disconnected"));
381     /* If there is no password yet then we cannot connect */
382     if(!config->password) {
383       comms_error(c, "no password is configured");
384       c->enabled = 0;
385       return;
386     }
387     /* Only try to connect if enabled */
388     if(c->enabled)
389       start_connect(c);
390     /* might now be state_disconnected (on error), state_connecting (slow
391      * connect) or state_connected (fast connect).  If state_disconnected then
392      * we just rely on a periodic callback from the event loop sometime. */
393     mode = 0;
394   }
395
396   if(c->state == state_connecting && mode) {
397     D(("state_connecting"));
398     maybe_connected(c);
399     /* Might be state_disconnected (on error) or state_connected (on success).
400      * In the former case we rely on the event loop for a periodic callback to
401      * retry. */
402     mode = 0;
403   }
404
405   if(c->state == state_connected) {
406     D(("state_connected"));
407     /* We just connected.  Initiate the authentication protocol. */
408     stash_command(c, 1/*queuejump*/, authbanner_opcallback,
409                   0/*completed*/, 0/*v*/, -1/*nbody*/, 0/*body*/, 0/*cmd*/);
410     /* We never stay is state_connected very long.  We could in principle jump
411      * straight to state_cmdresponse since there's actually no command to
412      * send, but that would arguably be cheating. */
413     c->state = state_idle;
414   }
415
416   if(c->state == state_idle) {
417     D(("state_idle"));
418     /* We are connected, and have finished any command we set off, look for
419      * some work to do */
420     if(c->ops) {
421       D(("have ops"));
422       if(c->authenticated) {
423         /* Transmit all unsent operations */
424         for(op = c->ops; op; op = op->next) {
425           if(!op->sent)
426             op_send(op);
427         }
428       } else {
429         /* Just send the head operation */
430         if(c->ops->cmd && !c->ops->sent)
431           op_send(c->ops);
432       }
433       /* Awaiting response for the operation at the head of the list */
434       c->state = state_cmdresponse;
435     } else
436       /* genuinely idle */
437       c->callbacks->report(c->u, 0);
438   }
439
440   /* Queue up a byte to send */
441   if(c->state == state_log
442      && c->output.nvec == 0
443      && time(&now) - c->last_prod > LOG_PROD_INTERVAL) {
444     put(c, "x", 1);
445     c->last_prod = now;
446   }
447   
448   if(c->state == state_cmdresponse
449      || c->state == state_body
450      || c->state == state_log) {
451     D(("state_%s", states[c->state]));
452     /* We are awaiting a response */
453     if(mode & DISORDER_POLL_WRITE) send_output(c);
454     if(mode & DISORDER_POLL_READ) read_input(c);
455     /* There are a couple of reasons we might want to re-enter the state
456      * machine from the top.  state_idle is obvious: there may be further
457      * commands to process.  Re-entering on state_disconnected means that we
458      * immediately retry connection if a comms error occurs during a command.
459      * This is different to the case where a connection fails, where we await a
460      * spontaneous call to initiate the retry. */
461     switch(c->state) {
462     case state_disconnected:            /* lost connection */
463     case state_idle:                    /* completed a command */
464       D(("retrying"));
465       disorder_eclient_polled(c, 0);
466       return;
467     default:
468       break;
469     }
470   }
471   
472   /* Figure out what to set the mode to */
473   switch(c->state) {
474   case state_disconnected:
475     D(("state_disconnected (2)"));
476     /* Probably an error occurred.  Await a retry. */
477     mode = 0;
478     break;
479   case state_connecting:
480     D(("state_connecting (2)"));
481     /* Waiting for connect to complete */
482     mode = DISORDER_POLL_READ|DISORDER_POLL_WRITE;
483     break;
484   case state_connected:
485     D(("state_connected (2)"));
486     assert(!"should never be in state_connected here");
487     break;
488   case state_idle:
489     D(("state_idle (2)"));
490     /* Connected but nothing to do. */
491     mode = 0;
492     break;
493   case state_cmdresponse:
494   case state_body:
495   case state_log:
496     D(("state_%s (2)", states[c->state]));
497     /* Gathering a response.  Wait for input. */
498     mode = DISORDER_POLL_READ;
499     /* Flush any pending output. */
500     if(c->output.nvec) mode |= DISORDER_POLL_WRITE;
501     break;
502   }
503   D(("fd=%d new mode [%s %s]",
504      c->fd,
505      mode & DISORDER_POLL_READ ? "READ" : "",
506      mode & DISORDER_POLL_WRITE ? "WRITE" : ""));
507   if(c->fd != -1) c->callbacks->poll(c->u, c, c->fd, mode);
508 }
509
510 /** @brief Called to start connecting */
511 static int start_connect(disorder_eclient *c) {
512   struct sockaddr *sa;
513   socklen_t len;
514
515   D(("start_connect"));
516   if((len = find_server(config, &sa, &c->ident)) == (socklen_t)-1)
517     return comms_error(c, "cannot look up server"); /* TODO better error */
518   if(c->fd != -1) {
519     xclose(c->fd);
520     c->fd = -1;
521   }
522   if((c->fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
523     return comms_error(c, "socket: %s", strerror(errno));
524   c->eof = 0;
525   nonblock(c->fd);
526   cloexec(c->fd);
527   if(connect(c->fd, sa, len) < 0) {
528     switch(errno) {
529     case EINTR:
530     case EINPROGRESS:
531       c->state = state_connecting;
532       /* We are called from _polled so the state machine will get to do its
533        * thing */
534       return 0;
535     default:
536       /* Signal the error to the caller. */
537       return comms_error(c, "connecting to %s: %s", c->ident, strerror(errno));
538     }
539   } else
540     c->state = state_connected;
541   return 0;
542 }
543
544 /** @brief Called when poll triggers while waiting for a connection */
545 static void maybe_connected(disorder_eclient *c) {
546   /* We either connected, or got an error. */
547   int err;
548   socklen_t len = sizeof err;
549   
550   D(("maybe_connected"));
551   /* Work around over-enthusiastic error slippage */
552   if(getsockopt(c->fd, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
553     err = errno;
554   if(err) {
555     /* The connection failed */
556     comms_error(c, "connecting to %s: %s", c->ident, strerror(err));
557     /* sets state_disconnected */
558   } else {
559     char *r;
560     
561     /* The connection succeeded */
562     c->state = state_connected;
563     byte_xasprintf(&r, "connected to %s", c->ident);
564     c->callbacks->report(c->u, r);
565     /* If this is a log client we expect to get a bunch of updates from the
566      * server straight away */
567   }
568 }
569
570 /* Authentication ************************************************************/
571
572 /** @brief Called with the greeting from the server */
573 static void authbanner_opcallback(disorder_eclient *c,
574                                   struct operation *op) {
575   size_t nonce_len;
576   const unsigned char *nonce;
577   const char *res;
578   char **rvec;
579   int nrvec;
580   const char *protocol, *algorithm, *challenge;
581   
582   D(("authbanner_opcallback"));
583   if(c->rc / 100 != 2
584      || !(rvec = split(c->line + 4, &nrvec, SPLIT_QUOTES, 0, 0))
585      || nrvec < 1) {
586     /* Banner told us to go away, or was malformed.  We cannot proceed. */
587     protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
588     disorder_eclient_close(c);
589     return;
590   }
591   switch(nrvec) {
592   case 1:
593     protocol = "1";
594     algorithm = "sha1";
595     challenge = *rvec++;
596     break;
597   case 2:
598     protocol = "1";
599     algorithm = *rvec++;
600     challenge = *rvec++;
601     break;
602   case 3:
603     protocol = *rvec++;
604     algorithm = *rvec++;
605     challenge = *rvec++;
606     break;
607   default:
608     protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
609     disorder_eclient_close(c);
610     return;
611   }
612   c->protocol = atoi(protocol);
613   if(c->protocol < 1 || c->protocol > 2) {
614     protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
615     disorder_eclient_close(c);
616     return;
617   }
618   nonce = unhex(challenge, &nonce_len);
619   res = authhash(nonce, nonce_len, config->password, algorithm);
620   if(!res) {
621     protocol_error(c, op, c->rc, "%s: unknown authentication algorithm '%s'",
622                    c->ident, algorithm);
623     disorder_eclient_close(c);
624     return;
625   }
626   stash_command(c, 1/*queuejump*/, authuser_opcallback, 0/*completed*/, 0/*v*/,
627                 -1/*nbody*/, 0/*body*/,
628                 "user", quoteutf8(config->username), quoteutf8(res),
629                 (char *)0);
630 }
631
632 /** @brief Called with the response to the @c user command */
633 static void authuser_opcallback(disorder_eclient *c,
634                                 struct operation *op) {
635   char *r;
636
637   D(("authuser_opcallback"));
638   if(c->rc / 100 != 2) {
639     /* Wrong password or something.  We cannot proceed. */
640     protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
641     c->enabled = 0;
642     disorder_eclient_close(c);
643     return;
644   }
645   /* OK, we're authenticated now. */
646   c->authenticated = 1;
647   byte_xasprintf(&r, "authenticated with %s", c->ident);
648   c->callbacks->report(c->u, r);
649   if(c->log_callbacks && !(c->ops && c->ops->opcallback == log_opcallback))
650     /* We are a log client, switch to logging mode */
651     stash_command(c, 0/*queuejump*/, log_opcallback, 0/*completed*/, c->log_v,
652                   -1/*nbody*/, 0/*body*/,
653                   "log", (char *)0);
654 }
655
656 /* Output ********************************************************************/
657
658 /* Chop N bytes off the front of a dynstr */
659 static void consume(struct dynstr *d, int n) {
660   D(("consume %d", n));
661   assert(d->nvec >= n);
662   memmove(d->vec, d->vec + n, d->nvec - n);
663   d->nvec -= n;
664 }
665
666 /* Write some bytes */
667 static void put(disorder_eclient *c, const char *s, size_t n) {
668   D(("put %d %.*s", c->fd, (int)n, s));
669   dynstr_append_bytes(&c->output, s, n);
670 }
671
672 /* Called when we can write to our FD, or at any other time */
673 static void send_output(disorder_eclient *c) {
674   int n;
675
676   D(("send_output %d bytes pending", c->output.nvec));
677   if(c->state > state_connecting && c->output.nvec) {
678     n = write(c->fd, c->output.vec, c->output.nvec);
679     if(n < 0) {
680       switch(errno) {
681       case EINTR:
682       case EAGAIN:
683         break;
684       default:
685         comms_error(c, "writing to %s: %s", c->ident, strerror(errno));
686         break;
687       }
688     } else
689       consume(&c->output, n);
690   }
691 }
692
693 /* Input *********************************************************************/
694
695 /* Called when c->fd might be readable, or at any other time */
696 static void read_input(disorder_eclient *c) {
697   char *nl;
698   int n;
699   char buffer[512];
700
701   D(("read_input in state %s", states[c->state]));
702   if(c->state <= state_connected) return; /* ignore bogus calls */
703   /* read some more input */
704   n = read(c->fd, buffer, sizeof buffer);
705   if(n < 0) {
706     switch(errno) {
707     case EINTR:
708     case EAGAIN:
709       break;
710     default:
711       comms_error(c, "reading from %s: %s", c->ident, strerror(errno));
712       break;
713     }
714     return;                             /* no new input to process */
715   } else if(n) {
716     D(("read %d bytes: [%.*s]", n, n, buffer));
717     dynstr_append_bytes(&c->input, buffer, n);
718   } else
719     c->eof = 1;
720   /* might have more than one line to process */
721   while(c->state > state_connecting
722         && (nl = memchr(c->input.vec, '\n', c->input.nvec))) {
723     process_line(c, xstrndup(c->input.vec, nl - c->input.vec));
724     /* we might have disconnected along the way, which zogs the input buffer */
725     if(c->state > state_connecting)
726       consume(&c->input, (nl - c->input.vec) + 1);
727   }
728   if(c->eof) {
729     comms_error(c, "reading from %s: server disconnected", c->ident);
730     c->authenticated = 0;
731   }
732 }
733
734 /* called with a line that has just been read */
735 static void process_line(disorder_eclient *c, char *line) {
736   D(("process_line %d [%s]", c->fd, line));
737   switch(c->state) {
738   case state_cmdresponse:
739     /* This is the first line of a response */
740     if(!(line[0] >= '0' && line[0] <= '9'
741          && line[1] >= '0' && line[1] <= '9'
742          && line[2] >= '0' && line[2] <= '9'
743          && line[3] == ' '))
744       fatal(0, "invalid response from server: %s", line);
745     c->rc = (line[0] * 10 + line[1]) * 10 + line[2] - 111 * '0';
746     c->line = line;
747     switch(c->rc % 10) {
748     case 3:
749       /* We need to collect the body. */
750       c->state = state_body;
751       vector_init(&c->vec);
752       break;
753     case 4:
754       assert(c->log_callbacks != 0);
755       if(c->log_callbacks->connected)
756         c->log_callbacks->connected(c->log_v);
757       c->state = state_log;
758       break;
759     default:
760       /* We've got the whole response.  Go into the idle state so the state
761        * machine knows we're done and then call the operation callback. */
762       complete(c);
763       break;
764     }
765     break;
766   case state_body:
767     if(strcmp(line, ".")) {
768       /* A line from the body */
769       vector_append(&c->vec, line + (line[0] == '.'));
770     } else {
771       /* End of the body. */
772       vector_terminate(&c->vec);
773       complete(c);
774     }
775     break;
776   case state_log:
777     if(strcmp(line, ".")) {
778       logline(c, line + (line[0] == '.'));
779     } else 
780       complete(c);
781     break;
782   default:
783     assert(!"wrong state for location");
784     break;
785   }
786 }
787
788 /* Called when an operation completes */
789 static void complete(disorder_eclient *c) {
790   struct operation *op;
791
792   D(("complete"));
793   /* Pop the operation off the queue */
794   op = c->ops;
795   c->ops = op->next;
796   if(c->opstail == &op->next)
797     c->opstail = &c->ops;
798   /* If we've pipelined a command ahead then we go straight to cmdresponser.
799    * Otherwise we go to idle, which will arrange further sends. */
800   c->state = c->ops && c->ops->sent ? state_cmdresponse : state_idle;
801   op->opcallback(c, op);
802   /* Note that we always call the opcallback even on error, though command
803    * opcallbacks generally always do the same error handling, i.e. just call
804    * protocol_error().  It's the auth* opcallbacks that have different
805    * behaviour. */
806 }
807
808 /* Operation setup ***********************************************************/
809
810 static void stash_command_vector(disorder_eclient *c,
811                                  int queuejump,
812                                  operation_callback *opcallback,
813                                  void (*completed)(),
814                                  void *v,
815                                  int nbody,
816                                  char **body,
817                                  int ncmd,
818                                  char **cmd) {
819   struct operation *op = xmalloc(sizeof *op);
820   struct dynstr d;
821   int n;
822
823   if(cmd) {
824     dynstr_init(&d);
825     for(n = 0; n < ncmd; ++n) {
826       if(n)
827         dynstr_append(&d, ' ');
828       dynstr_append_string(&d, quoteutf8(cmd[n]));
829     }
830     dynstr_append(&d, '\n');
831     dynstr_terminate(&d);
832     op->cmd = d.vec;
833   } else
834     op->cmd = 0;                        /* usually, awaiting challenge */
835   if(nbody >= 0) {
836     op->body = xcalloc(nbody + 1, sizeof (char *));
837     for(n = 0; n < nbody; ++n)
838       op->body[n] = xstrdup(body[n]);
839     op->body[n] = 0;
840   } else
841     op->body = NULL;
842   op->opcallback = opcallback;
843   op->completed = completed;
844   op->v = v;
845   op->next = 0;
846   op->client = c;
847   assert(op->sent == 0);
848   if(queuejump) {
849     /* Authentication operations jump the queue of useful commands */
850     op->next = c->ops;
851     c->ops = op;
852     if(c->opstail == &c->ops)
853       c->opstail = &op->next;
854     for(op = c->ops; op; op = op->next)
855       assert(!op->sent);
856   } else {
857     *c->opstail = op;
858     c->opstail = &op->next;
859   }
860 }
861
862 static void vstash_command(disorder_eclient *c,
863                            int queuejump,
864                            operation_callback *opcallback,
865                            void (*completed)(),
866                            void *v,
867                            int nbody,
868                            char **body,
869                            const char *cmd, va_list ap) {
870   char *arg;
871   struct vector vec;
872
873   D(("vstash_command %s", cmd ? cmd : "NULL"));
874   if(cmd) {
875     vector_init(&vec);
876     vector_append(&vec, (char *)cmd);
877     while((arg = va_arg(ap, char *)))
878       vector_append(&vec, arg);
879     stash_command_vector(c, queuejump, opcallback, completed, v, 
880                          nbody, body, vec.nvec, vec.vec);
881   } else
882     stash_command_vector(c, queuejump, opcallback, completed, v,
883                          nbody, body,
884                          0, 0);
885 }
886
887 static void stash_command(disorder_eclient *c,
888                           int queuejump,
889                           operation_callback *opcallback,
890                           void (*completed)(),
891                           void *v,
892                           int nbody,
893                           char **body,
894                           const char *cmd,
895                           ...) {
896   va_list ap;
897
898   va_start(ap, cmd);
899   vstash_command(c, queuejump, opcallback, completed, v, nbody, body, cmd, ap);
900   va_end(ap);
901 }
902
903 /* Command support ***********************************************************/
904
905 static const char *errorstring(disorder_eclient *c) {
906   char *s;
907
908   byte_xasprintf(&s, "%s: %s: %d", c->ident, c->line, c->rc);
909   return s;
910 }
911
912 /* for commands with a quoted string response */ 
913 static void string_response_opcallback(disorder_eclient *c,
914                                        struct operation *op) {
915   disorder_eclient_string_response *completed
916     = (disorder_eclient_string_response *)op->completed;
917     
918   D(("string_response_callback"));
919   if(c->rc / 100 == 2 || c->rc == 555) {
920     if(op->completed) {
921       if(c->rc == 555)
922         completed(op->v, NULL, NULL);
923       else if(c->protocol >= 2) {
924         char **rr = split(c->line + 4, 0, SPLIT_QUOTES, 0, 0);
925         
926         if(rr && *rr)
927           completed(op->v, NULL, *rr);
928         else
929           /* TODO error message a is bit lame but generally indicates a server
930            * bug rather than anything the user can address */
931           completed(op->v, "error parsing response", NULL);
932       } else
933         completed(op->v, NULL, c->line + 4);
934     }
935   } else
936     completed(op->v, errorstring(c), NULL);
937 }
938
939 /* for commands with a simple integer response */ 
940 static void integer_response_opcallback(disorder_eclient *c,
941                                         struct operation *op) {
942   disorder_eclient_integer_response *completed
943     = (disorder_eclient_integer_response *)op->completed;
944
945   D(("string_response_callback"));
946   if(c->rc / 100 == 2) {
947     long n;
948     int e;
949
950     e = xstrtol(&n, c->line + 4, 0, 10);
951     if(e)
952       completed(op->v, strerror(e), 0);
953     else
954       completed(op->v, 0, n);
955   } else
956     completed(op->v, errorstring(c), 0);
957 }
958
959 /* for commands with no response */
960 static void no_response_opcallback(disorder_eclient *c,
961                                    struct operation *op) {
962   disorder_eclient_no_response *completed
963     = (disorder_eclient_no_response *)op->completed;
964
965   D(("no_response_callback"));
966   if(c->rc / 100 == 2)
967     completed(op->v, NULL);
968   else
969     completed(op->v, errorstring(c));
970 }
971
972 /* error callback for queue_unmarshall */
973 static void eclient_queue_error(const char *msg,
974                                 void *u) {
975   struct operation *op = u;
976
977   /* TODO don't use protocol_error here */
978   protocol_error(op->client, op, -1, "error parsing queue entry: %s", msg);
979 }
980
981 /* for commands that expect a queue dump */
982 static void queue_response_opcallback(disorder_eclient *c,
983                                       struct operation *op) {
984   disorder_eclient_queue_response *const completed
985     = (disorder_eclient_queue_response *)op->completed;
986   int n;
987   int parse_failed = 0;
988   struct queue_entry *q, *qh = 0, **qtail = &qh, *qlast = 0;
989   
990   D(("queue_response_callback"));
991   if(c->rc / 100 == 2) {
992     /* parse the queue */
993     for(n = 0; n < c->vec.nvec; ++n) {
994       q = xmalloc(sizeof *q);
995       D(("queue_unmarshall %s", c->vec.vec[n]));
996       if(!queue_unmarshall(q, c->vec.vec[n], NULL, op)) {
997         q->prev = qlast;
998         *qtail = q;
999         qtail = &q->next;
1000         qlast = q;
1001       } else
1002         parse_failed = 1;
1003     }
1004     /* Currently we pass the partial queue to the callback along with the
1005      * error.  This might not be very useful in practice... */
1006     if(parse_failed)
1007       completed(op->v, "cannot parse result", qh);
1008     else
1009       completed(op->v, 0, qh);
1010   } else
1011     completed(op->v, errorstring(c), 0);
1012
1013
1014 /* for 'playing' */
1015 static void playing_response_opcallback(disorder_eclient *c,
1016                                         struct operation *op) {
1017   disorder_eclient_queue_response *const completed
1018     = (disorder_eclient_queue_response *)op->completed;
1019   struct queue_entry *q;
1020
1021   D(("playing_response_callback"));
1022   if(c->rc / 100 == 2) {
1023     switch(c->rc % 10) {
1024     case 2:
1025       if(queue_unmarshall(q = xmalloc(sizeof *q), c->line + 4,
1026                           NULL, c))
1027         completed(op->v, "cannot parse result", 0);
1028       else
1029         completed(op->v, 0, q);
1030       break;
1031     case 9:
1032       completed(op->v, 0, 0);
1033       break;
1034     default:
1035       completed(op->v, errorstring(c), 0);
1036       break;
1037     }
1038   } else
1039     completed(op->v, errorstring(c), 0);
1040 }
1041
1042 /* for commands that expect a list of some sort */
1043 static void list_response_opcallback(disorder_eclient *c,
1044                                      struct operation *op) {
1045   disorder_eclient_list_response *const completed =
1046     (disorder_eclient_list_response *)op->completed;
1047
1048   D(("list_response_callback"));
1049   if(c->rc / 100 == 2)
1050     completed(op->v, NULL, c->vec.nvec, c->vec.vec);
1051   else if(c->rc == 555)
1052     completed(op->v, NULL, -1, NULL);
1053   else
1054     completed(op->v, errorstring(c), 0, 0);
1055 }
1056
1057 /* for volume */
1058 static void volume_response_opcallback(disorder_eclient *c,
1059                                        struct operation *op) {
1060   disorder_eclient_volume_response *completed
1061     = (disorder_eclient_volume_response *)op->completed;
1062   int l, r;
1063
1064   D(("volume_response_callback"));
1065   if(c->rc / 100 == 2) {
1066     if(op->completed) {
1067       if(sscanf(c->line + 4, "%d %d", &l, &r) != 2 || l < 0 || r < 0)
1068         completed(op->v, "cannot parse volume response", 0, 0);
1069       else
1070         completed(op->v, 0, l, r);
1071     }
1072   } else
1073     completed(op->v, errorstring(c), 0, 0);
1074 }
1075
1076 static int simple(disorder_eclient *c,
1077                   operation_callback *opcallback,
1078                   void (*completed)(),
1079                   void *v,
1080                   const char *cmd, ...) {
1081   va_list ap;
1082
1083   va_start(ap, cmd);
1084   vstash_command(c, 0/*queuejump*/, opcallback, completed, v, -1, 0, cmd, ap);
1085   va_end(ap);
1086   /* Give the state machine a kick, since we might be in state_idle */
1087   disorder_eclient_polled(c, 0);
1088   return 0;
1089 }
1090
1091 static int simple_body(disorder_eclient *c,
1092                        operation_callback *opcallback,
1093                        void (*completed)(),
1094                        void *v,
1095                        int nbody,
1096                        char **body,
1097                        const char *cmd, ...) {
1098   va_list ap;
1099
1100   va_start(ap, cmd);
1101   vstash_command(c, 0/*queuejump*/, opcallback, completed, v, nbody, body, cmd, ap);
1102   va_end(ap);
1103   /* Give the state machine a kick, since we might be in state_idle */
1104   disorder_eclient_polled(c, 0);
1105   return 0;
1106 }
1107
1108 /* Commands ******************************************************************/
1109  
1110 int disorder_eclient_version(disorder_eclient *c,
1111                              disorder_eclient_string_response *completed,
1112                              void *v) {
1113   return simple(c, string_response_opcallback, (void (*)())completed, v,
1114                 "version", (char *)0);
1115 }
1116
1117 int disorder_eclient_namepart(disorder_eclient *c,
1118                               disorder_eclient_string_response *completed,
1119                               const char *track,
1120                               const char *context,
1121                               const char *part,
1122                               void *v) {
1123   return simple(c, string_response_opcallback, (void (*)())completed, v,
1124                 "part", track, context, part, (char *)0);
1125 }
1126
1127 int disorder_eclient_play(disorder_eclient *c,
1128                           const char *track,
1129                           disorder_eclient_no_response *completed,
1130                           void *v) {
1131   return simple(c, no_response_opcallback, (void (*)())completed, v,
1132                 "play", track, (char *)0);
1133 }
1134
1135 int disorder_eclient_pause(disorder_eclient *c,
1136                            disorder_eclient_no_response *completed,
1137                            void *v) {
1138   return simple(c, no_response_opcallback, (void (*)())completed, v,
1139                 "pause", (char *)0);
1140 }
1141
1142 int disorder_eclient_resume(disorder_eclient *c,
1143                             disorder_eclient_no_response *completed,
1144                             void *v) {
1145   return simple(c, no_response_opcallback, (void (*)())completed, v,
1146                 "resume", (char *)0);
1147 }
1148
1149 int disorder_eclient_scratch(disorder_eclient *c,
1150                              const char *id,
1151                              disorder_eclient_no_response *completed,
1152                              void *v) {
1153   return simple(c, no_response_opcallback, (void (*)())completed, v,
1154                 "scratch", id, (char *)0);
1155 }
1156
1157 int disorder_eclient_scratch_playing(disorder_eclient *c,
1158                                      disorder_eclient_no_response *completed,
1159                                      void *v) {
1160   return disorder_eclient_scratch(c, 0, completed, v);
1161 }
1162
1163 int disorder_eclient_remove(disorder_eclient *c,
1164                             const char *id,
1165                             disorder_eclient_no_response *completed,
1166                             void *v) {
1167   return simple(c, no_response_opcallback, (void (*)())completed, v,
1168                 "remove", id, (char *)0);
1169 }
1170
1171 int disorder_eclient_moveafter(disorder_eclient *c,
1172                                const char *target,
1173                                int nids,
1174                                const char **ids,
1175                                disorder_eclient_no_response *completed,
1176                                void *v) {
1177   struct vector vec;
1178   int n;
1179
1180   vector_init(&vec);
1181   vector_append(&vec, (char *)"moveafter");
1182   vector_append(&vec, (char *)target);
1183   for(n = 0; n < nids; ++n)
1184     vector_append(&vec, (char *)ids[n]);
1185   stash_command_vector(c, 0/*queuejump*/, no_response_opcallback, completed, v,
1186                        -1, 0, vec.nvec, vec.vec);
1187   disorder_eclient_polled(c, 0);
1188   return 0;
1189 }
1190
1191 int disorder_eclient_recent(disorder_eclient *c,
1192                             disorder_eclient_queue_response *completed,
1193                             void *v) {
1194   return simple(c, queue_response_opcallback, (void (*)())completed, v,
1195                 "recent", (char *)0);
1196 }
1197
1198 int disorder_eclient_queue(disorder_eclient *c,
1199                             disorder_eclient_queue_response *completed,
1200                             void *v) {
1201   return simple(c, queue_response_opcallback, (void (*)())completed, v,
1202                 "queue", (char *)0);
1203 }
1204
1205 int disorder_eclient_files(disorder_eclient *c,
1206                            disorder_eclient_list_response *completed,
1207                            const char *dir,
1208                            const char *re,
1209                            void *v) {
1210   return simple(c, list_response_opcallback, (void (*)())completed, v,
1211                 "files", dir, re, (char *)0);
1212 }
1213
1214 int disorder_eclient_dirs(disorder_eclient *c,
1215                           disorder_eclient_list_response *completed,
1216                           const char *dir,
1217                           const char *re,
1218                           void *v) {
1219   return simple(c, list_response_opcallback, (void (*)())completed, v,
1220                 "dirs", dir, re, (char *)0);
1221 }
1222
1223 int disorder_eclient_playing(disorder_eclient *c,
1224                              disorder_eclient_queue_response *completed,
1225                              void *v) {
1226   return simple(c, playing_response_opcallback, (void (*)())completed, v,
1227                 "playing", (char *)0);
1228 }
1229
1230 int disorder_eclient_length(disorder_eclient *c,
1231                             disorder_eclient_integer_response *completed,
1232                             const char *track,
1233                             void *v) {
1234   return simple(c, integer_response_opcallback, (void (*)())completed, v,
1235                 "length", track, (char *)0);
1236 }
1237
1238 int disorder_eclient_volume(disorder_eclient *c,
1239                             disorder_eclient_volume_response *completed,
1240                             int l, int r,
1241                             void *v) {
1242   char sl[64], sr[64];
1243
1244   if(l < 0 && r < 0) {
1245     return simple(c, volume_response_opcallback, (void (*)())completed, v,
1246                   "volume", (char *)0);
1247   } else if(l >= 0 && r >= 0) {
1248     assert(l <= 100);
1249     assert(r <= 100);
1250     byte_snprintf(sl, sizeof sl, "%d", l);
1251     byte_snprintf(sr, sizeof sr, "%d", r);
1252     return simple(c, volume_response_opcallback, (void (*)())completed, v,
1253                   "volume", sl, sr, (char *)0);
1254   } else {
1255     assert(!"invalid arguments to disorder_eclient_volume");
1256     return -1;                          /* gcc is being dim */
1257   }
1258 }
1259
1260 int disorder_eclient_enable(disorder_eclient *c,
1261                             disorder_eclient_no_response *completed,
1262                             void *v) {
1263   return simple(c, no_response_opcallback, (void (*)())completed, v,
1264                 "enable", (char *)0);
1265 }
1266
1267 int disorder_eclient_disable(disorder_eclient *c,
1268                              disorder_eclient_no_response *completed,
1269                              void *v){
1270   return simple(c, no_response_opcallback, (void (*)())completed, v,
1271                 "disable", (char *)0);
1272 }
1273
1274 int disorder_eclient_random_enable(disorder_eclient *c,
1275                                    disorder_eclient_no_response *completed,
1276                                    void *v){
1277   return simple(c, no_response_opcallback, (void (*)())completed, v,
1278                 "random-enable", (char *)0);
1279 }
1280
1281 int disorder_eclient_random_disable(disorder_eclient *c,
1282                                     disorder_eclient_no_response *completed,
1283                                     void *v){
1284   return simple(c, no_response_opcallback, (void (*)())completed, v,
1285                 "random-disable", (char *)0);
1286 }
1287
1288 int disorder_eclient_get(disorder_eclient *c,
1289                          disorder_eclient_string_response *completed,
1290                          const char *track, const char *pref,
1291                          void *v) {
1292   return simple(c, string_response_opcallback, (void (*)())completed, v, 
1293                 "get", track, pref, (char *)0);
1294 }
1295
1296 int disorder_eclient_set(disorder_eclient *c,
1297                          disorder_eclient_no_response *completed,
1298                          const char *track, const char *pref, 
1299                          const char *value,
1300                          void *v) {
1301   return simple(c, no_response_opcallback, (void (*)())completed, v, 
1302                 "set", track, pref, value, (char *)0);
1303 }
1304
1305 int disorder_eclient_unset(disorder_eclient *c,
1306                            disorder_eclient_no_response *completed,
1307                            const char *track, const char *pref, 
1308                            void *v) {
1309   return simple(c, no_response_opcallback, (void (*)())completed, v, 
1310                 "unset", track, pref, (char *)0);
1311 }
1312
1313 int disorder_eclient_resolve(disorder_eclient *c,
1314                              disorder_eclient_string_response *completed,
1315                              const char *track,
1316                              void *v) {
1317   return simple(c, string_response_opcallback,  (void (*)())completed, v, 
1318                 "resolve", track, (char *)0);
1319 }
1320
1321 int disorder_eclient_search(disorder_eclient *c,
1322                             disorder_eclient_list_response *completed,
1323                             const char *terms,
1324                             void *v) {
1325   if(!split(terms, 0, SPLIT_QUOTES, 0, 0)) return -1;
1326   return simple(c, list_response_opcallback, (void (*)())completed, v,
1327                 "search", terms, (char *)0);
1328 }
1329
1330 int disorder_eclient_nop(disorder_eclient *c,
1331                          disorder_eclient_no_response *completed,
1332                          void *v) {
1333   return simple(c, no_response_opcallback, (void (*)())completed, v, 
1334                 "nop", (char *)0);
1335 }
1336
1337 /** @brief Get the last @p max added tracks
1338  * @param c Client
1339  * @param completed Called with list
1340  * @param max Number of tracks to get, 0 for all
1341  * @param v Passed to @p completed
1342  *
1343  * The first track in the list is the most recently added.
1344  */
1345 int disorder_eclient_new_tracks(disorder_eclient *c,
1346                                 disorder_eclient_list_response *completed,
1347                                 int max,
1348                                 void *v) {
1349   char limit[32];
1350
1351   sprintf(limit, "%d", max);
1352   return simple(c, list_response_opcallback, (void (*)())completed, v,
1353                 "new", limit, (char *)0);
1354 }
1355
1356 static void rtp_response_opcallback(disorder_eclient *c,
1357                                     struct operation *op) {
1358   disorder_eclient_list_response *const completed =
1359     (disorder_eclient_list_response *)op->completed;
1360   D(("rtp_response_opcallback"));
1361   if(c->rc / 100 == 2) {
1362     int nvec;
1363     char **vec = split(c->line + 4, &nvec, SPLIT_QUOTES, 0, 0);
1364
1365     if(vec)
1366       completed(op->v, NULL, nvec, vec);
1367     else
1368       completed(op->v, "error parsing response", 0, 0);
1369   } else
1370     completed(op->v, errorstring(c), 0, 0);
1371 }
1372
1373 /** @brief Determine the RTP target address
1374  * @param c Client
1375  * @param completed Called with address details
1376  * @param v Passed to @p completed
1377  *
1378  * The address details will be two elements, the first being the hostname and
1379  * the second the service (port).
1380  */
1381 int disorder_eclient_rtp_address(disorder_eclient *c,
1382                                  disorder_eclient_list_response *completed,
1383                                  void *v) {
1384   return simple(c, rtp_response_opcallback, (void (*)())completed, v,
1385                 "rtp-address", (char *)0);
1386 }
1387
1388 /** @brief Get the list of users
1389  * @param c Client
1390  * @param completed Called with list of users
1391  * @param v Passed to @p completed
1392  *
1393  * The user list is not sorted in any particular order.
1394  */
1395 int disorder_eclient_users(disorder_eclient *c,
1396                            disorder_eclient_list_response *completed,
1397                            void *v) {
1398   return simple(c, list_response_opcallback, (void (*)())completed, v,
1399                 "users", (char *)0);
1400 }
1401
1402 /** @brief Delete a user
1403  * @param c Client
1404  * @param completed Called on completion
1405  * @param user User to delete
1406  * @param v Passed to @p completed
1407  */
1408 int disorder_eclient_deluser(disorder_eclient *c,
1409                              disorder_eclient_no_response *completed,
1410                              const char *user,
1411                              void *v) {
1412   return simple(c, no_response_opcallback, (void (*)())completed, v, 
1413                 "deluser", user, (char *)0);
1414 }
1415
1416 /** @brief Get a user property
1417  * @param c Client
1418  * @param completed Called on completion
1419  * @param user User to look up
1420  * @param property Property to look up
1421  * @param v Passed to @p completed
1422  */
1423 int disorder_eclient_userinfo(disorder_eclient *c,
1424                               disorder_eclient_string_response *completed,
1425                               const char *user,
1426                               const char *property,
1427                               void *v) {
1428   return simple(c, string_response_opcallback,  (void (*)())completed, v, 
1429                 "userinfo", user, property, (char *)0);
1430 }
1431
1432 /** @brief Modify a user property
1433  * @param c Client
1434  * @param completed Called on completion
1435  * @param user User to modify
1436  * @param property Property to modify
1437  * @param value New property value
1438  * @param v Passed to @p completed
1439  */
1440 int disorder_eclient_edituser(disorder_eclient *c,
1441                               disorder_eclient_no_response *completed,
1442                               const char *user,
1443                               const char *property,
1444                               const char *value,
1445                               void *v) {
1446   return simple(c, no_response_opcallback, (void (*)())completed, v, 
1447                 "edituser", user, property, value, (char *)0);
1448 }
1449
1450 /** @brief Create a new user
1451  * @param c Client
1452  * @param completed Called on completion
1453  * @param user User to create
1454  * @param password Initial password
1455  * @param rights Initial rights or NULL
1456  * @param v Passed to @p completed
1457  */
1458 int disorder_eclient_adduser(disorder_eclient *c,
1459                              disorder_eclient_no_response *completed,
1460                              const char *user,
1461                              const char *password,
1462                              const char *rights,
1463                              void *v) {
1464   return simple(c, no_response_opcallback, (void (*)())completed, v, 
1465                 "adduser", user, password, rights, (char *)0);
1466 }
1467
1468 /** @brief Adopt a track
1469  * @param c Client
1470  * @param completed Called on completion
1471  * @param id Track ID
1472  * @param v Passed to @p completed
1473  */
1474 int disorder_eclient_adopt(disorder_eclient *c,
1475                            disorder_eclient_no_response *completed,
1476                            const char *id,
1477                            void *v) {
1478   return simple(c, no_response_opcallback, (void (*)())completed, v, 
1479                 "adopt", id, (char *)0);
1480 }
1481
1482 /** @brief Get the list of playlists
1483  * @param c Client
1484  * @param completed Called with list of playlists
1485  * @param v Passed to @p completed
1486  *
1487  * The playlist list is not sorted in any particular order.
1488  */
1489 int disorder_eclient_playlists(disorder_eclient *c,
1490                                disorder_eclient_list_response *completed,
1491                                void *v) {
1492   return simple(c, list_response_opcallback, (void (*)())completed, v,
1493                 "playlists", (char *)0);
1494 }
1495
1496 /** @brief Delete a playlist
1497  * @param c Client
1498  * @param completed Called on completion
1499  * @param playlist Playlist to delete
1500  * @param v Passed to @p completed
1501  */
1502 int disorder_eclient_playlist_delete(disorder_eclient *c,
1503                                      disorder_eclient_no_response *completed,
1504                                      const char *playlist,
1505                                      void *v) {
1506   return simple(c, no_response_opcallback,  (void (*)())completed, v,
1507                 "playlist-delete", playlist, (char *)0);
1508 }
1509
1510 /** @brief Lock a playlist
1511  * @param c Client
1512  * @param completed Called on completion
1513  * @param playlist Playlist to lock
1514  * @param v Passed to @p completed
1515  */
1516 int disorder_eclient_playlist_lock(disorder_eclient *c,
1517                                    disorder_eclient_no_response *completed,
1518                                    const char *playlist,
1519                                    void *v) {
1520   return simple(c, no_response_opcallback,  (void (*)())completed, v,
1521                 "playlist-lock", playlist, (char *)0);
1522 }
1523
1524 /** @brief Unlock the locked a playlist
1525  * @param c Client
1526  * @param completed Called on completion
1527  * @param v Passed to @p completed
1528  */
1529 int disorder_eclient_playlist_unlock(disorder_eclient *c,
1530                                      disorder_eclient_no_response *completed,
1531                                      void *v) {
1532   return simple(c, no_response_opcallback,  (void (*)())completed, v,
1533                 "playlist-unlock", (char *)0);
1534 }
1535
1536 /** @brief Set a playlist's sharing
1537  * @param c Client
1538  * @param completed Called on completion
1539  * @param playlist Playlist to modify
1540  * @param sharing @c "public" or @c "private"
1541  * @param v Passed to @p completed
1542  */
1543 int disorder_eclient_playlist_set_share(disorder_eclient *c,
1544                                         disorder_eclient_no_response *completed,
1545                                         const char *playlist,
1546                                         const char *sharing,
1547                                         void *v) {
1548   return simple(c, no_response_opcallback,  (void (*)())completed, v,
1549                 "playlist-set-share", playlist, sharing, (char *)0);
1550 }
1551
1552 /** @brief Get a playlist's sharing
1553  * @param c Client
1554  * @param completed Called with sharing status
1555  * @param playlist Playlist to inspect
1556  * @param v Passed to @p completed
1557  */
1558 int disorder_eclient_playlist_get_share(disorder_eclient *c,
1559                                         disorder_eclient_string_response *completed,
1560                                         const char *playlist,
1561                                         void *v) {
1562   return simple(c, string_response_opcallback,  (void (*)())completed, v,
1563                 "playlist-get-share", playlist, (char *)0);
1564 }
1565
1566 /** @brief Set a playlist
1567  * @param c Client
1568  * @param completed Called on completion
1569  * @param playlist Playlist to modify
1570  * @param tracks List of tracks
1571  * @param ntracks Number of tracks
1572  * @param v Passed to @p completed
1573  */
1574 int disorder_eclient_playlist_set(disorder_eclient *c,
1575                                   disorder_eclient_no_response *completed,
1576                                   const char *playlist,
1577                                   char **tracks,
1578                                   int ntracks,
1579                                   void *v) {
1580   return simple_body(c, no_response_opcallback, (void (*)())completed, v,
1581                      ntracks, tracks,
1582                      "playlist-set", playlist, (char *)0);
1583 }
1584
1585 /** @brief Get a playlist's contents
1586  * @param c Client
1587  * @param completed Called with playlist contents
1588  * @param playlist Playlist to inspect
1589  * @param v Passed to @p completed
1590  */
1591 int disorder_eclient_playlist_get(disorder_eclient *c,
1592                                   disorder_eclient_list_response *completed,
1593                                   const char *playlist,
1594                                   void *v) {
1595   return simple(c, list_response_opcallback,  (void (*)())completed, v,
1596                 "playlist-get", playlist, (char *)0);
1597 }
1598
1599 /* Log clients ***************************************************************/
1600
1601 /** @brief Monitor the server log
1602  * @param c Client
1603  * @param callbacks Functions to call when anything happens
1604  * @param v Passed to @p callbacks functions
1605  *
1606  * Once a client is being used for logging it cannot be used for anything else.
1607  * There is magic in authuser_opcallback() to re-submit the @c log command
1608  * after reconnection.
1609  *
1610  * NB that the @c state callback may be called from within this function,
1611  * i.e. not solely later on from the event loop callback.
1612  */
1613 int disorder_eclient_log(disorder_eclient *c,
1614                          const disorder_eclient_log_callbacks *callbacks,
1615                          void *v) {
1616   if(c->log_callbacks) return -1;
1617   c->log_callbacks = callbacks;
1618   c->log_v = v;
1619   /* Repoort initial state */
1620   if(c->log_callbacks->state)
1621     c->log_callbacks->state(c->log_v, c->statebits);
1622   stash_command(c, 0/*queuejump*/, log_opcallback, 0/*completed*/, v,
1623                 -1, 0, "log", (char *)0);
1624   disorder_eclient_polled(c, 0);
1625   return 0;
1626 }
1627
1628 /* If we get here we've stopped being a log client */
1629 static void log_opcallback(disorder_eclient *c,
1630                            struct operation attribute((unused)) *op) {
1631   D(("log_opcallback"));
1632   c->log_callbacks = 0;
1633   c->log_v = 0;
1634 }
1635
1636 /* error callback for log line parsing */
1637 static void logline_error(const char *msg, void *u) {
1638   disorder_eclient *c = u;
1639   /* TODO don't use protocol_error here */
1640   protocol_error(c, c->ops, -1, "error parsing log line: %s", msg);
1641 }
1642
1643 /* process a single log line */
1644 static void logline(disorder_eclient *c, const char *line) {
1645   int nvec, n;
1646   char **vec;
1647   uintmax_t when;
1648
1649   D(("logline [%s]", line));
1650   vec = split(line, &nvec, SPLIT_QUOTES, logline_error, c);
1651   if(nvec < 2) return;                  /* probably an error, already
1652                                          * reported */
1653   if(sscanf(vec[0], "%"SCNxMAX, &when) != 1) {
1654     /* probably the wrong side of a format change */
1655     /* TODO don't use protocol_error here */
1656     protocol_error(c, c->ops, -1, "invalid log timestamp '%s'", vec[0]);
1657     return;
1658   }
1659   /* TODO: do something with the time */
1660   //fprintf(stderr, "log key: %s\n", vec[1]);
1661   n = TABLE_FIND(logentry_handlers, name, vec[1]);
1662   if(n < 0) {
1663     //fprintf(stderr, "...not found\n");
1664     return;                     /* probably a future command */
1665   }
1666   vec += 2;
1667   nvec -= 2;
1668   if(nvec < logentry_handlers[n].min || nvec > logentry_handlers[n].max) {
1669     //fprintf(stderr, "...wrong # args\n");
1670     return;
1671   }
1672   logentry_handlers[n].handler(c, nvec, vec);
1673 }
1674
1675 static void logentry_completed(disorder_eclient *c,
1676                                int attribute((unused)) nvec, char **vec) {
1677   c->statebits &= ~DISORDER_PLAYING;
1678   if(c->log_callbacks->completed)
1679     c->log_callbacks->completed(c->log_v, vec[0]);
1680   if(c->log_callbacks->state)
1681     c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
1682 }
1683
1684 static void logentry_failed(disorder_eclient *c,
1685                             int attribute((unused)) nvec, char **vec) {
1686   c->statebits &= ~DISORDER_PLAYING;
1687   if(c->log_callbacks->failed)
1688     c->log_callbacks->failed(c->log_v, vec[0], vec[1]);
1689   if(c->log_callbacks->state)
1690     c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
1691 }
1692
1693 static void logentry_moved(disorder_eclient *c,
1694                            int attribute((unused)) nvec, char **vec) {
1695   if(c->log_callbacks->moved)
1696     c->log_callbacks->moved(c->log_v, vec[0]);
1697 }
1698
1699 static void logentry_playing(disorder_eclient *c,
1700                              int attribute((unused)) nvec, char **vec) {
1701   c->statebits |= DISORDER_PLAYING;
1702   if(c->log_callbacks->playing)
1703     c->log_callbacks->playing(c->log_v, vec[0], vec[1]);
1704   if(c->log_callbacks->state)
1705     c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
1706 }
1707
1708 static void logentry_queue(disorder_eclient *c,
1709                            int attribute((unused)) nvec, char **vec) {
1710   struct queue_entry *q;
1711
1712   if(!c->log_callbacks->queue) return;
1713   q = xmalloc(sizeof *q);
1714   if(queue_unmarshall_vec(q, nvec, vec, eclient_queue_error, c))
1715     return;                             /* bogus */
1716   c->log_callbacks->queue(c->log_v, q);
1717 }
1718
1719 static void logentry_recent_added(disorder_eclient *c,
1720                                   int attribute((unused)) nvec, char **vec) {
1721   struct queue_entry *q;
1722
1723   if(!c->log_callbacks->recent_added) return;
1724   q = xmalloc(sizeof *q);
1725   if(queue_unmarshall_vec(q, nvec, vec, eclient_queue_error, c))
1726     return;                           /* bogus */
1727   c->log_callbacks->recent_added(c->log_v, q);
1728 }
1729
1730 static void logentry_recent_removed(disorder_eclient *c,
1731                                     int attribute((unused)) nvec, char **vec) {
1732   if(c->log_callbacks->recent_removed)
1733     c->log_callbacks->recent_removed(c->log_v, vec[0]);
1734 }
1735
1736 static void logentry_removed(disorder_eclient *c,
1737                              int attribute((unused)) nvec, char **vec) {
1738   if(c->log_callbacks->removed)
1739     c->log_callbacks->removed(c->log_v, vec[0], vec[1]);
1740 }
1741
1742 static void logentry_rescanned(disorder_eclient *c,
1743                                int attribute((unused)) nvec,
1744                                char attribute((unused)) **vec) {
1745   if(c->log_callbacks->rescanned)
1746     c->log_callbacks->rescanned(c->log_v);
1747 }
1748
1749 static void logentry_scratched(disorder_eclient *c,
1750                                int attribute((unused)) nvec, char **vec) {
1751   c->statebits &= ~DISORDER_PLAYING;
1752   if(c->log_callbacks->scratched)
1753     c->log_callbacks->scratched(c->log_v, vec[0], vec[1]);
1754   if(c->log_callbacks->state)
1755     c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
1756 }
1757
1758 static void logentry_user_add(disorder_eclient *c,
1759                               int attribute((unused)) nvec, char **vec) {
1760   if(c->log_callbacks->user_add)
1761     c->log_callbacks->user_add(c->log_v, vec[0]);
1762 }
1763
1764 static void logentry_user_confirm(disorder_eclient *c,
1765                               int attribute((unused)) nvec, char **vec) {
1766   if(c->log_callbacks->user_confirm)
1767     c->log_callbacks->user_confirm(c->log_v, vec[0]);
1768 }
1769
1770 static void logentry_user_delete(disorder_eclient *c,
1771                               int attribute((unused)) nvec, char **vec) {
1772   if(c->log_callbacks->user_delete)
1773     c->log_callbacks->user_delete(c->log_v, vec[0]);
1774 }
1775
1776 static void logentry_user_edit(disorder_eclient *c,
1777                               int attribute((unused)) nvec, char **vec) {
1778   if(c->log_callbacks->user_edit)
1779     c->log_callbacks->user_edit(c->log_v, vec[0], vec[1]);
1780 }
1781
1782 static void logentry_rights_changed(disorder_eclient *c,
1783                                     int attribute((unused)) nvec, char **vec) {
1784   if(c->log_callbacks->rights_changed) {
1785     rights_type r;
1786     if(!parse_rights(vec[0], &r, 0/*report*/))
1787       c->log_callbacks->rights_changed(c->log_v, r);
1788   }
1789 }
1790
1791 static void logentry_playlist_created(disorder_eclient *c,
1792                                       int attribute((unused)) nvec,
1793                                       char **vec) {
1794   if(c->log_callbacks->playlist_created)
1795     c->log_callbacks->playlist_created(c->log_v, vec[0], vec[1]);
1796 }
1797
1798 static void logentry_playlist_deleted(disorder_eclient *c,
1799                                       int attribute((unused)) nvec,
1800                                       char **vec) {
1801   if(c->log_callbacks->playlist_deleted)
1802     c->log_callbacks->playlist_deleted(c->log_v, vec[0]);
1803 }
1804
1805 static void logentry_playlist_modified(disorder_eclient *c,
1806                                       int attribute((unused)) nvec,
1807                                       char **vec) {
1808   if(c->log_callbacks->playlist_modified)
1809     c->log_callbacks->playlist_modified(c->log_v, vec[0], vec[1]);
1810 }
1811
1812 static const struct {
1813   unsigned long bit;
1814   const char *enable;
1815   const char *disable;
1816 } statestrings[] = {
1817   { DISORDER_PLAYING_ENABLED, "enable_play", "disable_play" },
1818   { DISORDER_RANDOM_ENABLED, "enable_random", "disable_random" },
1819   { DISORDER_TRACK_PAUSED, "pause", "resume" },
1820   { DISORDER_PLAYING, "playing", "completed" },
1821   { DISORDER_PLAYING, 0, "scratched" },
1822   { DISORDER_PLAYING, 0, "failed" },
1823 };
1824 #define NSTATES (int)(sizeof statestrings / sizeof *statestrings)
1825
1826 static void logentry_state(disorder_eclient *c,
1827                            int attribute((unused)) nvec, char **vec) {
1828   int n;
1829
1830   for(n = 0; n < NSTATES; ++n)
1831     if(statestrings[n].enable && !strcmp(vec[0], statestrings[n].enable)) {
1832       c->statebits |= statestrings[n].bit;
1833       break;
1834     } else if(statestrings[n].disable && !strcmp(vec[0], statestrings[n].disable)) {
1835       c->statebits &= ~statestrings[n].bit;
1836       break;
1837     }
1838   if(c->log_callbacks->state) 
1839     c->log_callbacks->state(c->log_v, c->statebits | DISORDER_CONNECTED);
1840 }
1841
1842 static void logentry_volume(disorder_eclient *c,
1843                             int attribute((unused)) nvec, char **vec) {
1844   long l, r;
1845
1846   if(!c->log_callbacks->volume) return;
1847   if(xstrtol(&l, vec[0], 0, 10)
1848      || xstrtol(&r, vec[1], 0, 10)
1849      || l < 0 || l > INT_MAX
1850      || r < 0 || r > INT_MAX)
1851     return;                             /* bogus */
1852   c->log_callbacks->volume(c->log_v, (int)l, (int)r);
1853 }
1854
1855 /** @brief Convert @p statebits to a string */
1856 char *disorder_eclient_interpret_state(unsigned long statebits) {
1857   struct dynstr d[1];
1858   size_t n;
1859
1860   static const struct {
1861     unsigned long bit;
1862     const char *name;
1863   } bits[] = {
1864     { DISORDER_PLAYING_ENABLED, "playing_enabled" },
1865     { DISORDER_RANDOM_ENABLED, "random_enabled" },
1866     { DISORDER_TRACK_PAUSED, "track_paused" },
1867     { DISORDER_PLAYING, "playing" },
1868     { DISORDER_CONNECTED, "connected" },
1869   };
1870 #define NBITS (sizeof bits / sizeof *bits)
1871
1872   dynstr_init(d);
1873   if(!statebits)
1874     dynstr_append(d, '0');
1875   for(n = 0; n < NBITS; ++n)
1876     if(statebits & bits[n].bit) {
1877       if(d->nvec)
1878         dynstr_append(d, '|');
1879       dynstr_append_string(d, bits[n].name);
1880       statebits ^= bits[n].bit;
1881     }
1882   if(statebits) {
1883     char s[20];
1884
1885     if(d->nvec)
1886       dynstr_append(d, '|');
1887     sprintf(s, "%#lx", statebits);
1888     dynstr_append_string(d, s);
1889   }
1890   dynstr_terminate(d);
1891   return d->vec;
1892 }
1893
1894 static void logentry_adopted(disorder_eclient *c,
1895                              int attribute((unused)) nvec, char **vec) {
1896   if(c->log_callbacks->adopted) 
1897     c->log_callbacks->adopted(c->log_v, vec[0], vec[1]);
1898 }
1899
1900 /*
1901 Local Variables:
1902 c-basic-offset:2
1903 comment-column:40
1904 fill-column:79
1905 indent-tabs-mode:nil
1906 End:
1907 */