X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/2b33c4cf27ec6f4bd73c16ed1395eab209dec51f..d42e98caaaf4f07c8d1252236f03eb68b8be4619:/server/server.c diff --git a/server/server.c b/server/server.c index e8e6a68..18f6851 100644 --- a/server/server.c +++ b/server/server.c @@ -2,76 +2,21 @@ * This file is part of DisOrder. * Copyright (C) 2004-2008 Richard Kettlewell * - * This program is free software; you can redistribute it and/or modify + * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 - * USA + * along with this program. If not, see . */ -#include -#include "types.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "event.h" -#include "server.h" -#include "syscalls.h" -#include "queue.h" -#include "server-queue.h" -#include "play.h" -#include "log.h" -#include "mem.h" -#include "state.h" -#include "charset.h" -#include "split.h" -#include "configuration.h" -#include "hex.h" -#include "rights.h" -#include "trackdb.h" -#include "table.h" -#include "kvp.h" -#include "mixer.h" -#include "sink.h" -#include "authhash.h" -#include "plugin.h" -#include "printf.h" -#include "trackname.h" -#include "eventlog.h" -#include "defs.h" -#include "cache.h" -#include "unicode.h" -#include "cookies.h" -#include "base64.h" -#include "hash.h" -#include "mime.h" -#include "sendmail.h" -#include "wstat.h" -#include "schedule.h" +#include "disorder-server.h" #ifndef NONCE_SIZE # define NONCE_SIZE 16 @@ -243,7 +188,7 @@ static int c_play(struct conn *c, char **vec, sink_writes(ev_writer_sink(c->w), "550 cannot resolve track\n"); return 1; } - q = queue_add(track, c->who, WHERE_BEFORE_RANDOM); + q = queue_add(track, c->who, WHERE_BEFORE_RANDOM, origin_picked); queue_write(); /* If we added the first track, and something is playing, then prepare the * new track. If nothing is playing then we don't bother as it wouldn't gain @@ -401,16 +346,16 @@ static void start_fresh_rescan(void *ru) { static int c_rescan(struct conn *c, char **vec, int nvec) { - int wait = 0, fresh = 0, n; + int flag_wait = 0, flag_fresh = 0, n; /* Parse flags */ for(n = 0; n < nvec; ++n) { if(!strcmp(vec[n], "wait")) - wait = 1; /* wait for rescan to complete */ + flag_wait = 1; /* wait for rescan to complete */ #if 0 /* Currently disabled because untested (and hard to test). */ else if(!strcmp(vec[n], "fresh")) - fresh = 1; /* don't piggyback underway rescan */ + flag_fresh = 1; /* don't piggyback underway rescan */ #endif else { sink_writes(ev_writer_sink(c->w), "550 unknown flag\n"); @@ -419,15 +364,15 @@ static int c_rescan(struct conn *c, } /* Report what was requested */ info("S%x rescan by %s (%s %s)", c->tag, c->who, - wait ? "wait" : "", - fresh ? "fresh" : ""); + flag_wait ? "wait" : "", + flag_fresh ? "fresh" : ""); if(trackdb_rescan_underway()) { - if(fresh) { + if(flag_fresh) { /* We want a fresh rescan but there is already one underway. Arrange a * callback when it completes and then set off a new one. */ - c->rescan_wait = wait; + c->rescan_wait = flag_wait; trackdb_add_rescanned(start_fresh_rescan, c); - if(wait) + if(flag_wait) return 0; else { sink_writes(ev_writer_sink(c->w), "250 rescan queued\n"); @@ -435,7 +380,7 @@ static int c_rescan(struct conn *c, } } else { /* There's a rescan underway, and it's acceptable to piggyback on it */ - if(wait) { + if(flag_wait) { /* We want to block until completion. */ trackdb_add_rescanned(finished_rescan, c); return 0; @@ -448,7 +393,7 @@ static int c_rescan(struct conn *c, } } else { /* No rescan is underway. fresh is therefore irrelevant. */ - if(wait) { + if(flag_wait) { /* We want to block until completion */ trackdb_rescan(c->ev, 1/*check*/, finished_rescan, c); return 0; @@ -929,8 +874,19 @@ static void logclient(const char *msg, void *user) { if(!c->w || !c->r) { /* This connection has gone up in smoke for some reason */ eventlog_remove(c->lo); + c->lo = 0; return; } + /* user_* messages are restricted */ + if(!strncmp(msg, "user_", 5)) { + /* They are only sent to admin users */ + if(!(c->rights & RIGHT_ADMIN)) + return; + /* They are not sent over TCP connections unless remote user-management is + * enabled */ + if(!config->remote_userman && !(c->rights & RIGHT__LOCAL)) + return; + } sink_printf(ev_writer_sink(c->w), "%"PRIxMAX" %s\n", (uintmax_t)time(0), msg); } @@ -1287,10 +1243,21 @@ static int c_edituser(struct conn *c, /* Update rights for this user */ rights_type r; - if(parse_rights(vec[2], &r, 1)) - for(d = connections; d; d = d->next) - if(!strcmp(d->who, vec[0])) + if(!parse_rights(vec[2], &r, 1)) { + const char *new_rights = rights_string(r); + for(d = connections; d; d = d->next) { + if(!strcmp(d->who, vec[0])) { + /* Update rights */ d->rights = r; + /* Notify any log connections */ + if(d->lo) + sink_printf(ev_writer_sink(d->w), + "%"PRIxMAX" rights_changed %s\n", + (uintmax_t)time(0), + quoteutf8(new_rights)); + } + } + } } sink_writes(ev_writer_sink(c->w), "250 OK\n"); } else { @@ -1458,7 +1425,7 @@ static int c_reminder(struct conn *c, return 1; } if(!(email = kvp_get(k, "email")) - || !strchr(email, '@')) { + || !email_valid(email)) { error(0, "user '%s' has no valid email address", vec[0]); sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n"); return 1; @@ -1567,7 +1534,7 @@ static int c_schedule_add(struct conn *c, char **vec, int nvec) { struct kvp *actiondata = 0; - char *id; + const char *id; /* Standard fields */ kvp_set(&actiondata, "who", c->who); @@ -1606,6 +1573,31 @@ static int c_schedule_add(struct conn *c, return 1; } +static int c_adopt(struct conn *c, + char **vec, + int attribute((unused)) nvec) { + struct queue_entry *q; + + if(!c->who) { + sink_writes(ev_writer_sink(c->w), "550 no identity\n"); + return 1; + } + if(!(q = queue_find(vec[0]))) { + sink_writes(ev_writer_sink(c->w), "550 no such track on the queue\n"); + return 1; + } + if(q->origin != origin_random) { + sink_writes(ev_writer_sink(c->w), "550 not a random track\n"); + return 1; + } + q->origin = origin_adopted; + q->submitter = xstrdup(c->who); + eventlog("adopted", q->id, q->submitter, (char *)0); + queue_write(); + sink_writes(ev_writer_sink(c->w), "250 OK\n"); + return 1; +} + static const struct command { /** @brief Command name */ const char *name; @@ -1627,6 +1619,7 @@ static const struct command { rights_type rights; } commands[] = { { "adduser", 2, 3, c_adduser, RIGHT_ADMIN|RIGHT__LOCAL }, + { "adopt", 1, 1, c_adopt, RIGHT_PLAY }, { "allfiles", 0, 2, c_allfiles, RIGHT_READ }, { "confirm", 1, 1, c_confirm, 0 }, { "cookie", 1, 1, c_cookie, 0 }, @@ -1711,7 +1704,7 @@ static int command(struct conn *c, char *line) { sink_writes(ev_writer_sink(c->w), "500 do what?\n"); return 1; } - if((n = TABLE_FIND(commands, struct command, name, vec[0])) < 0) + if((n = TABLE_FIND(commands, name, vec[0])) < 0) sink_writes(ev_writer_sink(c->w), "500 unknown command\n"); else { if(commands[n].rights @@ -1804,6 +1797,7 @@ static int listen_callback(ev_source *ev, D(("server listen_callback fd %d (%s)", fd, l->name)); nonblock(fd); cloexec(fd); + c->next = connections; c->tag = tags++; c->ev = ev; c->w = ev_writer_new(ev, fd, writer_error, c, @@ -1815,6 +1809,7 @@ static int listen_callback(ev_source *ev, c->reader = reader_callback; c->l = l; c->rights = 0; + connections = c; gcry_randomize(c->nonce, sizeof c->nonce, GCRY_STRONG_RANDOM); sink_printf(ev_writer_sink(c->w), "231 %d %s %s\n", 2,