chiark / gitweb /
Web interface starts to reflect user rights properly:
authorrjk@greenend.org.uk <>
Sun, 23 Dec 2007 18:16:43 +0000 (18:16 +0000)
committerrjk@greenend.org.uk <>
Sun, 23 Dec 2007 18:16:43 +0000 (18:16 +0000)
- scratch/move/remove rights check is moved to lib/queue-rights.c
  which is shared with the web interface
- new @rights@ and @movable@ expansions (not used yet)
- @removable@ and @scratchable@ rewritten for the new world

doc/disorder_config.5.in
lib/Makefile.am
lib/queue-rights.c [new file with mode: 0644]
lib/rights.h
server/cgimain.c
server/dcgi.c
server/dcgi.h
server/server.c
templates/playing.html

index 269abe855c7aad4348030e997145d162328eb6f3..eeef352daa801e3ae49c127c9944732e46b79aab 100644 (file)
@@ -911,6 +911,10 @@ file for full documentation of the labels used by the standard templates.
 .B @length@
 Expands to the length of the current track.
 .TP
+.B @movable@
+Expands to \fBtrue\fR if the current track is movable, otherwise to
+\fBfalse\fR.
+.TP
 .B @navigate{\fIDIRECTORY\fB}{\fITEMPLATE\fB}
 Expands \fITEMPLATE\fR for each component of \fIDIRECTORY\fR in turn.
 Use \fB@dirname\fR and \fB@basename@\fR to get the components of the path to
@@ -992,9 +996,25 @@ Expands to \fBtrue\fR if random play is currently enabled, otherwise to
 Expands \fITEMPLATE\fR repeatedly using the each recently played track in turn
 as the current track.  The most recently played track comes first.
 .TP
+.B @removable@
+Expands to \fBtrue\fR if the current track is removable, otherwise to
+\fBfalse\fR.
+.TP
 .B @resolve{\fITRACK\fB}@
 Resolve aliases for \fITRACK\fR and expands to the result.
 .TP
+.B @right{\fIRIGHT\fB}@
+Exapnds to \fBtrue\fR if the user has right \fIRIGHT\fR, otherwise to
+\fBfalse\fR.
+.TP
+.B @right{\fIRIGHT\fB}{\fITRUEPART\fB}{\fIFALSEPART\fB}@
+Expands to \fITRUEPART\fR if the user right \fIRIGHT\fR, otherwise to
+\fIFALSEPART\fR (which may be omitted).
+.TP
+.B @scratchable@
+Expands to \fBtrue\fR if the currently playing track is scratchable, otherwise
+to \fBfalse\fR.
+.TP
 .B @search{\fIPART\fB}\fR[\fB{\fICONTEXT\fB}\fR]\fB{\fITEMPLATE\fB}@
 Expands \fITEMPLATE\fR once for each group of search results that have
 a common value of track part \fIPART\fR.
index a7ddf48bfe1a644a0fa5b933765f26e17c676ab9..7b4ce0a61f07a3716efffadcd05a6d057ea0632c 100644 (file)
@@ -53,7 +53,7 @@ libdisorder_a_SOURCES=charset.c charset.h             \
        asprintf.c fprintf.c snprintf.c                 \
        queue.c queue.h                                 \
        regsub.c regsub.h                               \
-       rights.c rights.h                               \
+       rights.c queue-rights.c rights.h                \
        rtp.h                                           \
        selection.c selection.h                         \
        signame.c signame.h                             \
diff --git a/lib/queue-rights.c b/lib/queue-rights.c
new file mode 100644 (file)
index 0000000..523a4ad
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * This file is part of DisOrder.
+ * Copyright (C) 2007 Richard Kettlewell
+ *
+ * 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
+ * (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.
+ *
+ * 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
+ */
+/** @file lib/queue-rights.c
+ * @brief Various rights-checking operations
+ */
+
+#include <config.h>
+#include "types.h"
+
+#include <string.h>
+
+#include "queue.h"
+#include "rights.h"
+
+/** @brief Test for scratchability
+ * @param rights User rights
+ * @param who Username
+ * @param q Queue entry or NULL
+ * @return non-0 if scratchable, else 0
+ */
+int right_scratchable(rights_type rights, const char *who,
+                     const struct queue_entry *q) {
+  rights_type r;
+  
+  if(!q)
+    return 0;
+  if(q->submitter)
+    if(!strcmp(q->submitter, who))
+      r = RIGHT_SCRATCH_MINE|RIGHT_SCRATCH_ANY;
+    else
+      r = RIGHT_SCRATCH_ANY;
+  else
+    r = RIGHT_SCRATCH_RANDOM|RIGHT_SCRATCH_ANY;
+  return !!(rights & r);
+}
+
+/** @brief Test for movability
+ * @param rights User rights
+ * @param who Username
+ * @param q Queue entry or NULL
+ * @return non-0 if movable, else 0
+ */
+int right_movable(rights_type rights, const char *who,
+                 const struct queue_entry *q) {
+  rights_type r;
+
+  if(!q)
+    return 0;
+  if(q->submitter)
+    if(!strcmp(q->submitter, who))
+      r = RIGHT_MOVE_MINE|RIGHT_MOVE_ANY;
+    else
+      r = RIGHT_MOVE_ANY;
+  else
+    r = RIGHT_MOVE_RANDOM|RIGHT_MOVE_ANY;
+  return !!(rights & r);
+}
+
+/** @brief Test for removability
+ * @param rights User rights
+ * @param who Username
+ * @param q Queue entry or NULL
+ * @return non-0 if removable, else 0
+ */
+int right_removable(rights_type rights, const char *who,
+                   const struct queue_entry *q) {
+  rights_type r;
+  
+  if(!q)
+    return 0;
+  if(q->submitter)
+    if(!strcmp(q->submitter, who))
+      r = RIGHT_REMOVE_MINE|RIGHT_REMOVE_ANY;
+    else
+      r = RIGHT_REMOVE_ANY;
+  else
+    r = RIGHT_REMOVE_RANDOM|RIGHT_REMOVE_ANY;
+  return !!(rights & r);
+}
+
+/*
+Local Variables:
+c-basic-offset:2
+comment-column:40
+fill-column:79
+indent-tabs-mode:nil
+End:
+*/
index 794213c6f6bf390a0630dbab0d930ea67401ac30..c51ca5ca0c4bcc0b04c472651e9b64c234aa6ddd 100644 (file)
@@ -24,6 +24,8 @@
 #ifndef RIGHTS_H
 #define RIGHTS_H
 
+struct queue_entry;
+
 /** @brief User can perform read-only operations */
 #define RIGHT_READ            0x00000001
 
@@ -102,6 +104,12 @@ typedef uint32_t rights_type;
 
 char *rights_string(rights_type r);
 int parse_rights(const char *s, rights_type *rp, int report);
+int right_scratchable(rights_type rights, const char *who,
+                     const struct queue_entry *q);
+int right_movable(rights_type rights, const char *who,
+                 const struct queue_entry *q);
+int right_removable(rights_type rights, const char *who,
+                   const struct queue_entry *q);
 
 #endif /* RIGHTS_H */
 
index ec63bf8188ed0ab2e55d453263f4bbad23ef7e28..53f7a9bac6189e37df639ed1f78594d3d41916c5 100644 (file)
@@ -33,7 +33,6 @@
 #include "client.h"
 #include "sink.h"
 #include "cgi.h"
-#include "dcgi.h"
 #include "mem.h"
 #include "log.h"
 #include "configuration.h"
@@ -41,6 +40,7 @@
 #include "api-client.h"
 #include "mime.h"
 #include "printf.h"
+#include "dcgi.h"
 
 /** @brief Infer the base URL for the web interface if it's not set
  *
@@ -111,14 +111,7 @@ int main(int argc, char **argv) {
        login_cookie = cd.cookies[n].value;
     }
   }
-  /* Log in with the cookie if possible otherwise as guest */
-  if(disorder_connect_cookie(g.client, login_cookie)) {
-    disorder_cgi_error(&output, &s, "connect");
-    return 0;
-  }
-  /* If there was a cookie but it went bad, we forget it */
-  if(login_cookie && !strcmp(disorder_user(g.client), "guest"))
-    login_cookie = 0;
+  disorder_cgi_login(&s, &output);
   /* TODO RFC 3875 s8.2 recommendations e.g. concerning PATH_INFO */
   disorder_cgi(&output, &s);
   if(fclose(stdout) < 0) fatal(errno, "error closing stdout");
index e81aac6e14bfa9ffb7c47fc6e3b5114862ca9d3a..08203f8052f16a37fd46ac298a55eb52d53fe58d 100644 (file)
@@ -39,7 +39,6 @@
 #include "vector.h"
 #include "sink.h"
 #include "cgi.h"
-#include "dcgi.h"
 #include "log.h"
 #include "configuration.h"
 #include "table.h"
@@ -54,6 +53,7 @@
 #include "defs.h"
 #include "trackname.h"
 #include "charset.h"
+#include "dcgi.h"
 
 char *login_cookie;
 
@@ -141,6 +141,7 @@ static void lookups(dcgi_state *ds, unsigned want) {
   unsigned need;
   struct queue_entry *r, *rnext;
   const char *dir, *re;
+  char *rights;
 
   if(ds->g->client && (need = want ^ (ds->g->flags & want)) != 0) {
     if(need & DC_QUEUE)
@@ -175,6 +176,12 @@ static void lookups(dcgi_state *ds, unsigned want) {
                          &ds->g->files, &ds->g->nfiles))
          ds->g->nfiles = 0;
     }
+    if(need & DC_RIGHTS) {
+      ds->g->rights = RIGHT_READ;      /* fail-safe */
+      if(!disorder_userinfo(ds->g->client, disorder_user(ds->g->client),
+                           "rights", &rights))
+       parse_rights(rights, &ds->g->rights, 1);
+    }
     ds->g->flags |= need;
   }
 }
@@ -473,11 +480,7 @@ static void act_logout(cgi_sink *output,
   disorder_revoke(ds->g->client);
   login_cookie = 0;
   /* Reconnect as guest */
-  ds->g->client = disorder_new(0);
-  if(disorder_connect_cookie(ds->g->client, 0)) {
-    disorder_cgi_error(output, ds, "connect");
-    exit(0);
-  }
+  disorder_cgi_login(ds, output);
   /* Back to the login page */
   expand_template(ds, output, "login");
 }
@@ -1236,17 +1239,12 @@ static void exp_scratchable(int attribute((unused)) nargs,
                            cgi_sink *output,
                            void attribute((unused)) *u) {
   dcgi_state *ds = u;
-  int result;
-
-  if(config->restrictions & RESTRICT_SCRATCH) {
-    lookups(ds, DC_PLAYING);
-    result = (ds->g->playing
-             && (!ds->g->playing->submitter
-                 || !strcmp(ds->g->playing->submitter,
-                            disorder_user(ds->g->client))));
-  } else
-    result = 1;
-  sink_printf(output->sink, "%s", bool2str(result));
+
+  lookups(ds, DC_PLAYING|DC_RIGHTS);
+  sink_printf(output->sink, "%s",
+             bool2str(right_scratchable(ds->g->rights,
+                                        disorder_user(ds->g->client),
+                                        ds->g->playing)));
 }
 
 static void exp_removable(int attribute((unused)) nargs,
@@ -1254,16 +1252,25 @@ static void exp_removable(int attribute((unused)) nargs,
                          cgi_sink *output,
                          void attribute((unused)) *u) {
   dcgi_state *ds = u;
-  int result;
 
-  if(config->restrictions & RESTRICT_REMOVE)
-    result = (ds->track
-             && ds->track->submitter
-             && !strcmp(ds->track->submitter,
-                        disorder_user(ds->g->client)));
-  else
-    result = 1;
-  sink_printf(output->sink, "%s", bool2str(result));
+  lookups(ds, DC_RIGHTS);
+  sink_printf(output->sink, "%s",
+             bool2str(right_removable(ds->g->rights,
+                                      disorder_user(ds->g->client),
+                                      ds->track)));
+}
+
+static void exp_movable(int attribute((unused)) nargs,
+                       char attribute((unused)) **args,
+                       cgi_sink *output,
+                       void attribute((unused)) *u) {
+  dcgi_state *ds = u;
+
+  lookups(ds, DC_RIGHTS);
+  sink_printf(output->sink, "%s",
+             bool2str(right_movable(ds->g->rights,
+                                    disorder_user(ds->g->client),
+                                    ds->track)));
 }
 
 static void exp_navigate(int attribute((unused)) nargs,
@@ -1536,6 +1543,25 @@ static void exp_user(int attribute((unused)) nargs,
   cgi_output(output, "%s", disorder_user(ds->g->client));
 }
 
+static void exp_right(int attribute((unused)) nargs,
+                     char **args,
+                     cgi_sink *output,
+                     void *u) {
+  dcgi_state *const ds = u;
+  const char *right = expandarg(args[0], ds);
+  rights_type r;
+
+  lookups(ds, DC_RIGHTS);
+  if(parse_rights(right, &r, 1/*report*/))
+    r = 0;
+  if(args[1] == 0)
+    cgi_output(output, "%s", bool2str(!!(r & ds->g->rights)));
+  else if(r & ds->g->rights)
+    expandstring(output, args[1], ds);
+  else if(args[2])
+    expandstring(output, args[2], ds);
+}
+
 static const struct cgi_expansion expansions[] = {
   { "#", 0, INT_MAX, EXP_MAGIC, exp_comment },
   { "action", 0, 0, 0, exp_action },
@@ -1563,6 +1589,7 @@ static const struct cgi_expansion expansions[] = {
   { "isrecent", 0, 0, 0, exp_isrecent },
   { "label", 1, 1, 0, exp_label },
   { "length", 0, 0, 0, exp_length },
+  { "movable", 0, 0, 0, exp_movable },
   { "navigate", 2, 2, EXP_MAGIC, exp_navigate },
   { "ne", 2, 2, 0, exp_ne },
   { "new", 1, 1, EXP_MAGIC, exp_new },
@@ -1583,6 +1610,7 @@ static const struct cgi_expansion expansions[] = {
   { "recent", 1, 1, EXP_MAGIC, exp_recent },
   { "removable", 0, 0, 0, exp_removable },
   { "resolve", 1, 1, 0, exp_resolve },
+  { "right", 1, 3, EXP_MAGIC, exp_right },
   { "scratchable", 0, 0, 0, exp_scratchable },
   { "search", 2, 3, EXP_MAGIC, exp_search },
   { "server-version", 0, 0, 0, exp_server_version },
@@ -1646,6 +1674,22 @@ void disorder_cgi_error(cgi_sink *output, dcgi_state *ds,
   perform_action(output, ds, "error");
 }
 
+/** @brief Log in as the current user or guest if none */
+void disorder_cgi_login(dcgi_state *ds, cgi_sink *output) {
+  /* Create a new connection */
+  ds->g->client = disorder_new(0);
+  /* Forget everything we knew */
+  ds->g->flags = 0;
+  /* Reconnect */
+  if(disorder_connect_cookie(ds->g->client, login_cookie)) {
+    disorder_cgi_error(output, ds, "connect");
+    exit(0);
+  }
+  /* If there was a cookie but it went bad, we forget it */
+  if(login_cookie && !strcmp(disorder_user(ds->g->client), "guest"))
+    login_cookie = 0;
+}
+
 /*
 Local Variables:
 c-basic-offset:2
index 0cf5a8979da486b6d370e5ed5aae3219e3a494ff..ac110d841cfb54b5f9b9b4d2a820ecfcf7b173bc 100644 (file)
@@ -31,12 +31,14 @@ typedef struct dcgi_global {
 #define DC_DIRS 0x0010
 #define DC_FILES 0x0020
 #define DC_NEW 0x0040
+#define DC_RIGHTS 0x0080
   struct queue_entry *queue, *playing, *recent;
   int volume_left, volume_right;
   char **files, **dirs;
   int nfiles, ndirs;
   char **new;
   int nnew;
+  rights_type rights;
 } dcgi_global;
 
 typedef struct dcgi_state {
@@ -57,6 +59,7 @@ typedef struct dcgi_state {
 void disorder_cgi(cgi_sink *output, dcgi_state *ds);
 void disorder_cgi_error(cgi_sink *output, dcgi_state *ds,
                        const char *msg);
+void disorder_cgi_login(dcgi_state *ds, cgi_sink *output);
 
 extern char *login_cookie;
 
index e6ba7f86c9641802cd6445051267f26b5d262277..b680f2d49d0ed62af9af5b10d8b8b2ed1eb9ae9e 100644 (file)
@@ -233,20 +233,12 @@ static int c_play(struct conn *c, char **vec,
 static int c_remove(struct conn *c, char **vec,
                    int attribute((unused)) nvec) {
   struct queue_entry *q;
-  rights_type r;
 
   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->submitter)
-    if(!strcmp(q->submitter, c->who))
-      r = RIGHT_REMOVE_MINE|RIGHT_REMOVE_ANY;
-    else
-      r = RIGHT_REMOVE_ANY;
-  else
-    r = RIGHT_REMOVE_RANDOM|RIGHT_REMOVE_ANY;
-  if(!(c->rights & r)) {
+  if(!right_removable(c->rights, c->who, q)) {
     error(0, "%s attempted remove but lacks required rights", c->who);
     sink_writes(ev_writer_sink(c->w),
                "510 Not authorized to remove that track\n");
@@ -269,8 +261,6 @@ static int c_remove(struct conn *c, char **vec,
 static int c_scratch(struct conn *c,
                     char **vec,
                     int nvec) {
-  rights_type r;
-
   if(!playing) {
     sink_writes(ev_writer_sink(c->w), "250 nothing is playing\n");
     return 1;                  /* completed */
@@ -278,14 +268,7 @@ static int c_scratch(struct conn *c,
   /* TODO there is a bug here: if we specify an ID but it's not the currently
    * playing track then you will get 550 if you weren't authorized to scratch
    * the currently playing track. */
-  if(playing->submitter)
-    if(!strcmp(playing->submitter, c->who))
-      r = RIGHT_SCRATCH_MINE|RIGHT_SCRATCH_ANY;
-    else
-      r = RIGHT_SCRATCH_ANY;
-  else
-    r = RIGHT_SCRATCH_RANDOM|RIGHT_SCRATCH_ANY;
-  if(!(c->rights & r)) {
+  if(!right_scratchable(c->rights, c->who, playing)) {
     error(0, "%s attempted scratch but lacks required rights", c->who);
     sink_writes(ev_writer_sink(c->w),
                "510 Not authorized to scratch that track\n");
@@ -852,20 +835,13 @@ static int c_log(struct conn *c,
  * @return 0 if move is prohibited, non-0 if it is allowed
  */
 static int has_move_rights(struct conn *c, struct queue_entry **qs, int nqs) {
-  rights_type r = 0;
-
   for(; nqs > 0; ++qs, --nqs) {
     struct queue_entry *const q = *qs;
 
-    if(q->submitter)
-      if(!strcmp(q->submitter, c->who))
-       r |= RIGHT_MOVE_MINE|RIGHT_MOVE_ANY;
-      else
-      r |= RIGHT_MOVE_ANY;
-    else
-      r |= RIGHT_MOVE_RANDOM|RIGHT_MOVE_ANY;
+    if(!right_movable(c->rights, c->who, q))
+      return 0;
   }
-  return !!(c->rights & r);
+  return 1;
 }
 
 static int c_move(struct conn *c,
index 2f1f42ff9d744fd7198558a6bcacf0876286418e..31eadfd6a743d3606b09bd0006a27bc40c5334a7 100644 (file)
@@ -186,7 +186,10 @@ USA
        href="@url@?action=remove&#38;nonce=@nonce@&#38;id=@id@&#38;mgmt=@arg:mgmt@"><img
        class=button src="@label:images.scratch@"
        title="@label:playing.removeverbose@" 
-       alt="@label:playing.remove@"></a>}{&nbsp;}@</td>
+       alt="@label:playing.remove@"></a>}{<img
+       class=button src="@label:images.noscratch@"
+       title="@label:playing.removeverbose@"
+       alt="@label:playing.remove@">}@</td>
       @if{@arg:mgmt@}{
       @if{@isfirst@}
     {<td class=imgbutton>