* 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 <http://www.gnu.org/licenses/>.
*/
-#include <config.h>
-#include "types.h"
-
-#include <pwd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <gcrypt.h>
-#include <stddef.h>
-#include <time.h>
-#include <limits.h>
-#include <pcre.h>
-#include <netdb.h>
-#include <netinet/in.h>
-
-#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
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
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");
}
/* 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");
}
} 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;
}
} 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;
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);
}
/* 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 {
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;
char **vec,
int nvec) {
struct kvp *actiondata = 0;
- char *id;
+ const char *id;
/* Standard fields */
kvp_set(&actiondata, "who", c->who);
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;
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 },
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
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,
"client writer");
+ if(!c->w) {
+ error(0, "ev_writer_new for file inbound connection (fd=%d) failed",
+ fd);
+ close(fd);
+ return 0;
+ }
c->r = ev_reader_new(ev, fd, redirect_reader_callback, reader_error, c,
"client reader");
+ if(!c->r)
+ /* Main reason for failure is the FD is too big and that will already have
+ * been handled */
+ fatal(0, "ev_reader_new for file inbound connection (fd=%d) failed", fd);
ev_tie(c->r, c->w);
c->fd = fd;
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,