chiark / gitweb /
uaudio: more sophisticated choice of default playback API
[disorder] / lib / trackdb-playlists.c
index 1492f8c4c13beebbf3ab34aac8655d471ff132a0..27d33193feb7f6db8e7898785467178ed084a199 100644 (file)
@@ -32,6 +32,8 @@
 #include "log.h"
 #include "configuration.h"
 #include "vector.h"
+#include "eventlog.h"
+#include "validity.h"
 
 static int trackdb_playlist_get_tid(const char *name,
                                     const char *who,
@@ -53,43 +55,6 @@ static int trackdb_playlist_delete_tid(const char *name,
                                        const char *who,
                                        DB_TXN *tid);
 
-/** @brief Parse a playlist name
- * @param name Playlist name
- * @param ownerp Where to put owner, or NULL
- * @param sharep Where to put default sharing, or NULL
- * @return 0 on success, -1 on error
- *
- * Playlists take the form USER.PLAYLIST or just PLAYLIST.  The PLAYLIST part
- * is alphanumeric and nonempty.  USER is a username (see valid_username()).
- */
-int playlist_parse_name(const char *name,
-                        char **ownerp,
-                        char **sharep) {
-  const char *dot = strchr(name, '.'), *share;
-  char *owner;
-
-  if(dot) {
-    /* Owned playlist */
-    owner = xstrndup(name, dot - name);
-    if(!valid_username(owner))
-      return -1;
-    if(!valid_username(dot + 1))
-      return -1;
-    share = "private";
-  } else {
-    /* Shared playlist */
-    if(!valid_username(name))
-      return -1;
-    owner = 0;
-    share = "public";
-  }
-  if(ownerp)
-    *ownerp = owner;
-  if(sharep)
-    *sharep = xstrdup(share);
-  return 0;
-}
-
 /** @brief Check read access rights
  * @param name Playlist name
  * @param who Who wants to read
@@ -100,7 +65,7 @@ static int playlist_may_read(const char *name,
                              const char *share) {
   char *owner;
   
-  if(!playlist_parse_name(name, &owner, 0))
+  if(playlist_parse_name(name, &owner, 0))
     return 0;
   /* Anyone can read shared playlists */
   if(!owner)
@@ -125,7 +90,7 @@ static int playlist_may_write(const char *name,
                               const char attribute((unused)) *share) {
   char *owner;
   
-  if(!playlist_parse_name(name, &owner, 0))
+  if(playlist_parse_name(name, &owner, 0))
     return 0;
   /* Anyone can modify shared playlists */
   if(!owner)
@@ -147,7 +112,7 @@ static int playlist_may_write(const char *name,
  *
  * Possible return values:
  * - @c 0 on success
- * - @c DB_NOTFOUND if the playlist doesn't exist
+ * - @c ENOENT if the playlist doesn't exist
  * - @c EINVAL if the playlist name is invalid
  * - @c EACCES if the playlist cannot be read by @p who
  */
@@ -158,13 +123,16 @@ int trackdb_playlist_get(const char *name,
                          char **sharep) {
   int e;
 
-  if(!playlist_parse_name(name, 0, 0)) {
-    error(0, "invalid playlist name '%s'", name);
+  if(playlist_parse_name(name, 0, 0)) {
+    disorder_error(0, "invalid playlist name '%s'", name);
     return EINVAL;
   }
   WITH_TRANSACTION(trackdb_playlist_get_tid(name, who,
                                             tracksp, ntracksp, sharep,
                                             tid));
+  /* Don't expose libdb error codes too much */
+  if(e == DB_NOTFOUND)
+    e = ENOENT;
   return e;
 }
 
@@ -182,7 +150,7 @@ static int trackdb_playlist_get_tid(const char *name,
     return e;
   /* Get sharability */
   if(!(s = kvp_get(k, "sharing"))) {
-    error(0, "playlist '%s' has no 'sharing' key", name);
+    disorder_error(0, "playlist '%s' has no 'sharing' key", name);
     s = "private";
   }
   /* Check the read is allowed */
@@ -193,12 +161,12 @@ static int trackdb_playlist_get_tid(const char *name,
     *sharep = xstrdup(s);
   /* Get track count */
   if(!(s = kvp_get(k, "count"))) {
-    error(0, "playlist '%s' has no 'count' key", name);
+    disorder_error(0, "playlist '%s' has no 'count' key", name);
     s = "0";
   }
   ntracks = atoi(s);
   if(ntracks < 0) {
-    error(0, "playlist '%s' has negative count", name);
+    disorder_error(0, "playlist '%s' has negative count", name);
     ntracks = 0;
   }
   /* Return track count */
@@ -212,7 +180,7 @@ static int trackdb_playlist_get_tid(const char *name,
     for(int n = 0; n < ntracks; ++n) {
       snprintf(b, sizeof b, "%d", n);
       if(!(s = kvp_get(k, b))) {
-        error(0, "playlist '%s' lacks track %d", name, n);
+        disorder_error(0, "playlist '%s' lacks track %d", name, n);
         s = "unknown";
       }
       tracks[n] = xstrdup(s);
@@ -226,6 +194,7 @@ static int trackdb_playlist_get_tid(const char *name,
 
 /** @brief Modify or create a playlist
  * @param name Playlist name
+ * @param who User modifying playlist
  * @param tracks List of tracks to set, or NULL to leave alone
  * @param ntracks Length of @p tracks
  * @param share Sharing status, or NULL to leave alone
@@ -237,6 +206,10 @@ static int trackdb_playlist_get_tid(const char *name,
  * none, and the default sharing is private (if it is an owned one) or shared
  * (otherwise).
  *
+ * If neither @c tracks nor @c share are set then we only do an access check.
+ * The database is never modified (even to create the playlist) in this
+ * situation.
+ *
  * Possible return values:
  * - @c 0 on success
  * - @c EINVAL if the playlist name is invalid
@@ -250,8 +223,8 @@ int trackdb_playlist_set(const char *name,
   int e;
   char *owner;
   
-  if(!playlist_parse_name(name, &owner, 0)) {
-    error(0, "invalid playlist name '%s'", name);
+  if(playlist_parse_name(name, &owner, 0)) {
+    disorder_error(0, "invalid playlist name '%s'", name);
     return EINVAL;
   }
   /* Check valid share types */
@@ -260,13 +233,13 @@ int trackdb_playlist_set(const char *name,
       /* Playlists with an owner must be public or private */
       if(strcmp(share, "public")
          && strcmp(share, "private")) {
-        error(0, "playlist '%s' must be public or private", name);
+        disorder_error(0, "playlist '%s' must be public or private", name);
         return EINVAL;
       }
     } else {
       /* Playlists with no owner must be shared */
       if(strcmp(share, "shared")) {
-        error(0, "playlist '%s' must be shared", name);
+        disorder_error(0, "playlist '%s' must be shared", name);
         return EINVAL;
       }
     }        
@@ -286,6 +259,7 @@ static int trackdb_playlist_set_tid(const char *name,
   struct kvp *k;
   int e;
   const char *s;
+  const char *event = "playlist_modified";
 
   if((e = trackdb_getdata(trackdb_playlistsdb, name, &k, tid))
      && e != DB_NOTFOUND)
@@ -304,24 +278,28 @@ static int trackdb_playlist_set_tid(const char *name,
     k = 0;
     kvp_set(&k, "count", 0);
     kvp_set(&k, "sharing", defshare);
+    event = "playlist_created";
   }
   /* Check that the modification is allowed */
   if(!(s = kvp_get(k, "sharing"))) {
-    error(0, "playlist '%s' has no 'sharing' key", name);
+    disorder_error(0, "playlist '%s' has no 'sharing' key", name);
     s = "private";
   }
   if(!playlist_may_write(name, who, s))
     return EACCES;
+  /* If no change was requested then don't even create */
+  if(!share && !tracks)
+    return 0;
   /* Set the new values */
   if(share)
-    kvp_set(&k, "share", share);
+    kvp_set(&k, "sharing", share);
   if(tracks) {
     char b[16];
     int oldcount, n;
 
     /* Sanity check track count */
     if(ntracks < 0 || ntracks > config->playlist_max) {
-      error(0, "invalid track count %d", ntracks);
+      disorder_error(0, "invalid track count %d", ntracks);
       return EINVAL;
     }
     /* Set the tracks */
@@ -344,7 +322,11 @@ static int trackdb_playlist_set_tid(const char *name,
     kvp_set(&k, "count", b);
   }
   /* Store the resulting record */
-  return trackdb_putdata(trackdb_playlistsdb, name, k, tid, 0);
+  e = trackdb_putdata(trackdb_playlistsdb, name, k, tid, 0);
+  /* Log the event */
+  if(!e)
+    eventlog(event, name, kvp_get(k, "sharing"), (char *)0);
+  return e;
 }
 
 /** @brief Get a list of playlists
@@ -376,11 +358,15 @@ static int trackdb_playlist_list_tid(const char *who,
   while(!(e = c->c_get(c, k, prepare_data(d), DB_NEXT))) {
     char *name = xstrndup(k->data, k->size), *owner;
     const char *share = kvp_get(kvp_urldecode(d->data, d->size),
-                                "share");
+                                "sharing");
 
     /* Extract owner; malformed names are skipped */
     if(playlist_parse_name(name, &owner, 0)) {
-      error(0, "invalid playlist name '%s' found in database", name);
+      disorder_error(0, "invalid playlist name '%s' found in database", name);
+      continue;
+    }
+    if(!share) {
+      disorder_error(0, "playlist '%s' has no 'sharing' key", name);
       continue;
     }
     /* Always list public and shared playlists
@@ -393,13 +379,14 @@ static int trackdb_playlist_list_tid(const char *who,
            && owner && !strcmp(owner, who)))
       vector_append(v, name);
   }
+  trackdb_closecursor(c);
   switch(e) {
   case DB_NOTFOUND:
     break;
   case DB_LOCK_DEADLOCK:
     return e;
   default:
-    fatal(0, "c->c_get: %s", db_strerror(e));
+    disorder_fatal(0, "c->c_get: %s", db_strerror(e));
   }
   vector_terminate(v);
   if(playlistsp)
@@ -418,19 +405,21 @@ static int trackdb_playlist_list_tid(const char *who,
  * - @c 0 on success
  * - @c EINVAL if the playlist name is invalid
  * - @c EACCES if the playlist cannot be modified by @p who
- * - @c DB_NOTFOUND if the playlist doesn't exist
+ * - @c ENOENT if the playlist doesn't exist
  */
 int trackdb_playlist_delete(const char *name,
                             const char *who) {
   int e;
   char *owner;
   
-  if(!playlist_parse_name(name, &owner, 0)) {
-    error(0, "invalid playlist name '%s'", name);
+  if(playlist_parse_name(name, &owner, 0)) {
+    disorder_error(0, "invalid playlist name '%s'", name);
     return EINVAL;
   }
   /* We've checked as much as we can for now, now go and attempt the change */
   WITH_TRANSACTION(trackdb_playlist_delete_tid(name, who, tid));
+  if(e == DB_NOTFOUND)
+    e = ENOENT;
   return e;
 }
 
@@ -445,13 +434,16 @@ static int trackdb_playlist_delete_tid(const char *name,
     return e;
   /* Check that modification is allowed */
   if(!(s = kvp_get(k, "sharing"))) {
-    error(0, "playlist '%s' has no 'sharing' key", name);
+    disorder_error(0, "playlist '%s' has no 'sharing' key", name);
     s = "private";
   }
   if(!playlist_may_write(name, who, s))
     return EACCES;
   /* Delete the playlist */
-  return trackdb_delkey(trackdb_playlistsdb, name, tid);
+  e = trackdb_delkey(trackdb_playlistsdb, name, tid);
+  if(!e)
+    eventlog("playlist_deleted", name, 0);
+  return e;
 }
 
 /*