chiark / gitweb /
disorder setup-guest + docs + tests
authorRichard Kettlewell <rjk@greenend.org.uk>
Sat, 22 Dec 2007 11:37:45 +0000 (11:37 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sat, 22 Dec 2007 11:37:45 +0000 (11:37 +0000)
adduser/authorize now take an optional rights argument, removing the
race where you wanted a new user not to have some of the default
rights.

The command line client now delays creating a connection until it
needs it, allowing per-command help, syntax checking etc to succeed
even in the absence of a working server (mostly useful during
development).

17 files changed:
clients/authorize.c
clients/authorize.h
clients/disorder.c
doc/disorder.1.in
doc/disorder_protocol.5.in
lib/client.c
lib/client.h
lib/configuration.c
lib/configuration.h
lib/cookies.c
lib/rights.c
lib/rights.h
lib/trackdb.c
lib/trackdb.h
scripts/completion.bash
server/server.c
tests/user.py

index bb6d366..c442d23 100644 (file)
 /** @brief Create a DisOrder login for the calling user, called @p user
  * @param client DisOrder client
  * @param user Username to create (UTF-8)
+ * @param rights Initial rights or NULL for default
  * @return 0 on success, non-0 on error
  */
-int authorize(disorder_client *client, const char *user) {
+int authorize(disorder_client *client, const char *user, const char *rights) {
   uint8_t pwbin[10];
   const struct passwd *pw, *jbpw;
   gid_t jbgid;
@@ -82,7 +83,7 @@ int authorize(disorder_client *client, const char *user) {
     fatal(errno, "error renaming %s to %s", t, c);
 
   /* create the user on the server */
-  if(disorder_adduser(client, user, pwhex))
+  if(disorder_adduser(client, user, pwhex, rights))
     return -1;
 
   return 0;
index 07e87ef..5734fa5 100644 (file)
@@ -20,7 +20,7 @@
 #ifndef AUTHORIZE_H
 #define AUTHORIZE_H
 
-int authorize(disorder_client *client, const char *user);
+int authorize(disorder_client *client, const char *user, const char *rights);
 
 #endif /* AUTHORIZE_H */
 
index 5b43b09..3413791 100644 (file)
@@ -54,7 +54,7 @@
 #include "authorize.h"
 #include "vector.h"
 
-static int auto_reconfigure;
+static disorder_client *client;
 
 static const struct option options[] = {
   { "help", no_argument, 0, 'h' },
@@ -91,11 +91,18 @@ static void version(void) {
   exit(0);
 }
 
-static void cf_version(disorder_client *c,
-                      char attribute((unused)) **argv) {
+static disorder_client *getclient(void) {
+  if(!client) {
+    if(!(client = disorder_new(1))) exit(EXIT_FAILURE);
+    if(disorder_connect(client)) exit(EXIT_FAILURE);
+  }
+  return client;
+}
+
+static void cf_version(char attribute((unused)) **argv) {
   char *v;
 
-  if(disorder_version(c, &v)) exit(EXIT_FAILURE);
+  if(disorder_version(getclient(), &v)) exit(EXIT_FAILURE);
   xprintf("%s\n", nullcheck(utf82mb(v)));
 }
 
@@ -114,78 +121,71 @@ static void print_queue_entry(const struct queue_entry *q) {
   if(q->wstat) xprintf("  %s\n", wstat(q->wstat));
 }
 
-static void cf_playing(disorder_client *c,
-                      char attribute((unused)) **argv) {
+static void cf_playing(char attribute((unused)) **argv) {
   struct queue_entry *q;
 
-  if(disorder_playing(c, &q)) exit(EXIT_FAILURE);
+  if(disorder_playing(getclient(), &q)) exit(EXIT_FAILURE);
   if(q)
     print_queue_entry(q);
   else
     xprintf("nothing\n");
 }
 
-static void cf_play(disorder_client *c, char **argv) {
+static void cf_play(char **argv) {
   while(*argv)
-    if(disorder_play(c, *argv++)) exit(EXIT_FAILURE);
+    if(disorder_play(getclient(), *argv++)) exit(EXIT_FAILURE);
 }
 
-static void cf_remove(disorder_client *c, char **argv) {
-  if(disorder_remove(c, argv[0])) exit(EXIT_FAILURE);
+static void cf_remove(char **argv) {
+  if(disorder_remove(getclient(), argv[0])) exit(EXIT_FAILURE);
 }
 
-static void cf_disable(disorder_client *c,
-                      char attribute((unused)) **argv) {
-  if(disorder_disable(c)) exit(EXIT_FAILURE);
+static void cf_disable(char attribute((unused)) **argv) {
+  if(disorder_disable(getclient())) exit(EXIT_FAILURE);
 }
 
-static void cf_enable(disorder_client *c, char attribute((unused)) **argv) {
-  if(disorder_enable(c)) exit(EXIT_FAILURE);
+static void cf_enable(char attribute((unused)) **argv) {
+  if(disorder_enable(getclient())) exit(EXIT_FAILURE);
 }
 
-static void cf_scratch(disorder_client *c,
-                      char **argv) {
-  if(disorder_scratch(c, argv[0])) exit(EXIT_FAILURE);
+static void cf_scratch(char **argv) {
+  if(disorder_scratch(getclient(), argv[0])) exit(EXIT_FAILURE);
 }
 
-static void cf_shutdown(disorder_client *c,
-                       char attribute((unused)) **argv) {
-  if(disorder_shutdown(c)) exit(EXIT_FAILURE);
+static void cf_shutdown(char attribute((unused)) **argv) {
+  if(disorder_shutdown(getclient())) exit(EXIT_FAILURE);
 }
 
-static void cf_reconfigure(disorder_client *c,
-                          char attribute((unused)) **argv) {
+static void cf_reconfigure(char attribute((unused)) **argv) {
   /* Re-check configuration for server */
   if(config_read(1)) fatal(0, "cannot read configuration");
-  if(disorder_reconfigure(c)) exit(EXIT_FAILURE);
+  if(disorder_reconfigure(getclient())) exit(EXIT_FAILURE);
 }
 
-static void cf_rescan(disorder_client *c, char attribute((unused)) **argv) {
-  if(disorder_rescan(c)) exit(EXIT_FAILURE);
+static void cf_rescan(char attribute((unused)) **argv) {
+  if(disorder_rescan(getclient())) exit(EXIT_FAILURE);
 }
 
-static void cf_somequeue(disorder_client *c,
-                        int (*fn)(disorder_client *c,
+static void cf_somequeue(int (*fn)(disorder_client *c,
                                   struct queue_entry **qp)) {
   struct queue_entry *q;
 
-  if(fn(c, &q)) exit(EXIT_FAILURE);
+  if(fn(getclient(), &q)) exit(EXIT_FAILURE);
   while(q) {
     print_queue_entry(q);
     q = q->next;
   }
 }
 
-static void cf_recent(disorder_client *c, char attribute((unused)) **argv) {
-  cf_somequeue(c, disorder_recent);
+static void cf_recent(char attribute((unused)) **argv) {
+  cf_somequeue(disorder_recent);
 }
 
-static void cf_queue(disorder_client *c, char attribute((unused)) **argv) {
-  cf_somequeue(c, disorder_queue);
+static void cf_queue(char attribute((unused)) **argv) {
+  cf_somequeue(disorder_queue);
 }
 
-static void cf_quack(disorder_client attribute((unused)) *c,
-                    char attribute((unused)) **argv) {
+static void cf_quack(char attribute((unused)) **argv) {
   xprintf("\n"
          " .------------------.\n"
          " | Naath is a babe! |\n"
@@ -197,7 +197,7 @@ static void cf_quack(disorder_client attribute((unused)) *c,
          "\n");
 }
 
-static void cf_somelist(disorder_client *c, char **argv,
+static void cf_somelist(char **argv,
                        int (*fn)(disorder_client *c,
                                  const char *arg, const char *re,
                                  char ***vecp, int *nvecp)) {
@@ -208,7 +208,7 @@ static void cf_somelist(disorder_client *c, char **argv,
     re = xstrdup(argv[1] + 1);
   else
     re = 0;
-  if(fn(c, argv[0], re, &vec, 0)) exit(EXIT_FAILURE);
+  if(fn(getclient(), argv[0], re, &vec, 0)) exit(EXIT_FAILURE);
   while(*vec)
     xprintf("%s\n", nullcheck(utf82mb(*vec++)));
 }
@@ -217,98 +217,90 @@ static int isarg_regexp(const char *s) {
   return s[0] == '~';
 }
 
-static void cf_dirs(disorder_client *c,
-                   char **argv) {
-  cf_somelist(c, argv, disorder_directories);
+static void cf_dirs(char **argv) {
+  cf_somelist(argv, disorder_directories);
 }
 
-static void cf_files(disorder_client *c, char **argv) {
-  cf_somelist(c, argv, disorder_files);
+static void cf_files(char **argv) {
+  cf_somelist(argv, disorder_files);
 }
 
-static void cf_allfiles(disorder_client *c, char **argv) {
-  cf_somelist(c, argv, disorder_allfiles);
+static void cf_allfiles(char **argv) {
+  cf_somelist(argv, disorder_allfiles);
 }
 
-static void cf_get(disorder_client *c, char **argv) {
+static void cf_get(char **argv) {
   char *value;
 
-  if(disorder_get(c, argv[0], argv[1], &value)) exit(EXIT_FAILURE);
+  if(disorder_get(getclient(), argv[0], argv[1], &value)) exit(EXIT_FAILURE);
   xprintf("%s\n", nullcheck(utf82mb(value)));
 }
 
-static void cf_length(disorder_client *c, char **argv) {
+static void cf_length(char **argv) {
   long length;
 
-  if(disorder_length(c, argv[0], &length)) exit(EXIT_FAILURE);
+  if(disorder_length(getclient(), argv[0], &length)) exit(EXIT_FAILURE);
   xprintf("%ld\n", length);
 }
 
-static void cf_set(disorder_client *c, char **argv) {
-  if(disorder_set(c, argv[0], argv[1], argv[2])) exit(EXIT_FAILURE);
+static void cf_set(char **argv) {
+  if(disorder_set(getclient(), argv[0], argv[1], argv[2])) exit(EXIT_FAILURE);
 }
 
-static void cf_unset(disorder_client *c, char **argv) {
-  if(disorder_unset(c, argv[0], argv[1])) exit(EXIT_FAILURE);
+static void cf_unset(char **argv) {
+  if(disorder_unset(getclient(), argv[0], argv[1])) exit(EXIT_FAILURE);
 }
 
-static void cf_prefs(disorder_client *c, char **argv) {
+static void cf_prefs(char **argv) {
   struct kvp *k;
 
-  if(disorder_prefs(c, argv[0], &k)) exit(EXIT_FAILURE);
+  if(disorder_prefs(getclient(), argv[0], &k)) exit(EXIT_FAILURE);
   for(; k; k = k->next)
     xprintf("%s = %s\n",
            nullcheck(utf82mb(k->name)), nullcheck(utf82mb(k->value)));
 }
 
-static void cf_search(disorder_client *c, char **argv) {
+static void cf_search(char **argv) {
   char **results;
   int nresults, n;
 
-  if(disorder_search(c, *argv, &results, &nresults)) exit(EXIT_FAILURE);
+  if(disorder_search(getclient(), *argv, &results, &nresults)) exit(EXIT_FAILURE);
   for(n = 0; n < nresults; ++n)
     xprintf("%s\n", nullcheck(utf82mb(results[n])));
 }
 
-static void cf_random_disable(disorder_client *c,
-                             char attribute((unused)) **argv) {
-  if(disorder_random_disable(c)) exit(EXIT_FAILURE);
+static void cf_random_disable(char attribute((unused)) **argv) {
+  if(disorder_random_disable(getclient())) exit(EXIT_FAILURE);
 }
 
-static void cf_random_enable(disorder_client *c,
-                            char attribute((unused)) **argv) {
-  if(disorder_random_enable(c)) exit(EXIT_FAILURE);
+static void cf_random_enable(char attribute((unused)) **argv) {
+  if(disorder_random_enable(getclient())) exit(EXIT_FAILURE);
 }
 
-static void cf_stats(disorder_client *c,
-                    char attribute((unused)) **argv) {
+static void cf_stats(char attribute((unused)) **argv) {
   char **vec;
 
-  if(disorder_stats(c, &vec, 0)) exit(EXIT_FAILURE);
+  if(disorder_stats(getclient(), &vec, 0)) exit(EXIT_FAILURE);
   while(*vec)
       xprintf("%s\n", nullcheck(utf82mb(*vec++)));
 }
 
-static void cf_get_volume(disorder_client *c,
-                         char attribute((unused)) **argv) {
+static void cf_get_volume(char attribute((unused)) **argv) {
   int l, r;
 
-  if(disorder_get_volume(c, &l, &r)) exit(EXIT_FAILURE);
+  if(disorder_get_volume(getclient(), &l, &r)) exit(EXIT_FAILURE);
   xprintf("%d %d\n", l, r);
 }
 
-static void cf_set_volume(disorder_client *c,
-                         char **argv) {
-  if(disorder_set_volume(c, atoi(argv[0]), atoi(argv[1]))) exit(EXIT_FAILURE);
+static void cf_set_volume(char **argv) {
+  if(disorder_set_volume(getclient(), atoi(argv[0]), atoi(argv[1]))) exit(EXIT_FAILURE);
 }
 
-static void cf_log(disorder_client *c,
-                  char attribute((unused)) **argv) {
-  if(disorder_log(c, sink_stdio("stdout", stdout))) exit(EXIT_FAILURE);
+static void cf_log(char attribute((unused)) **argv) {
+  if(disorder_log(getclient(), sink_stdio("stdout", stdout))) exit(EXIT_FAILURE);
 }
 
-static void cf_move(disorder_client *c,
-                  char **argv) {
+static void cf_move(char **argv) {
   long n;
   int e;
   
@@ -316,14 +308,13 @@ static void cf_move(disorder_client *c,
     fatal(e, "cannot convert '%s'", argv[1]);
   if(n > INT_MAX || n < INT_MIN)
     fatal(e, "%ld out of range", n);
-  if(disorder_move(c, argv[0], (int)n)) exit(EXIT_FAILURE);
+  if(disorder_move(getclient(), argv[0], (int)n)) exit(EXIT_FAILURE);
 }
 
-static void cf_part(disorder_client *c,
-                   char **argv) {
+static void cf_part(char **argv) {
   char *s;
 
-  if(disorder_part(c, &s, argv[0], argv[1], argv[2])) exit(EXIT_FAILURE);
+  if(disorder_part(getclient(), &s, argv[0], argv[1], argv[2])) exit(EXIT_FAILURE);
   xprintf("%s\n", nullcheck(utf82mb(s)));
 }
 
@@ -331,61 +322,54 @@ static int isarg_filename(const char *s) {
   return s[0] == '/';
 }
 
-static void cf_authorize(disorder_client *c,
-                        char **argv) {
-  if(!authorize(c, argv[0]))
-    auto_reconfigure = 1;
+static void cf_authorize(char **argv) {
+  authorize(getclient(), argv[0], argv[1]);
 }
 
-static void cf_resolve(disorder_client *c,
-                      char **argv) {
+static void cf_resolve(char **argv) {
   char *track;
 
-  if(disorder_resolve(c, &track, argv[0])) exit(EXIT_FAILURE);
+  if(disorder_resolve(getclient(), &track, argv[0])) exit(EXIT_FAILURE);
   xprintf("%s\n", nullcheck(utf82mb(track)));
 }
 
-static void cf_pause(disorder_client *c,
-                       char attribute((unused)) **argv) {
-  if(disorder_pause(c)) exit(EXIT_FAILURE);
+static void cf_pause(char attribute((unused)) **argv) {
+  if(disorder_pause(getclient())) exit(EXIT_FAILURE);
 }
 
-static void cf_resume(disorder_client *c,
-                       char attribute((unused)) **argv) {
-  if(disorder_resume(c)) exit(EXIT_FAILURE);
+static void cf_resume(char attribute((unused)) **argv) {
+  if(disorder_resume(getclient())) exit(EXIT_FAILURE);
 }
 
-static void cf_tags(disorder_client *c,
-                    char attribute((unused)) **argv) {
+static void cf_tags(char attribute((unused)) **argv) {
   char **vec;
 
-  if(disorder_tags(c, &vec, 0)) exit(EXIT_FAILURE);
+  if(disorder_tags(getclient(), &vec, 0)) exit(EXIT_FAILURE);
   while(*vec)
       xprintf("%s\n", nullcheck(utf82mb(*vec++)));
 }
 
-static void cf_users(disorder_client *c,
-                    char attribute((unused)) **argv) {
+static void cf_users(char attribute((unused)) **argv) {
   char **vec;
 
-  if(disorder_users(c, &vec, 0)) exit(EXIT_FAILURE);
+  if(disorder_users(getclient(), &vec, 0)) exit(EXIT_FAILURE);
   while(*vec)
     xprintf("%s\n", nullcheck(utf82mb(*vec++)));
 }
 
-static void cf_get_global(disorder_client *c, char **argv) {
+static void cf_get_global(char **argv) {
   char *value;
 
-  if(disorder_get_global(c, argv[0], &value)) exit(EXIT_FAILURE);
+  if(disorder_get_global(getclient(), argv[0], &value)) exit(EXIT_FAILURE);
   xprintf("%s\n", nullcheck(utf82mb(value)));
 }
 
-static void cf_set_global(disorder_client *c, char **argv) {
-  if(disorder_set_global(c, argv[0], argv[1])) exit(EXIT_FAILURE);
+static void cf_set_global(char **argv) {
+  if(disorder_set_global(getclient(), argv[0], argv[1])) exit(EXIT_FAILURE);
 }
 
-static void cf_unset_global(disorder_client *c, char **argv) {
-  if(disorder_unset_global(c, argv[0])) exit(EXIT_FAILURE);
+static void cf_unset_global(char **argv) {
+  if(disorder_unset_global(getclient(), argv[0])) exit(EXIT_FAILURE);
 }
 
 static int isarg_integer(const char *s) {
@@ -398,62 +382,108 @@ static int isarg_integer(const char *s) {
   return 1;
 }
 
-static void cf_new(disorder_client *c,
-                  char **argv) {
+static void cf_new(char **argv) {
   char **vec;
 
-  if(disorder_new_tracks(c, &vec, 0, argv[0] ? atoi(argv[0]) : 0))
+  if(disorder_new_tracks(getclient(), &vec, 0, argv[0] ? atoi(argv[0]) : 0))
     exit(EXIT_FAILURE);
   while(*vec)
     xprintf("%s\n", nullcheck(utf82mb(*vec++)));
 }
 
-static void cf_rtp_address(disorder_client *c,
-                          char attribute((unused)) **argv) {
+static void cf_rtp_address(char attribute((unused)) **argv) {
   char *address, *port;
 
-  if(disorder_rtp_address(c, &address, &port)) exit(EXIT_FAILURE);
+  if(disorder_rtp_address(getclient(), &address, &port)) exit(EXIT_FAILURE);
   xprintf("address: %s\nport: %s\n", address, port);
 }
 
-static void cf_adduser(disorder_client *c,
-                      char **argv) {
-  if(disorder_adduser(c, argv[0], argv[1]))
+static int isarg_rights(const char *arg) {
+  return strchr(arg, ',') || !parse_rights(arg, 0, 0);
+}
+
+static void cf_adduser(char **argv) {
+  if(disorder_adduser(getclient(), argv[0], argv[1], argv[2]))
     exit(EXIT_FAILURE);
 }
 
-static void cf_deluser(disorder_client *c,
-                      char **argv) {
-  if(disorder_deluser(c, argv[0]))
+static void cf_deluser(char **argv) {
+  if(disorder_deluser(getclient(), argv[0]))
     exit(EXIT_FAILURE);
 }
 
-static void cf_edituser(disorder_client *c,
-                       char **argv) {
-  if(disorder_edituser(c, argv[0], argv[1], argv[2]))
+static void cf_edituser(char **argv) {
+  if(disorder_edituser(getclient(), argv[0], argv[1], argv[2]))
     exit(EXIT_FAILURE);
 }
 
-static void cf_userinfo(disorder_client *c, char **argv) {
+static void cf_userinfo(char **argv) {
   char *s;
 
-  if(disorder_userinfo(c, argv[0], argv[1], &s))
+  if(disorder_userinfo(getclient(), argv[0], argv[1], &s))
     exit(EXIT_FAILURE);
   xprintf("%s\n", nullcheck(utf82mb(s)));
 }
 
+static int isarg_option(const char *arg) {
+  return arg[0] == '-';
+}
+
+static int argvlen(char **argv) {
+  char **start = argv;
+  
+  while(*argv)
+    ++argv;
+  return argv - start;
+}
+
+static const struct option setup_guest_options[] = {
+  { "help", no_argument, 0, 'h' },
+  { "online-registration", no_argument, 0, 'r' },
+  { "no-online-registration", no_argument, 0, 'R' },
+  { 0, 0, 0, 0 }
+};
+
+static void help_setup_guest(void) {
+  xprintf("Usage:\n"
+         "  disorder setup-guest [OPTIONS]\n"
+         "Options:\n"
+         "  --help, -h                Display usage message\n"
+         "  --online-registration     Enable online registration (default)\n"
+         "  --no-online-registration  Disable online registration\n");
+  xfclose(stdout);
+  exit(0);
+}
+
+static void cf_setup_guest(char **argv) {
+  int n, online_registration = 1;
+  
+  while((n = getopt_long(argvlen(argv) + 1, argv - 1,
+                        "hrR", setup_guest_options, 0)) >= 0) {
+    switch(n) {
+    case 'h': help_setup_guest();
+    case 'r': online_registration = 1; break;
+    case 'R': online_registration = 0; break;
+    default: fatal(0, "invalid option");
+    }
+  }
+  if(disorder_adduser(getclient(), "guest", "",
+                     online_registration ? "read,register" : "read"))
+    exit(EXIT_FAILURE);
+}
+
 static const struct command {
   const char *name;
   int min, max;
-  void (*fn)(disorder_client *c, char **);
+  void (*fn)(char **);
   int (*isarg)(const char *);
   const char *argstr, *desc;
 } commands[] = {
-  { "adduser",        2, 2, cf_adduser, 0, "USER PASSWORD",
+  { "adduser",        2, 3, cf_adduser, isarg_rights, "USER PASSWORD [RIGHTS]",
                       "Create a new user" },
   { "allfiles",       1, 2, cf_allfiles, isarg_regexp, "DIR [~REGEXP]",
                       "List all files and directories in DIR" },
-  { "authorize",      1, 1, cf_authorize, 0, "USER",
+  { "authorize",      1, 2, cf_authorize, isarg_rights, "USER [RIGHTS]",
                       "Authorize USER to connect to the server" },
   { "deluser",        1, 1, cf_deluser, 0, "USER",
                       "Delete a user" },
@@ -528,6 +558,8 @@ static const struct command {
                       "Set a global preference value" },
   { "set-volume",     2, 2, cf_set_volume, 0, "LEFT RIGHT",
                       "Set the volume" },
+  { "setup-guest",    0, INT_MAX, cf_setup_guest, isarg_option, "[OPTIONS]",
+                      "Create the guest login" },
   { "shutdown",       0, 0, cf_shutdown, 0, "",
                       "Shut down the daemon" },
   { "stats",          0, 0, cf_stats, 0, "",
@@ -575,7 +607,6 @@ static void help_commands(void) {
 
 int main(int argc, char **argv) {
   int n, i, j, local = 0;
-  disorder_client *c = 0;
   int status = 0;
   struct vector args;
   const char *user = 0, *password = 0;
@@ -608,17 +639,19 @@ int main(int argc, char **argv) {
     config->password = password;
   if(local)
     config->connect.n = 0;
-  if(!(c = disorder_new(1))) exit(EXIT_FAILURE);
-  if(disorder_connect(c)) exit(EXIT_FAILURE);
   n = optind;
+  optind = 1;                          /* for subsequent getopt calls */
   /* accumulate command args */
   while(n < argc) {
     if((i = TABLE_FIND(commands, struct command, name, argv[n])) < 0)
       fatal(0, "unknown command '%s'", argv[n]);
     if(n + commands[i].min >= argc)
       fatal(0, "missing arguments to '%s'", argv[n]);
-    n++;
     vector_init(&args);
+    /* Include the command name in the args, but at element -1, for
+     * the benefit of subcommand getopt calls */
+    vector_append(&args, argv[n]);
+    n++;
     for(j = 0; j < commands[i].min; ++j)
       vector_append(&args, nullcheck(mb2utf8(argv[n + j])));
     for(; j < commands[i].max
@@ -626,14 +659,10 @@ int main(int argc, char **argv) {
          && commands[i].isarg(argv[n + j]); ++j)
       vector_append(&args, nullcheck(mb2utf8(argv[n + j])));
     vector_terminate(&args);
-    commands[i].fn(c, args.vec);
+    commands[i].fn(args.vec + 1);
     n += j;
   }
-  if(auto_reconfigure) {
-    assert(c != 0);
-    if(disorder_reconfigure(c)) exit(EXIT_FAILURE);
-  }
-  if(c && disorder_close(c)) exit(EXIT_FAILURE);
+  if(client && disorder_close(client)) exit(EXIT_FAILURE);
   if(fclose(stdout) < 0) fatal(errno, "error closing stdout");
   return status;
 }
index 847ae35..d04cb3b 100644 (file)
@@ -54,15 +54,17 @@ Display version number.
 List all known commands.
 .SH COMMANDS
 .TP
-.B adduser \fIUSER PASSWORD\fR
-Create a new user with default rights.
-.TP
-.B authorize \fIUSER\fR
-Chooses a password for \fIUSER\fR and adds it to \fIconfig.private\fR.  Also
-creates an appropriate \fIconfig.USER\fR, be owned by the user.
+.B adduser \fIUSER PASSWORD\fR [\fIRIGHTS\fR]
+Create a new user.  If \fIRIGHTS\fR is not specified then the
+\fBdefault_rights\fR setting from the server's configuration file applies.
+.TP
+.B authorize \fIUSER\fR [\fIRIGHTS\fR]
+Create \fIUSER\fR with a random password.  \fIUSER\fR must be a UNIX login
+user (not just any old string).  If \fIRIGHTS\fR is not specified then the
+\fBdefault_rights\fR setting from the server's configuration file applies.
 .IP
-If at least one \fBauthorize\fR command succeeds then the server is
-automatically told to re-read its configuration.
+An appropriate \fIconfig.USER\fR is created, owned by the user, so they should
+be able to log in immediately.
 .TP
 .B deluser \fIUSER\fR
 Delete a user.
@@ -74,7 +76,7 @@ An optional regexp may be specified, marked with an initial \fB~\fR.  Only
 directories with a basename matching the regexp will be returned.
 .TP
 .B disable
-Disables playing after the current track finishes.
+Disable playing after the current track finishes.
 .TP
 .B edituser \fIUSER PROPERTY VALUE
 Set some property of a user.
@@ -95,13 +97,13 @@ Display the preference \fIKEY\fR for \fITRACK\fR.
 Get a global preference.
 .TP
 .B get-volume
-Displays the current volume settings.
+Display the current volume settings.
 .TP
 .B length \fITRACK\fR
-Reports the length of \fITRACK\fR in seconds.
+Display the length of \fITRACK\fR in seconds.
 .TP
 .B log
-Writes event log messages to standard output, until the server is terminated.
+Write event log messages to standard output, until the server is terminated.
 See \fBdisorder_protocol\fR (5) for details of the output syntax.
 .TP
 .B move \fITRACK\fR \fIDELTA\fR
@@ -193,7 +195,15 @@ Set the preference \fIKEY\fR for \fITRACK\fR to \fIVALUE\fR.
 Set a global preference.
 .TP
 .B set-volume \fBLEFT\fR \fBRIGHT\fR
-Sets the volume.
+Set the volume.
+.TP
+.B setup-guest \fR[\fB--no-online-registration\fR]
+Create the "guest" user for use by the web interface.  This user will have no
+password and will only have the "read" and "register" rights, the latter
+allowing new users to automatically register themselves via the web interface.
+.IP
+With the option \fB--no-online-registration\fR, the "register" right is
+suppressed and users must be manually created by an administrator.
 .TP
 .B shutdown
 Shut down the daemon.
index a1a4af2..b48eb76 100644 (file)
@@ -50,9 +50,11 @@ to execute the command.
 .PP
 Neither commands nor responses have a body unless stated otherwise.
 .TP
-.B adduser \fIUSERNAME PASSWORD
-Creates a new user with the given username and password.  Requires the
-\fBadmin\fR right, and only works on local connections.
+.B adduser \fIUSERNAME PASSWORD \fR[\fIRIGHTS\fR]
+Creates a new user with the given username and password.  The new user's rights
+list can be specified; if it is not then the \fBdefault_rights\fR setting
+applies instead.  Requires the \fBadmin\fR right, and only works on local
+connections.
 .TP
 .B allfiles \fIDIRECTORY\fR [\fIREGEXP\fR]
 Lists all the files and directories in \fIDIRECTORY\fR in a response body.
index 39272e6..8096ee7 100644 (file)
@@ -678,8 +678,9 @@ int disorder_rtp_address(disorder_client *c, char **addressp, char **portp) {
 }
 
 int disorder_adduser(disorder_client *c,
-                    const char *user, const char *password) {
-  return disorder_simple(c, 0, "adduser", user, password, (char *)0);
+                    const char *user, const char *password,
+                    const char *rights) {
+  return disorder_simple(c, 0, "adduser", user, password, rights, (char *)0);
 }
 
 int disorder_deluser(disorder_client *c, const char *user) {
index c685b3d..88a86f4 100644 (file)
@@ -189,7 +189,8 @@ int disorder_new_tracks(disorder_client *c,
 int disorder_rtp_address(disorder_client *c, char **addressp, char **portp);
 
 int disorder_adduser(disorder_client *c,
-                    const char *user, const char *password);
+                    const char *user, const char *password,
+                    const char *rights);
 int disorder_deluser(disorder_client *c, const char *user);
 int disorder_userinfo(disorder_client *c, const char *user, const char *key,
                      char **valuep);
index 0f1dc96..7ed9276 100644 (file)
@@ -493,19 +493,17 @@ static int set_backend(const struct config_state *cs,
 static int set_rights(const struct config_state *cs,
                      const struct conf *whoami,
                      int nvec, char **vec) {
-  rights_type r;
-
   if(nvec != 1) {
     error(0, "%s:%d: '%s' requires one argument",
          cs->path, cs->line, whoami->name);
     return -1;
   }
-  if(parse_rights(vec[0], &r)) {
+  if(parse_rights(vec[0], 0, 1)) {
     error(0, "%s:%d: invalid rights string '%s'",
          cs->path, cs->line, vec[0]);
     return -1;
   }
-  *ADDRESS(cs->config, rights_type) = r;
+  *ADDRESS(cs->config, char *) = vec[0];
   return 0;
 }
 
@@ -1207,7 +1205,7 @@ static void config_postdefaults(struct config *c,
       r |= RIGHT_REMOVE_MINE;
     else
       r |= RIGHT_REMOVE_ANY;
-    c->default_rights = r;
+    c->default_rights = rights_string(r);
   }
 }
 
index f08b866..11e3487 100644 (file)
@@ -254,7 +254,7 @@ struct config {
   long cookie_key_lifetime;
 
   /** @brief Default rights for a new user */
-  rights_type default_rights;
+  char *default_rights;
   
   /* derived values: */
   int nparts;                          /* number of distinct name parts */
index 585fd7e..425b84c 100644 (file)
@@ -198,7 +198,7 @@ char *verify_cookie(const char *cookie, rights_type *rights) {
   }
   password = kvp_get(k, "password");
   if(!password) password = "";
-  if(parse_rights(kvp_get(k, "rights"), rights))
+  if(parse_rights(kvp_get(k, "rights"), rights, 1))
     return 0;
   /* construct the expected subject.  We re-encode the timestamp and the
    * password. */
index d03eae1..f0dc892 100644 (file)
@@ -78,16 +78,18 @@ char *rights_string(rights_type r) {
 /** @brief Parse a rights list
  * @param s Rights list in string form
  * @param rp Where to store rights, or NULL to just validate
+ * @param report Nonzero to log errors
  * @return 0 on success, non-0 if @p s is not valid
  */
-int parse_rights(const char *s, rights_type *rp) {
+int parse_rights(const char *s, rights_type *rp, int report) {
   rights_type r = 0;
   const char *t;
   size_t n, l;
 
   if(!*s) {
     /* You can't have no rights */
-    error(0, "empty rights string");
+    if(report)
+      error(0, "empty rights string");
     return -1;
   }
   while(*s) {
@@ -103,7 +105,8 @@ int parse_rights(const char *s, rights_type *rp) {
           && !strncmp(rights_names[n].name, s, l))
          break;
       if(n >= NRIGHTS) {
-       error(0, "unknown user right '%.*s'", (int)l, s);
+       if(report)
+          error(0, "unknown user right '%.*s'", (int)l, s);
        return -1;
       }
       r |= rights_names[n].bit;
index fb2d625..794213c 100644 (file)
 typedef uint32_t rights_type;
 
 char *rights_string(rights_type r);
-int parse_rights(const char *s, rights_type *rp);
+int parse_rights(const char *s, rights_type *rp, int report);
 
 #endif /* RIGHTS_H */
 
index 3694748..dc4cd4e 100644 (file)
@@ -2459,6 +2459,10 @@ static int create_user(const char *user,
     error(0, "invalid username '%s'", user);
     return -1;
   }
+  if(parse_rights(rights, 0, 1)) {
+    error(0, "invalid rights string");
+    return -1;
+  }
   /* data for this user */
   if(password)
     kvp_set(&k, "password", password);
@@ -2485,10 +2489,16 @@ static int one_old_user(const char *user, const char *password,
   /* pick rights */
   if(!strcmp(user, "root"))
     rights = "all";
-  else if(trusted(user))
-    rights = rights_string(config->default_rights|RIGHT_ADMIN|RIGHT_RESCAN);
-  else
-    rights = rights_string(config->default_rights);
+  else if(trusted(user)) {
+    rights_type r;
+
+    parse_rights(config->default_rights, &r, 1);
+    r &= (RIGHT_SCRATCH__MASK|RIGHT_MOVE__MASK|RIGHT_REMOVE__MASK);
+    r |= (RIGHT_ADMIN|RIGHT_RESCAN
+          |RIGHT_SCRATCH_ANY|RIGHT_MOVE_ANY|RIGHT_REMOVE_ANY);
+    rights = rights_string(r);
+  } else
+    rights = config->default_rights;
   return create_user(user, password, rights, 0/*email*/, 0/*confirmation*/,
                      tid, DB_NOOVERWRITE);
 }
@@ -2569,13 +2579,12 @@ const char *trackdb_get_password(const char *user) {
  */
 int trackdb_adduser(const char *user,
                     const char *password,
-                    rights_type rights,
+                    const char *rights,
                     const char *email,
                     const char *confirmation) {
   int e;
-  const char *r = rights_string(rights);
 
-  WITH_TRANSACTION(create_user(user, password, r, email, confirmation,
+  WITH_TRANSACTION(create_user(user, password, rights, email, confirmation,
                                tid, DB_NOOVERWRITE));
   if(e) {
     error(0, "cannot created user '%s' because they already exist", user);
@@ -2583,9 +2592,9 @@ int trackdb_adduser(const char *user,
   } else {
     if(email)
       info("created user '%s' with rights '%s' and email address '%s'",
-           user, r, email);
+           user, rights, email);
     else
-      info("created user '%s' with rights '%s'", user, r);
+      info("created user '%s' with rights '%s'", user, rights);
     return 0;
   }
 }
@@ -2658,7 +2667,7 @@ int trackdb_edituserinfo(const char *user,
       error(0, "cannot remove 'rights' key from user '%s'", user);
       return -1;
     }
-    if(parse_rights(value, 0)) {
+    if(parse_rights(value, 0, 1)) {
       error(0, "invalid rights string");
       return -1;
     }
index d777146..d414545 100644 (file)
@@ -161,7 +161,7 @@ void trackdb_create_root(void);
 const char *trackdb_get_password(const char *user);
 int trackdb_adduser(const char *user,
                     const char *password,
-                    rights_type rights,
+                    const char *rights,
                     const char *email,
                     const char *confirmation);
 int trackdb_deluser(const char *user);
index 3239366..898157c 100644 (file)
@@ -32,6 +32,7 @@ complete -o default \
              search set set-volume shutdown stats unset version resolve
              part pause resume scratch-id get-global set-global unset-global
              tags new rtp-address adduser users edituser deluser userinfo
+             setup-guest
              -h --help -H --help-commands --version -V --config -c
              --length --debug -d" \
         disorder
index 22cf454..a69c451 100644 (file)
@@ -434,7 +434,7 @@ static int c_user(struct conn *c,
   }
   password = kvp_get(k, "password");
   if(!password) password = "";
-  if(parse_rights(kvp_get(k, "rights"), &rights)) {
+  if(parse_rights(kvp_get(k, "rights"), &rights, 1)) {
     error(0, "error parsing rights for %s", vec[0]);
     sink_writes(ev_writer_sink(c->w), "530 authentication failed\n");
     return 1;
@@ -1077,8 +1077,18 @@ static int c_revoke(struct conn *c,
 
 static int c_adduser(struct conn *c,
                     char **vec,
-                    int attribute((unused)) nvec) {
-  if(trackdb_adduser(vec[0], vec[1], config->default_rights,
+                    int nvec) {
+  const char *rights;
+
+  if(nvec > 2) {
+    rights = vec[2];
+    if(parse_rights(vec[2], 0, 1)) {
+      sink_writes(ev_writer_sink(c->w), "550 Invalid rights list\n");
+      return -1;
+    }
+  } else
+    rights = config->default_rights;
+  if(trackdb_adduser(vec[0], vec[1], rights,
                     0/*email*/, 0/*confirmation*/))
     sink_writes(ev_writer_sink(c->w), "550 Cannot create user\n");
   else
@@ -1213,7 +1223,7 @@ static const struct command {
    */
   rights_type rights;
 } commands[] = {
-  { "adduser",        2, 2,       c_adduser,        RIGHT_ADMIN|RIGHT__LOCAL },
+  { "adduser",        2, 3,       c_adduser,        RIGHT_ADMIN|RIGHT__LOCAL },
   { "allfiles",       0, 2,       c_allfiles,       RIGHT_READ },
   { "confirm",        1, 1,       c_confirm,        0 },
   { "cookie",         1, 1,       c_cookie,         0 },
index 74f0212..2e5f86e 100755 (executable)
@@ -55,8 +55,15 @@ def test():
     users = c.users()
     assert dtest.lists_have_same_contents(users,
                                           ["fred", "root"])
+    print " creating the guest user"
+    dtest.command(["disorder",
+                   "--config", disorder._configfile, "--no-per-user-config",
+                   "--user", "root", "setup-guest"])
+    print " logging in as guest user"
+    gc = disorder.client(user="guest", password="")
+    gc.version()
     print " testing user registration"
-    cs = c.register("joe", "joepass", "joe@nowhere.invalid")
+    cs = gc.register("joe", "joepass", "joe@nowhere.invalid")
     print " confirmation string is %s" % cs
     print " checking unconfirmed user cannot log in"
     jc = disorder.client(user="joe", password="joepass")
@@ -67,7 +74,8 @@ def test():
     except disorder.operationError:
       pass                              # good
     print " confirming user"
-    c.confirm(cs)
+    gc = disorder.client(user="guest", password="")
+    gc.confirm(cs)
     print " checking confirmed user can log in"
     jc = disorder.client(user="joe", password="joepass")
     jc.version()