chiark / gitweb /
missing initialization
[disorder] / server / server.c
CommitLineData
460b9539 1/*
2 * This file is part of DisOrder.
768d7355 3 * Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell
460b9539 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 2 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, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * 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, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18 * USA
19 */
20
21#include <config.h>
22#include "types.h"
23
24#include <pwd.h>
25#include <sys/types.h>
26#include <sys/socket.h>
27#include <sys/time.h>
28#include <unistd.h>
29#include <stdlib.h>
30#include <string.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <gcrypt.h>
34#include <stddef.h>
35#include <time.h>
36#include <limits.h>
37#include <pcre.h>
38#include <netdb.h>
39#include <netinet/in.h>
40
41#include "event.h"
42#include "server.h"
43#include "syscalls.h"
44#include "queue.h"
f08c0859 45#include "server-queue.h"
460b9539 46#include "play.h"
47#include "log.h"
48#include "mem.h"
49#include "state.h"
50#include "charset.h"
51#include "split.h"
52#include "configuration.h"
53#include "hex.h"
5df73aeb 54#include "rights.h"
460b9539 55#include "trackdb.h"
56#include "table.h"
57#include "kvp.h"
58#include "mixer.h"
59#include "sink.h"
60#include "authhash.h"
61#include "plugin.h"
62#include "printf.h"
63#include "trackname.h"
64#include "eventlog.h"
65#include "defs.h"
66#include "cache.h"
f9635e06 67#include "unicode.h"
b12be54a 68#include "cookies.h"
460b9539 69
70#ifndef NONCE_SIZE
71# define NONCE_SIZE 16
72#endif
73
74int volume_left, volume_right; /* last known volume */
75
18e6d6e6 76/** @brief Accept all well-formed login attempts
77 *
78 * Used in debugging.
79 */
80int wideopen;
81
460b9539 82struct listener {
83 const char *name;
84 int pf;
85};
86
397ef7bb 87/** @brief One client connection */
460b9539 88struct conn {
397ef7bb 89 /** @brief Read commands from here */
460b9539 90 ev_reader *r;
397ef7bb 91 /** @brief Send responses to here */
460b9539 92 ev_writer *w;
397ef7bb 93 /** @brief Underlying file descriptor */
460b9539 94 int fd;
397ef7bb 95 /** @brief Unique identifier for connection used in log messages */
460b9539 96 unsigned tag;
397ef7bb 97 /** @brief Login name or NULL */
460b9539 98 char *who;
397ef7bb 99 /** @brief Event loop */
460b9539 100 ev_source *ev;
397ef7bb 101 /** @brief Nonce chosen for this connection */
460b9539 102 unsigned char nonce[NONCE_SIZE];
397ef7bb
RK
103 /** @brief Current reader callback
104 *
105 * We change this depending on whether we're servicing the @b log command
106 */
460b9539 107 ev_reader_callback *reader;
397ef7bb 108 /** @brief Event log output sending to this connection */
460b9539 109 struct eventlog_output *lo;
397ef7bb 110 /** @brief Parent listener */
460b9539 111 const struct listener *l;
b12be54a
RK
112 /** @brief Login cookie or NULL */
113 char *cookie;
eb5dc014
RK
114 /** @brief Connection rights */
115 rights_type rights;
460b9539 116};
117
118static int reader_callback(ev_source *ev,
119 ev_reader *reader,
460b9539 120 void *ptr,
121 size_t bytes,
122 int eof,
123 void *u);
124
125static const char *noyes[] = { "no", "yes" };
126
397ef7bb
RK
127/** @brief Called when a connection's writer fails or is shut down
128 *
129 * If the connection still has a raeder that is cancelled.
130 */
460b9539 131static int writer_error(ev_source attribute((unused)) *ev,
460b9539 132 int errno_value,
133 void *u) {
134 struct conn *c = u;
135
8d8b8c1f 136 D(("server writer_error S%x %d", c->tag, errno_value));
460b9539 137 if(errno_value == 0) {
138 /* writer is done */
8d8b8c1f 139 D(("S%x writer completed", c->tag));
460b9539 140 } else {
141 if(errno_value != EPIPE)
142 error(errno_value, "S%x write error on socket", c->tag);
38b8221f 143 if(c->r) {
8d8b8c1f 144 D(("cancel reader"));
38b8221f
RK
145 ev_reader_cancel(c->r);
146 c->r = 0;
147 }
8d8b8c1f 148 D(("done cancel reader"));
460b9539 149 }
38b8221f 150 c->w = 0;
75d64210 151 ev_report(ev);
460b9539 152 return 0;
153}
154
397ef7bb
RK
155/** @brief Called when a conncetion's reader fails or is shut down
156 *
157 * If connection still has a writer then it is closed.
158 */
460b9539 159static int reader_error(ev_source attribute((unused)) *ev,
460b9539 160 int errno_value,
161 void *u) {
162 struct conn *c = u;
163
8d8b8c1f 164 D(("server reader_error S%x %d", c->tag, errno_value));
1563f6f6 165 error(errno_value, "S%x read error on socket", c->tag);
38b8221f
RK
166 if(c->w)
167 ev_writer_close(c->w);
168 c->w = 0;
169 c->r = 0;
768d7355 170 ev_report(ev);
460b9539 171 return 0;
172}
173
460b9539 174static int c_disable(struct conn *c, char **vec, int nvec) {
175 if(nvec == 0)
176 disable_playing(c->who);
177 else if(nvec == 1 && !strcmp(vec[0], "now"))
178 disable_playing(c->who);
179 else {
180 sink_writes(ev_writer_sink(c->w), "550 invalid argument\n");
181 return 1; /* completed */
182 }
183 sink_writes(ev_writer_sink(c->w), "250 OK\n");
184 return 1; /* completed */
185}
186
187static int c_enable(struct conn *c,
188 char attribute((unused)) **vec,
189 int attribute((unused)) nvec) {
190 enable_playing(c->who, c->ev);
191 /* Enable implicitly unpauses if there is nothing playing */
192 if(paused && !playing) resume_playing(c->who);
193 sink_writes(ev_writer_sink(c->w), "250 OK\n");
194 return 1; /* completed */
195}
196
197static int c_enabled(struct conn *c,
198 char attribute((unused)) **vec,
199 int attribute((unused)) nvec) {
200 sink_printf(ev_writer_sink(c->w), "252 %s\n", noyes[playing_is_enabled()]);
201 return 1; /* completed */
202}
203
204static int c_play(struct conn *c, char **vec,
205 int attribute((unused)) nvec) {
206 const char *track;
207 struct queue_entry *q;
208
209 if(!trackdb_exists(vec[0])) {
210 sink_writes(ev_writer_sink(c->w), "550 track is not in database\n");
211 return 1;
212 }
213 if(!(track = trackdb_resolve(vec[0]))) {
214 sink_writes(ev_writer_sink(c->w), "550 cannot resolve track\n");
215 return 1;
216 }
217 q = queue_add(track, c->who, WHERE_BEFORE_RANDOM);
218 queue_write();
219 /* If we added the first track, and something is playing, then prepare the
220 * new track. If nothing is playing then we don't bother as it wouldn't gain
221 * anything. */
222 if(q == qhead.next && playing)
223 prepare(c->ev, q);
81e440ce 224 sink_printf(ev_writer_sink(c->w), "252 %s\n", q->id);
460b9539 225 /* If the queue was empty but we are for some reason paused then
226 * unpause. */
227 if(!playing) resume_playing(0);
228 play(c->ev);
229 return 1; /* completed */
230}
231
232static int c_remove(struct conn *c, char **vec,
233 int attribute((unused)) nvec) {
234 struct queue_entry *q;
eb5dc014 235 rights_type r;
460b9539 236
237 if(!(q = queue_find(vec[0]))) {
238 sink_writes(ev_writer_sink(c->w), "550 no such track on the queue\n");
239 return 1;
240 }
eb5dc014
RK
241 if(q->submitter)
242 if(!strcmp(q->submitter, c->who))
243 r = RIGHT_REMOVE_MINE;
244 else
245 r = RIGHT_REMOVE_ANY;
246 else
247 r = RIGHT_REMOVE_RANDOM;
248 if(!(c->rights & r)) {
249 sink_writes(ev_writer_sink(c->w),
250 "550 Not authorized to remove that track\n");
251 return 1;
460b9539 252 }
253 queue_remove(q, c->who);
254 /* De-prepare the track. */
255 abandon(c->ev, q);
eb5dc014 256 /* If we removed a random track then add another one. */
460b9539 257 if(q->state == playing_random)
258 add_random_track();
259 /* Prepare whatever the next head track is. */
260 if(qhead.next != &qhead)
261 prepare(c->ev, qhead.next);
262 queue_write();
263 sink_writes(ev_writer_sink(c->w), "250 removed\n");
264 return 1; /* completed */
265}
266
267static int c_scratch(struct conn *c,
268 char **vec,
269 int nvec) {
eb5dc014
RK
270 rights_type r;
271
460b9539 272 if(!playing) {
273 sink_writes(ev_writer_sink(c->w), "250 nothing is playing\n");
274 return 1; /* completed */
275 }
eb5dc014
RK
276 /* TODO there is a bug here: if we specify an ID but it's not the currently
277 * playing track then you will get 550 if you weren't authorized to scratch
278 * the currently playing track. */
279 if(playing->submitter)
280 if(!strcmp(playing->submitter, c->who))
281 r = RIGHT_SCRATCH_MINE;
282 else
283 r = RIGHT_SCRATCH_ANY;
284 else
285 r = RIGHT_SCRATCH_RANDOM;
286 if(!(c->rights & r)) {
287 sink_writes(ev_writer_sink(c->w),
288 "550 Not authorized to scratch that track\n");
289 return 1;
460b9539 290 }
291 scratch(c->who, nvec == 1 ? vec[0] : 0);
292 /* If you scratch an unpaused track then it is automatically unpaused */
293 resume_playing(0);
294 sink_writes(ev_writer_sink(c->w), "250 scratched\n");
295 return 1; /* completed */
296}
297
298static int c_pause(struct conn *c,
299 char attribute((unused)) **vec,
300 int attribute((unused)) nvec) {
301 if(!playing) {
302 sink_writes(ev_writer_sink(c->w), "250 nothing is playing\n");
303 return 1; /* completed */
304 }
305 if(paused) {
306 sink_writes(ev_writer_sink(c->w), "250 already paused\n");
307 return 1; /* completed */
308 }
309 if(pause_playing(c->who) < 0)
310 sink_writes(ev_writer_sink(c->w), "550 cannot pause this track\n");
311 else
312 sink_writes(ev_writer_sink(c->w), "250 paused\n");
313 return 1;
314}
315
316static int c_resume(struct conn *c,
317 char attribute((unused)) **vec,
318 int attribute((unused)) nvec) {
319 if(!paused) {
320 sink_writes(ev_writer_sink(c->w), "250 not paused\n");
321 return 1; /* completed */
322 }
323 resume_playing(c->who);
324 sink_writes(ev_writer_sink(c->w), "250 paused\n");
325 return 1;
326}
327
328static int c_shutdown(struct conn *c,
329 char attribute((unused)) **vec,
330 int attribute((unused)) nvec) {
331 info("S%x shut down by %s", c->tag, c->who);
332 sink_writes(ev_writer_sink(c->w), "250 shutting down\n");
333 ev_writer_flush(c->w);
334 quit(c->ev);
335}
336
337static int c_reconfigure(struct conn *c,
338 char attribute((unused)) **vec,
339 int attribute((unused)) nvec) {
340 info("S%x reconfigure by %s", c->tag, c->who);
341 if(reconfigure(c->ev, 1))
342 sink_writes(ev_writer_sink(c->w), "550 error reading new config\n");
343 else
344 sink_writes(ev_writer_sink(c->w), "250 installed new config\n");
345 return 1; /* completed */
346}
347
348static int c_rescan(struct conn *c,
349 char attribute((unused)) **vec,
350 int attribute((unused)) nvec) {
351 info("S%x rescan by %s", c->tag, c->who);
352 trackdb_rescan(c->ev);
353 sink_writes(ev_writer_sink(c->w), "250 initiated rescan\n");
354 return 1; /* completed */
355}
356
357static int c_version(struct conn *c,
358 char attribute((unused)) **vec,
359 int attribute((unused)) nvec) {
360 /* VERSION had better only use the basic character set */
a05e4467 361 sink_printf(ev_writer_sink(c->w), "251 %s\n", disorder_short_version_string);
460b9539 362 return 1; /* completed */
363}
364
365static int c_playing(struct conn *c,
366 char attribute((unused)) **vec,
367 int attribute((unused)) nvec) {
368 if(playing) {
369 queue_fix_sofar(playing);
370 playing->expected = 0;
371 sink_printf(ev_writer_sink(c->w), "252 %s\n", queue_marshall(playing));
372 } else
373 sink_printf(ev_writer_sink(c->w), "259 nothing playing\n");
374 return 1; /* completed */
375}
376
b12be54a 377static const char *connection_host(struct conn *c) {
460b9539 378 union {
379 struct sockaddr sa;
380 struct sockaddr_in in;
381 struct sockaddr_in6 in6;
382 } u;
383 socklen_t l;
b12be54a 384 int n;
460b9539 385 char host[1024];
386
460b9539 387 /* get connection data */
388 l = sizeof u;
389 if(getpeername(c->fd, &u.sa, &l) < 0) {
390 error(errno, "S%x error calling getpeername", c->tag);
b12be54a 391 return 0;
460b9539 392 }
393 if(c->l->pf != PF_UNIX) {
394 if((n = getnameinfo(&u.sa, l,
395 host, sizeof host, 0, 0, NI_NUMERICHOST))) {
396 error(0, "S%x error calling getnameinfo: %s", c->tag, gai_strerror(n));
b12be54a 397 return 0;
460b9539 398 }
b12be54a 399 return xstrdup(host);
18e6d6e6 400 } else
b12be54a
RK
401 return "local";
402}
403
404static int c_user(struct conn *c,
405 char **vec,
406 int attribute((unused)) nvec) {
eb5dc014 407 struct kvp *k;
f0feb22e 408 const char *res, *host, *password;
eb5dc014 409 rights_type rights;
b12be54a
RK
410
411 if(c->who) {
412 sink_writes(ev_writer_sink(c->w), "530 already authenticated\n");
413 return 1;
414 }
415 /* get connection data */
416 if(!(host = connection_host(c))) {
417 sink_writes(ev_writer_sink(c->w), "530 authentication failure\n");
418 return 1;
419 }
460b9539 420 /* find the user */
eb5dc014 421 k = trackdb_getuserinfo(vec[0]);
f0feb22e 422 /* reject nonexistent users */
eb5dc014
RK
423 if(!k) {
424 error(0, "S%x unknown user '%s' from %s", c->tag, vec[0], host);
425 sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
426 return 1;
427 }
428 /* reject unconfirmed users */
429 if(kvp_get(k, "confirmation")) {
430 error(0, "S%x unconfirmed user '%s' from %s", c->tag, vec[0], host);
431 sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
432 return 1;
433 }
434 password = kvp_get(k, "password");
435 if(!password) password = "";
436 if(parse_rights(kvp_get(k, "rights"), &rights)) {
437 error(0, "error parsing rights for %s", vec[0]);
18e6d6e6 438 sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
439 return 1;
440 }
f0feb22e
RK
441 /* check whether the response is right */
442 res = authhash(c->nonce, sizeof c->nonce, password,
637fdea3 443 config->authorization_algorithm);
18e6d6e6 444 if(wideopen || (res && !strcmp(res, vec[1]))) {
445 c->who = vec[0];
eb5dc014 446 c->rights = rights;
18e6d6e6 447 /* currently we only bother logging remote connections */
eb5dc014 448 if(strcmp(host, "local")) {
18e6d6e6 449 info("S%x %s connected from %s", c->tag, vec[0], host);
eb5dc014
RK
450 c->rights |= RIGHT__LOCAL;
451 }
18e6d6e6 452 sink_writes(ev_writer_sink(c->w), "230 OK\n");
453 return 1;
460b9539 454 }
455 /* oops, response was wrong */
18e6d6e6 456 info("S%x authentication failure for %s from %s", c->tag, vec[0], host);
460b9539 457 sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
458 return 1;
459}
460
461static int c_recent(struct conn *c,
462 char attribute((unused)) **vec,
463 int attribute((unused)) nvec) {
464 const struct queue_entry *q;
465
466 sink_writes(ev_writer_sink(c->w), "253 Tracks follow\n");
467 for(q = phead.next; q != &phead; q = q->next)
468 sink_printf(ev_writer_sink(c->w), " %s\n", queue_marshall(q));
469 sink_writes(ev_writer_sink(c->w), ".\n");
470 return 1; /* completed */
471}
472
473static int c_queue(struct conn *c,
474 char attribute((unused)) **vec,
475 int attribute((unused)) nvec) {
476 struct queue_entry *q;
477 time_t when = 0;
478 const char *l;
479 long length;
480
481 sink_writes(ev_writer_sink(c->w), "253 Tracks follow\n");
482 if(playing_is_enabled() && !paused) {
483 if(playing) {
484 queue_fix_sofar(playing);
485 if((l = trackdb_get(playing->track, "_length"))
486 && (length = atol(l))) {
487 time(&when);
488 when += length - playing->sofar + config->gap;
489 }
490 } else
491 /* Nothing is playing but playing is enabled, so whatever is
492 * first in the queue can be expected to start immediately. */
493 time(&when);
494 }
495 for(q = qhead.next; q != &qhead; q = q->next) {
496 /* fill in estimated start time */
497 q->expected = when;
498 sink_printf(ev_writer_sink(c->w), " %s\n", queue_marshall(q));
499 /* update for next track */
500 if(when) {
501 if((l = trackdb_get(q->track, "_length"))
502 && (length = atol(l)))
503 when += length + config->gap;
504 else
505 when = 0;
506 }
507 }
508 sink_writes(ev_writer_sink(c->w), ".\n");
509 return 1; /* completed */
510}
511
512static int output_list(struct conn *c, char **vec) {
513 while(*vec)
514 sink_printf(ev_writer_sink(c->w), "%s\n", *vec++);
515 sink_writes(ev_writer_sink(c->w), ".\n");
516 return 1;
517}
518
519static int files_dirs(struct conn *c,
520 char **vec,
521 int nvec,
522 enum trackdb_listable what) {
523 const char *dir, *re, *errstr;
524 int erroffset;
525 pcre *rec;
526 char **fvec, *key;
527
528 switch(nvec) {
529 case 0: dir = 0; re = 0; break;
530 case 1: dir = vec[0]; re = 0; break;
531 case 2: dir = vec[0]; re = vec[1]; break;
532 default: abort();
533 }
534 /* A bit of a bodge to make sure the args don't trample on cache keys */
535 if(dir && strchr(dir, '\n')) {
536 sink_writes(ev_writer_sink(c->w), "550 invalid directory name\n");
537 return 1;
538 }
539 if(re && strchr(re, '\n')) {
540 sink_writes(ev_writer_sink(c->w), "550 invalid regexp\n");
541 return 1;
542 }
543 /* We bother eliminating "" because the web interface is relatively
544 * likely to send it */
545 if(re && *re) {
546 byte_xasprintf(&key, "%d\n%s\n%s", (int)what, dir ? dir : "", re);
547 fvec = (char **)cache_get(&cache_files_type, key);
548 if(fvec) {
549 /* Got a cache hit, don't store the answer in the cache */
550 key = 0;
551 ++cache_files_hits;
552 rec = 0; /* quieten compiler */
553 } else {
554 /* Cache miss, we'll do the lookup and key != 0 so we'll store the answer
555 * in the cache. */
556 if(!(rec = pcre_compile(re, PCRE_CASELESS|PCRE_UTF8,
557 &errstr, &erroffset, 0))) {
558 sink_printf(ev_writer_sink(c->w), "550 Error compiling regexp: %s\n",
559 errstr);
560 return 1;
561 }
562 /* It only counts as a miss if the regexp was valid. */
563 ++cache_files_misses;
564 }
565 } else {
566 /* No regexp, don't bother caching the result */
567 rec = 0;
568 key = 0;
569 fvec = 0;
570 }
571 if(!fvec) {
572 /* No cache hit (either because a miss, or because we did not look) so do
573 * the lookup */
574 if(dir && *dir)
575 fvec = trackdb_list(dir, 0, what, rec);
576 else
577 fvec = trackdb_list(0, 0, what, rec);
578 }
579 if(key)
580 /* Put the answer in the cache */
581 cache_put(&cache_files_type, key, fvec);
582 sink_writes(ev_writer_sink(c->w), "253 Listing follow\n");
583 return output_list(c, fvec);
584}
585
586static int c_files(struct conn *c,
587 char **vec,
588 int nvec) {
589 return files_dirs(c, vec, nvec, trackdb_files);
590}
591
592static int c_dirs(struct conn *c,
593 char **vec,
594 int nvec) {
595 return files_dirs(c, vec, nvec, trackdb_directories);
596}
597
598static int c_allfiles(struct conn *c,
599 char **vec,
600 int nvec) {
601 return files_dirs(c, vec, nvec, trackdb_directories|trackdb_files);
602}
603
604static int c_get(struct conn *c,
605 char **vec,
606 int attribute((unused)) nvec) {
607 const char *v;
608
609 if(vec[1][0] != '_' && (v = trackdb_get(vec[0], vec[1])))
7b32e917 610 sink_printf(ev_writer_sink(c->w), "252 %s\n", quoteutf8(v));
460b9539 611 else
fb1bc1f5 612 sink_writes(ev_writer_sink(c->w), "555 not found\n");
460b9539 613 return 1;
614}
615
616static int c_length(struct conn *c,
617 char **vec,
618 int attribute((unused)) nvec) {
619 const char *track, *v;
620
621 if(!(track = trackdb_resolve(vec[0]))) {
622 sink_writes(ev_writer_sink(c->w), "550 cannot resolve track\n");
623 return 1;
624 }
625 if((v = trackdb_get(track, "_length")))
7b32e917 626 sink_printf(ev_writer_sink(c->w), "252 %s\n", quoteutf8(v));
460b9539 627 else
628 sink_writes(ev_writer_sink(c->w), "550 not found\n");
629 return 1;
630}
631
632static int c_set(struct conn *c,
633 char **vec,
634 int attribute((unused)) nvec) {
635 if(vec[1][0] != '_' && !trackdb_set(vec[0], vec[1], vec[2]))
636 sink_writes(ev_writer_sink(c->w), "250 OK\n");
637 else
638 sink_writes(ev_writer_sink(c->w), "550 not found\n");
639 return 1;
640}
641
642static int c_prefs(struct conn *c,
643 char **vec,
644 int attribute((unused)) nvec) {
645 struct kvp *k;
646
647 k = trackdb_get_all(vec[0]);
648 sink_writes(ev_writer_sink(c->w), "253 prefs follow\n");
649 for(; k; k = k->next)
650 if(k->name[0] != '_') /* omit internal values */
651 sink_printf(ev_writer_sink(c->w),
652 " %s %s\n", quoteutf8(k->name), quoteutf8(k->value));
653 sink_writes(ev_writer_sink(c->w), ".\n");
654 return 1;
655}
656
657static int c_exists(struct conn *c,
658 char **vec,
659 int attribute((unused)) nvec) {
660 sink_printf(ev_writer_sink(c->w), "252 %s\n", noyes[trackdb_exists(vec[0])]);
661 return 1;
662}
663
664static void search_parse_error(const char *msg, void *u) {
665 *(const char **)u = msg;
666}
667
668static int c_search(struct conn *c,
669 char **vec,
670 int attribute((unused)) nvec) {
671 char **terms, **results;
672 int nterms, nresults, n;
673 const char *e = "unknown error";
674
675 /* This is a bit of a bodge. Initially it's there to make the eclient
676 * interface a bit more convenient to add searching to, but it has the more
677 * compelling advantage that if everything uses it, then interpretation of
678 * user-supplied search strings will be the same everywhere. */
679 if(!(terms = split(vec[0], &nterms, SPLIT_QUOTES, search_parse_error, &e))) {
680 sink_printf(ev_writer_sink(c->w), "550 %s\n", e);
681 } else {
682 results = trackdb_search(terms, nterms, &nresults);
683 sink_printf(ev_writer_sink(c->w), "253 %d matches\n", nresults);
684 for(n = 0; n < nresults; ++n)
685 sink_printf(ev_writer_sink(c->w), "%s\n", results[n]);
686 sink_writes(ev_writer_sink(c->w), ".\n");
687 }
688 return 1;
689}
690
691static int c_random_enable(struct conn *c,
692 char attribute((unused)) **vec,
693 int attribute((unused)) nvec) {
694 enable_random(c->who, c->ev);
695 /* Enable implicitly unpauses if there is nothing playing */
696 if(paused && !playing) resume_playing(c->who);
697 sink_writes(ev_writer_sink(c->w), "250 OK\n");
698 return 1; /* completed */
699}
700
701static int c_random_disable(struct conn *c,
702 char attribute((unused)) **vec,
703 int attribute((unused)) nvec) {
704 disable_random(c->who);
705 sink_writes(ev_writer_sink(c->w), "250 OK\n");
706 return 1; /* completed */
707}
708
709static int c_random_enabled(struct conn *c,
710 char attribute((unused)) **vec,
711 int attribute((unused)) nvec) {
712 sink_printf(ev_writer_sink(c->w), "252 %s\n", noyes[random_is_enabled()]);
713 return 1; /* completed */
714}
715
d6dde5a3
RK
716static void got_stats(char *stats, void *u) {
717 struct conn *const c = u;
718
719 sink_printf(ev_writer_sink(c->w), "253 stats\n%s\n.\n", stats);
720 /* Now we can start processing commands again */
721 ev_reader_enable(c->r);
722}
723
460b9539 724static int c_stats(struct conn *c,
725 char attribute((unused)) **vec,
726 int attribute((unused)) nvec) {
d6dde5a3
RK
727 trackdb_stats_subprocess(c->ev, got_stats, c);
728 return 0; /* not yet complete */
460b9539 729}
730
731static int c_volume(struct conn *c,
732 char **vec,
733 int nvec) {
734 int l, r, set;
735 char lb[32], rb[32];
eb5dc014 736 rights_type rights;
460b9539 737
738 switch(nvec) {
739 case 0:
740 set = 0;
741 break;
742 case 1:
743 l = r = atoi(vec[0]);
744 set = 1;
745 break;
746 case 2:
747 l = atoi(vec[0]);
748 r = atoi(vec[1]);
749 set = 1;
750 break;
751 default:
752 abort();
753 }
eb5dc014
RK
754 rights = set ? RIGHT_VOLUME : RIGHT_READ;
755 if(!(c->rights & rights)) {
756 sink_writes(ev_writer_sink(c->w), "530 Prohibited\n");
757 return 1;
758 }
460b9539 759 if(mixer_control(&l, &r, set))
760 sink_writes(ev_writer_sink(c->w), "550 error accessing mixer\n");
761 else {
762 sink_printf(ev_writer_sink(c->w), "252 %d %d\n", l, r);
763 if(l != volume_left || r != volume_right) {
764 volume_left = l;
765 volume_right = r;
766 snprintf(lb, sizeof lb, "%d", l);
767 snprintf(rb, sizeof rb, "%d", r);
768 eventlog("volume", lb, rb, (char *)0);
769 }
770 }
771 return 1;
772}
773
397ef7bb
RK
774/** @brief Called when data arrives on a log connection
775 *
776 * We just discard all such data. The client may occasionally send data as a
777 * keepalive.
778 */
779static int logging_reader_callback(ev_source attribute((unused)) *ev,
460b9539 780 ev_reader *reader,
397ef7bb 781 void attribute((unused)) *ptr,
460b9539 782 size_t bytes,
397ef7bb
RK
783 int attribute((unused)) eof,
784 void attribute((unused)) *u) {
460b9539 785 struct conn *c = u;
786
397ef7bb
RK
787 ev_reader_consume(reader, bytes);
788 if(eof) {
789 /* Oops, that's all for now */
8d8b8c1f 790 D(("logging reader eof"));
397ef7bb 791 if(c->w) {
8d8b8c1f 792 D(("close writer"));
397ef7bb
RK
793 ev_writer_close(c->w);
794 c->w = 0;
795 }
796 c->r = 0;
f6033c46 797 }
397ef7bb 798 return 0;
460b9539 799}
800
801static void logclient(const char *msg, void *user) {
802 struct conn *c = user;
803
f6033c46
RK
804 if(!c->w || !c->r) {
805 /* This connection has gone up in smoke for some reason */
806 eventlog_remove(c->lo);
807 return;
808 }
460b9539 809 sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" %s\n",
810 (uintmax_t)time(0), msg);
811}
812
813static int c_log(struct conn *c,
814 char attribute((unused)) **vec,
815 int attribute((unused)) nvec) {
816 time_t now;
817
818 sink_writes(ev_writer_sink(c->w), "254 OK\n");
819 /* pump out initial state */
820 time(&now);
821 sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" state %s\n",
822 (uintmax_t)now,
823 playing_is_enabled() ? "enable_play" : "disable_play");
824 sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" state %s\n",
825 (uintmax_t)now,
826 random_is_enabled() ? "enable_random" : "disable_random");
827 sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" state %s\n",
828 (uintmax_t)now,
829 paused ? "pause" : "resume");
5abe307a
RK
830 if(playing)
831 sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" state playing\n",
832 (uintmax_t)now);
df44d69b
RK
833 /* Initial volume */
834 sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" volume %d %d\n",
835 (uintmax_t)now, volume_left, volume_right);
460b9539 836 c->lo = xmalloc(sizeof *c->lo);
837 c->lo->fn = logclient;
838 c->lo->user = c;
839 eventlog_add(c->lo);
840 c->reader = logging_reader_callback;
841 return 0;
842}
843
eb5dc014
RK
844/** @brief Test whether a move is allowed
845 * @param c Connection
846 * @param qs List of IDs on queue
847 * @param nqs Number of IDs
848 * @return 0 if move is prohibited, non-0 if it is allowed
849 */
850static int has_move_rights(struct conn *c, struct queue_entry **qs, int nqs) {
851 rights_type r = 0;
852
853 for(; nqs > 0; ++qs, --nqs) {
854 struct queue_entry *const q = *qs;
855
856 if(q->submitter)
857 if(!strcmp(q->submitter, c->who))
858 r |= RIGHT_MOVE_MINE;
859 else
860 r |= RIGHT_MOVE_ANY;
861 else
862 r |= RIGHT_MOVE_RANDOM;
863 }
864 return (c->rights & r) == r;
865}
866
460b9539 867static int c_move(struct conn *c,
868 char **vec,
869 int attribute((unused)) nvec) {
870 struct queue_entry *q;
871 int n;
872
460b9539 873 if(!(q = queue_find(vec[0]))) {
874 sink_writes(ev_writer_sink(c->w), "550 no such track on the queue\n");
875 return 1;
876 }
eb5dc014
RK
877 if(!has_move_rights(c, &q, 1)) {
878 sink_writes(ev_writer_sink(c->w),
879 "550 Not authorized to move that track\n");
880 return 1;
881 }
460b9539 882 n = queue_move(q, atoi(vec[1]), c->who);
460b9539 883 sink_printf(ev_writer_sink(c->w), "252 %d\n", n);
884 /* If we've moved to the head of the queue then prepare the track. */
885 if(q == qhead.next)
886 prepare(c->ev, q);
887 return 1;
888}
889
890static int c_moveafter(struct conn *c,
891 char **vec,
892 int attribute((unused)) nvec) {
893 struct queue_entry *q, **qs;
894 int n;
895
460b9539 896 if(vec[0][0]) {
897 if(!(q = queue_find(vec[0]))) {
898 sink_writes(ev_writer_sink(c->w), "550 no such track on the queue\n");
899 return 1;
900 }
901 } else
902 q = 0;
903 ++vec;
904 --nvec;
905 qs = xcalloc(nvec, sizeof *qs);
906 for(n = 0; n < nvec; ++n)
907 if(!(qs[n] = queue_find(vec[n]))) {
908 sink_writes(ev_writer_sink(c->w), "550 no such track on the queue\n");
909 return 1;
910 }
eb5dc014
RK
911 if(!has_move_rights(c, qs, nvec)) {
912 sink_writes(ev_writer_sink(c->w),
913 "550 Not authorized to move those tracks\n");
914 return 1;
915 }
460b9539 916 queue_moveafter(q, nvec, qs, c->who);
460b9539 917 sink_printf(ev_writer_sink(c->w), "250 Moved tracks\n");
918 /* If we've moved to the head of the queue then prepare the track. */
919 if(q == qhead.next)
920 prepare(c->ev, q);
921 return 1;
922}
923
924static int c_part(struct conn *c,
925 char **vec,
926 int attribute((unused)) nvec) {
927 sink_printf(ev_writer_sink(c->w), "252 %s\n",
7b32e917 928 quoteutf8(trackdb_getpart(vec[0], vec[1], vec[2])));
460b9539 929 return 1;
930}
931
932static int c_resolve(struct conn *c,
933 char **vec,
934 int attribute((unused)) nvec) {
935 const char *track;
936
937 if(!(track = trackdb_resolve(vec[0]))) {
938 sink_writes(ev_writer_sink(c->w), "550 cannot resolve track\n");
939 return 1;
940 }
7b32e917 941 sink_printf(ev_writer_sink(c->w), "252 %s\n", quoteutf8(track));
460b9539 942 return 1;
943}
944
945static int c_tags(struct conn *c,
946 char attribute((unused)) **vec,
947 int attribute((unused)) nvec) {
948 char **tags = trackdb_alltags();
949
950 sink_printf(ev_writer_sink(c->w), "253 Tag list follows\n");
951 while(*tags) {
952 sink_printf(ev_writer_sink(c->w), "%s%s\n",
953 **tags == '.' ? "." : "", *tags);
954 ++tags;
955 }
956 sink_writes(ev_writer_sink(c->w), ".\n");
957 return 1; /* completed */
460b9539 958}
959
960static int c_set_global(struct conn *c,
961 char **vec,
962 int attribute((unused)) nvec) {
f9635e06
RK
963 if(vec[0][0] == '_') {
964 sink_writes(ev_writer_sink(c->w), "550 cannot set internal global preferences\n");
965 return 1;
966 }
460b9539 967 trackdb_set_global(vec[0], vec[1], c->who);
968 sink_printf(ev_writer_sink(c->w), "250 OK\n");
969 return 1;
970}
971
972static int c_get_global(struct conn *c,
973 char **vec,
974 int attribute((unused)) nvec) {
975 const char *s = trackdb_get_global(vec[0]);
976
977 if(s)
7b32e917 978 sink_printf(ev_writer_sink(c->w), "252 %s\n", quoteutf8(s));
460b9539 979 else
fb1bc1f5 980 sink_writes(ev_writer_sink(c->w), "555 not found\n");
460b9539 981 return 1;
982}
983
7858930d 984static int c_nop(struct conn *c,
985 char attribute((unused)) **vec,
986 int attribute((unused)) nvec) {
987 sink_printf(ev_writer_sink(c->w), "250 Quack\n");
988 return 1;
989}
990
2a10b70b
RK
991static int c_new(struct conn *c,
992 char **vec,
993 int nvec) {
994 char **tracks = trackdb_new(0, nvec > 0 ? atoi(vec[0]) : INT_MAX);
995
996 sink_printf(ev_writer_sink(c->w), "253 New track list follows\n");
997 while(*tracks) {
998 sink_printf(ev_writer_sink(c->w), "%s%s\n",
999 **tracks == '.' ? "." : "", *tracks);
1000 ++tracks;
1001 }
1002 sink_writes(ev_writer_sink(c->w), ".\n");
1003 return 1; /* completed */
1004
1005}
1006
ca831831
RK
1007static int c_rtp_address(struct conn *c,
1008 char attribute((unused)) **vec,
1009 int attribute((unused)) nvec) {
1010 if(config->speaker_backend == BACKEND_NETWORK) {
1011 sink_printf(ev_writer_sink(c->w), "252 %s %s\n",
1012 quoteutf8(config->broadcast.s[0]),
1013 quoteutf8(config->broadcast.s[1]));
1014 } else
1015 sink_writes(ev_writer_sink(c->w), "550 No RTP\n");
1016 return 1;
1017}
b12be54a
RK
1018
1019static int c_cookie(struct conn *c,
1020 char **vec,
1021 int attribute((unused)) nvec) {
1022 const char *host;
1023 char *user;
eb5dc014 1024 rights_type rights;
b12be54a
RK
1025
1026 /* Can't log in twice on the same connection */
1027 if(c->who) {
1028 sink_writes(ev_writer_sink(c->w), "530 already authenticated\n");
1029 return 1;
1030 }
1031 /* Get some kind of peer identifcation */
1032 if(!(host = connection_host(c))) {
1033 sink_writes(ev_writer_sink(c->w), "530 authentication failure\n");
1034 return 1;
1035 }
1036 /* Check the cookie */
eb5dc014 1037 user = verify_cookie(vec[0], &rights);
b12be54a
RK
1038 if(!user) {
1039 sink_writes(ev_writer_sink(c->w), "530 authentication failure\n");
1040 return 1;
1041 }
1042 /* Log in */
eb5dc014 1043 c->who = vec[0];
b12be54a 1044 c->cookie = vec[0];
eb5dc014
RK
1045 c->rights = rights;
1046 if(strcmp(host, "local")) {
b12be54a 1047 info("S%x %s connected with cookie from %s", c->tag, user, host);
eb5dc014
RK
1048 c->rights |= RIGHT__LOCAL;
1049 }
b12be54a
RK
1050 sink_writes(ev_writer_sink(c->w), "230 OK\n");
1051 return 1;
1052}
1053
1054static int c_make_cookie(struct conn *c,
1055 char attribute((unused)) **vec,
1056 int attribute((unused)) nvec) {
1057 const char *cookie = make_cookie(c->who);
1058
1059 if(cookie)
eb5dc014 1060 sink_printf(ev_writer_sink(c->w), "252 %s\n", quoteutf8(cookie));
b12be54a
RK
1061 else
1062 sink_writes(ev_writer_sink(c->w), "550 Cannot create cookie\n");
1063 return 1;
1064}
1065
1066static int c_revoke(struct conn *c,
1067 char attribute((unused)) **vec,
1068 int attribute((unused)) nvec) {
1069 if(c->cookie) {
1070 revoke_cookie(c->cookie);
1071 sink_writes(ev_writer_sink(c->w), "250 OK\n");
1072 } else
1073 sink_writes(ev_writer_sink(c->w), "550 Did not log in with cookie\n");
1074 return 1;
1075}
1076
f0feb22e
RK
1077static int c_adduser(struct conn *c,
1078 char **vec,
1079 int attribute((unused)) nvec) {
04e1fa7c 1080 if(trackdb_adduser(vec[0], vec[1], config->default_rights, 0))
f0feb22e
RK
1081 sink_writes(ev_writer_sink(c->w), "550 Cannot create user\n");
1082 else
1083 sink_writes(ev_writer_sink(c->w), "250 User created\n");
1084 return 1;
1085}
1086
1087static int c_deluser(struct conn *c,
1088 char **vec,
1089 int attribute((unused)) nvec) {
f0feb22e
RK
1090 if(trackdb_deluser(vec[0]))
1091 sink_writes(ev_writer_sink(c->w), "550 Cannot deleted user\n");
1092 else
1093 sink_writes(ev_writer_sink(c->w), "250 User deleted\n");
1094 return 1;
1095}
1096
1097static int c_edituser(struct conn *c,
5df73aeb 1098 char **vec,
f0feb22e 1099 int attribute((unused)) nvec) {
eb5dc014
RK
1100 /* RIGHT_ADMIN can do anything; otherwise you can only set your own email
1101 * address and password. */
1102 if((c->rights & RIGHT_ADMIN)
5df73aeb
RK
1103 || (!strcmp(c->who, vec[0])
1104 && (!strcmp(vec[1], "email")
1105 || !strcmp(vec[1], "password")))) {
1106 if(trackdb_edituserinfo(vec[0], vec[1], vec[2]))
1107 sink_writes(ev_writer_sink(c->w), "550 Failed to change setting\n");
1108 else
1109 sink_writes(ev_writer_sink(c->w), "250 OK\n");
1110 } else
1111 sink_writes(ev_writer_sink(c->w), "550 Restricted to administrators\n");
f0feb22e
RK
1112 return 1;
1113}
1114
1115static int c_userinfo(struct conn *c,
1116 char attribute((unused)) **vec,
1117 int attribute((unused)) nvec) {
5df73aeb
RK
1118 struct kvp *k;
1119 const char *value;
eb5dc014
RK
1120
1121 /* RIGHT_ADMIN allows anything; otherwise you can only get your own email
1122 * address and righst list. */
1123 if((c->rights & RIGHT_ADMIN)
5df73aeb
RK
1124 || (!strcmp(c->who, vec[0])
1125 && (!strcmp(vec[1], "email")
1126 || !strcmp(vec[1], "rights")))) {
1127 if((k = trackdb_getuserinfo(vec[0])))
1128 if((value = kvp_get(k, vec[1])))
1129 sink_printf(ev_writer_sink(c->w), "252 %s\n", quoteutf8(value));
1130 else
1131 sink_writes(ev_writer_sink(c->w), "555 Not set\n");
1132 else
1133 sink_writes(ev_writer_sink(c->w), "550 No such user\n");
1134 } else
1135 sink_writes(ev_writer_sink(c->w), "550 Restricted to administrators\n");
f0feb22e
RK
1136 return 1;
1137}
1138
c3be4f19
RK
1139static int c_users(struct conn *c,
1140 char attribute((unused)) **vec,
1141 int attribute((unused)) nvec) {
1142 /* TODO de-dupe with c_tags */
1143 char **users = trackdb_listusers();
1144
1145 sink_writes(ev_writer_sink(c->w), "253 User list follows\n");
1146 while(*users) {
1147 sink_printf(ev_writer_sink(c->w), "%s%s\n",
1148 **users == '.' ? "." : "", *users);
1149 ++users;
1150 }
1151 sink_writes(ev_writer_sink(c->w), ".\n");
1152 return 1; /* completed */
1153}
1154
460b9539 1155static const struct command {
eb5dc014 1156 /** @brief Command name */
460b9539 1157 const char *name;
eb5dc014
RK
1158
1159 /** @brief Minimum number of arguments */
1160 int minargs;
1161
1162 /** @brief Maximum number of arguments */
1163 int maxargs;
1164
1165 /** @brief Function to process command */
460b9539 1166 int (*fn)(struct conn *, char **, int);
eb5dc014
RK
1167
1168 /** @brief Rights required to execute command
1169 *
1170 * 0 means that the command can be issued without logging in. If multiple
1171 * bits are listed here any of those rights will do.
1172 */
1173 rights_type rights;
460b9539 1174} commands[] = {
eb5dc014
RK
1175 { "adduser", 2, 2, c_adduser, RIGHT_ADMIN|RIGHT__LOCAL },
1176 { "allfiles", 0, 2, c_allfiles, RIGHT_READ },
b12be54a 1177 { "cookie", 1, 1, c_cookie, 0 },
eb5dc014
RK
1178 { "deluser", 1, 1, c_deluser, RIGHT_ADMIN|RIGHT__LOCAL },
1179 { "dirs", 0, 2, c_dirs, RIGHT_READ },
1180 { "disable", 0, 1, c_disable, RIGHT_GLOBAL_PREFS },
1181 { "edituser", 3, 3, c_edituser, RIGHT_ADMIN|RIGHT_USERINFO },
1182 { "enable", 0, 0, c_enable, RIGHT_GLOBAL_PREFS },
1183 { "enabled", 0, 0, c_enabled, RIGHT_READ },
1184 { "exists", 1, 1, c_exists, RIGHT_READ },
1185 { "files", 0, 2, c_files, RIGHT_READ },
1186 { "get", 2, 2, c_get, RIGHT_READ },
1187 { "get-global", 1, 1, c_get_global, RIGHT_READ },
1188 { "length", 1, 1, c_length, RIGHT_READ },
1189 { "log", 0, 0, c_log, RIGHT_READ },
1190 { "make-cookie", 0, 0, c_make_cookie, RIGHT_READ },
1191 { "move", 2, 2, c_move, RIGHT_MOVE__MASK },
1192 { "moveafter", 1, INT_MAX, c_moveafter, RIGHT_MOVE__MASK },
1193 { "new", 0, 1, c_new, RIGHT_READ },
1194 { "nop", 0, 0, c_nop, 0 },
1195 { "part", 3, 3, c_part, RIGHT_READ },
1196 { "pause", 0, 0, c_pause, RIGHT_PAUSE },
1197 { "play", 1, 1, c_play, RIGHT_PLAY },
1198 { "playing", 0, 0, c_playing, RIGHT_READ },
1199 { "prefs", 1, 1, c_prefs, RIGHT_READ },
1200 { "queue", 0, 0, c_queue, RIGHT_READ },
1201 { "random-disable", 0, 0, c_random_disable, RIGHT_GLOBAL_PREFS },
1202 { "random-enable", 0, 0, c_random_enable, RIGHT_GLOBAL_PREFS },
1203 { "random-enabled", 0, 0, c_random_enabled, RIGHT_READ },
1204 { "recent", 0, 0, c_recent, RIGHT_READ },
1205 { "reconfigure", 0, 0, c_reconfigure, RIGHT_ADMIN },
1206 { "remove", 1, 1, c_remove, RIGHT_REMOVE__MASK },
1207 { "rescan", 0, 0, c_rescan, RIGHT_RESCAN },
1208 { "resolve", 1, 1, c_resolve, RIGHT_READ },
1209 { "resume", 0, 0, c_resume, RIGHT_PAUSE },
1210 { "revoke", 0, 0, c_revoke, RIGHT_READ },
1211 { "rtp-address", 0, 0, c_rtp_address, 0 },
1212 { "scratch", 0, 1, c_scratch, RIGHT_SCRATCH__MASK },
1213 { "search", 1, 1, c_search, RIGHT_READ },
1214 { "set", 3, 3, c_set, RIGHT_PREFS, },
1215 { "set-global", 2, 2, c_set_global, RIGHT_GLOBAL_PREFS },
1216 { "shutdown", 0, 0, c_shutdown, RIGHT_ADMIN },
1217 { "stats", 0, 0, c_stats, RIGHT_READ },
1218 { "tags", 0, 0, c_tags, RIGHT_READ },
1219 { "unset", 2, 2, c_set, RIGHT_PREFS },
1220 { "unset-global", 1, 1, c_set_global, RIGHT_GLOBAL_PREFS },
460b9539 1221 { "user", 2, 2, c_user, 0 },
eb5dc014
RK
1222 { "userinfo", 2, 2, c_userinfo, RIGHT_READ },
1223 { "users", 0, 0, c_users, RIGHT_READ },
1224 { "version", 0, 0, c_version, RIGHT_READ },
1225 { "volume", 0, 2, c_volume, RIGHT_READ|RIGHT_VOLUME }
460b9539 1226};
1227
1228static void command_error(const char *msg, void *u) {
1229 struct conn *c = u;
1230
1231 sink_printf(ev_writer_sink(c->w), "500 parse error: %s\n", msg);
1232}
1233
1234/* process a command. Return 1 if complete, 0 if incomplete. */
1235static int command(struct conn *c, char *line) {
1236 char **vec;
1237 int nvec, n;
1238
1239 D(("server command %s", line));
f9635e06
RK
1240 /* We force everything into NFC as early as possible */
1241 if(!(line = utf8_compose_canon(line, strlen(line), 0))) {
1242 sink_writes(ev_writer_sink(c->w), "500 cannot normalize command\n");
1243 return 1;
1244 }
460b9539 1245 if(!(vec = split(line, &nvec, SPLIT_QUOTES, command_error, c))) {
1246 sink_writes(ev_writer_sink(c->w), "500 cannot parse command\n");
1247 return 1;
1248 }
1249 if(nvec == 0) {
1250 sink_writes(ev_writer_sink(c->w), "500 do what?\n");
1251 return 1;
1252 }
1253 if((n = TABLE_FIND(commands, struct command, name, vec[0])) < 0)
1254 sink_writes(ev_writer_sink(c->w), "500 unknown command\n");
1255 else {
eb5dc014
RK
1256 if(commands[n].rights
1257 && !(c->rights & commands[n].rights)) {
1258 sink_writes(ev_writer_sink(c->w), "530 Prohibited\n");
460b9539 1259 return 1;
1260 }
1261 ++vec;
1262 --nvec;
1263 if(nvec < commands[n].minargs) {
1264 sink_writes(ev_writer_sink(c->w), "500 missing argument(s)\n");
1265 return 1;
1266 }
1267 if(nvec > commands[n].maxargs) {
1268 sink_writes(ev_writer_sink(c->w), "500 too many arguments\n");
1269 return 1;
1270 }
1271 return commands[n].fn(c, vec, nvec);
1272 }
1273 return 1; /* completed */
1274}
1275
1276/* redirect to the right reader callback for our current state */
1277static int redirect_reader_callback(ev_source *ev,
1278 ev_reader *reader,
460b9539 1279 void *ptr,
1280 size_t bytes,
1281 int eof,
1282 void *u) {
1283 struct conn *c = u;
1284
75d64210 1285 return c->reader(ev, reader, ptr, bytes, eof, u);
460b9539 1286}
1287
1288/* the main command reader */
1289static int reader_callback(ev_source attribute((unused)) *ev,
1290 ev_reader *reader,
460b9539 1291 void *ptr,
1292 size_t bytes,
1293 int eof,
1294 void *u) {
1295 struct conn *c = u;
1296 char *eol;
1297 int complete;
1298
1299 D(("server reader_callback"));
1300 while((eol = memchr(ptr, '\n', bytes))) {
1301 *eol++ = 0;
1302 ev_reader_consume(reader, eol - (char *)ptr);
1303 complete = command(c, ptr);
1304 bytes -= (eol - (char *)ptr);
1305 ptr = eol;
1306 if(!complete) {
1307 /* the command had better have set a new reader callback */
1308 if(bytes || eof)
1309 /* there are further bytes to read, or we are at eof; arrange for the
1310 * command's reader callback to handle them */
1311 return ev_reader_incomplete(reader);
1312 /* nothing's going on right now */
1313 return 0;
1314 }
1315 /* command completed, we can go around and handle the next one */
1316 }
1317 if(eof) {
1318 if(bytes)
1319 error(0, "S%x unterminated line", c->tag);
8d8b8c1f 1320 D(("normal reader close"));
397ef7bb
RK
1321 c->r = 0;
1322 if(c->w) {
8d8b8c1f 1323 D(("close associated writer"));
397ef7bb
RK
1324 ev_writer_close(c->w);
1325 c->w = 0;
1326 }
460b9539 1327 }
1328 return 0;
1329}
1330
1331static int listen_callback(ev_source *ev,
1332 int fd,
1333 const struct sockaddr attribute((unused)) *remote,
1334 socklen_t attribute((unused)) rlen,
1335 void *u) {
1336 const struct listener *l = u;
1337 struct conn *c = xmalloc(sizeof *c);
1338 static unsigned tags;
1339
1340 D(("server listen_callback fd %d (%s)", fd, l->name));
1341 nonblock(fd);
1342 cloexec(fd);
1343 c->tag = tags++;
1344 c->ev = ev;
e8c92ba7
RK
1345 c->w = ev_writer_new(ev, fd, writer_error, c,
1346 "client writer");
1347 c->r = ev_reader_new(ev, fd, redirect_reader_callback, reader_error, c,
1348 "client reader");
75d64210 1349 ev_tie(c->r, c->w);
460b9539 1350 c->fd = fd;
1351 c->reader = reader_callback;
1352 c->l = l;
eb5dc014 1353 c->rights = 0;
460b9539 1354 gcry_randomize(c->nonce, sizeof c->nonce, GCRY_STRONG_RANDOM);
7b32e917
RK
1355 sink_printf(ev_writer_sink(c->w), "231 %d %s %s\n",
1356 2,
b3141726
RK
1357 config->authorization_algorithm,
1358 hex(c->nonce, sizeof c->nonce));
460b9539 1359 return 0;
1360}
1361
1362int server_start(ev_source *ev, int pf,
1363 size_t socklen, const struct sockaddr *sa,
1364 const char *name) {
1365 int fd;
1366 struct listener *l = xmalloc(sizeof *l);
1367 static const int one = 1;
1368
1369 D(("server_init socket %s", name));
1370 fd = xsocket(pf, SOCK_STREAM, 0);
1371 xsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one);
1372 if(bind(fd, sa, socklen) < 0) {
1373 error(errno, "error binding to %s", name);
1374 return -1;
1375 }
1376 xlisten(fd, 128);
1377 nonblock(fd);
1378 cloexec(fd);
1379 l->name = name;
1380 l->pf = pf;
e8c92ba7
RK
1381 if(ev_listen(ev, fd, listen_callback, l, "server listener"))
1382 exit(EXIT_FAILURE);
460b9539 1383 return fd;
1384}
1385
1386int server_stop(ev_source *ev, int fd) {
1387 xclose(fd);
1388 return ev_listen_cancel(ev, fd);
1389}
1390
1391/*
1392Local Variables:
1393c-basic-offset:2
1394comment-column:40
1395fill-column:79
1396End:
1397*/