chiark / gitweb /
Synchronize with DisOrder 4.1
authorRichard Kettlewell <rjk@greenend.org.uk>
Sat, 28 Jun 2008 15:16:46 +0000 (16:16 +0100)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sat, 28 Jun 2008 15:16:46 +0000 (16:16 +0100)
30 files changed:
CHANGES.html
README
README.developers
README.upgrades
cgi/Makefile.am
cgi/actions.c
cgi/cgimain.c
cgi/disorder-cgi.h
cgi/macros-disorder.c
configure.ac
debian/README.Debian
debian/changelog
debian/postinst.disorder-server
debian/rules
disobedience/choose.h
disobedience/control.c
disobedience/disobedience.h
disobedience/log.c
disobedience/menu.c
disobedience/properties.c
disobedience/queue.c
disobedience/recent.c
disobedience/users.c
lib/eclient.h
lib/trackdb.c
scripts/setup.in
server/server.c
server/speaker-alsa.c
templates/choose.tmpl
tests/Makefile.am

index 8a39719348e2496f1ba9c33660c08407faafc4f8..2a3c89421eed3974c11327b5b441ce4bec509181 100644 (file)
@@ -57,7 +57,71 @@ span.command {
 
 <p>This file documents recent user-visible changes to DisOrder.</p>
 
-<h2>Changes up to version 3.1</h2>
+<h2>Changes up to version 4.1</h2>
+
+<div class=section>
+
+  <h3>Disobedience</h3>
+  
+    <div class=section>
+  
+      <p>Disobedience has been largely rewritten:</p>
+
+      <ul>
+        
+        <li>All the tabs now use native GTK+ list/tree widgets, resulting in
+        greater speed in some cases and more consistency with other GTK+
+        applications.</li>
+
+        <li>You can now use type-ahead find in the choose tab.  The initiation
+        of a search is delayed slightly to avoid lots of updates when you're
+        half way through entering search terms.</li>
+
+        <li>The choose tab now shows track lengths.</li>
+    
+        <li>Many buttons are now more reliably made insensitive when they can't
+        be used.</li>
+
+        <li>You can now play tracks off the recent tab.</li>
+        
+      </ul>
+      
+      <p>Disobedience attempts to cope with servers from older versions, up to
+      a point, but this is not well tested and it's best to keep the server
+      fully up to date.</p>
+
+    </div>
+     
+  <h3>Server</h3>
+  
+    <div class=section>
+
+      <p>When a track shares a directory with its alias, the real track name is
+      now returned instead of the alias (the opposite way round to the previous
+      behaviour).</p>
+      
+    </div>
+</div>
+
+<h2>Changes up to version 4.0.2</h2>
+
+<div class=section>
+
+  <p>Corrected web browser linked from Disobedience.</p>
+  
+</div>
+
+<h2>Changes up to version 4.0.1</h2>
+
+<div class=section>
+
+  <p>Libtool and Automake now install the CGI correctly.  As part of this,
+  <tt>cgidir</tt> has been renamed to <tt>cgiexecdir</tt>.  The configure
+  script will report an error if you try to use the old name.</p>
+  
+</div>
+
+<h2>Changes up to version 4.0</h2>
 
 <div class=section>
 
diff --git a/README b/README
index acd06ee5b2ee690269849eaa0194b37259be5a8c..8cd2c35ff954953429e4b5a4ded972d666c9d057 100644 (file)
--- a/README
+++ b/README
@@ -34,9 +34,9 @@ Build dependencies:
   libao            0.8.6
   libasound        1.0.13
   libFLAC          1.1.2
-  GNU C            4.1.2
-  GNU Make         3.81
-  GNU Sed          4.1.5
+  GNU C            4.1.2               }
+  GNU Make         3.81                } Non-GNU versions will NOT work
+  GNU Sed          4.1.5               }
   Python           2.4.4               (optional)
   GTK+             2.8.20              (if you want the GTK+ client)
   GLIB             2.12.4              (if you want the GTK+ client)
@@ -91,7 +91,7 @@ platform, please get in touch.
    If configure cannot guess where your web server keeps its HTML documents and
    CGI programs, you may have to tell it, for instance:
 
-     ./configure cgidir=/whatever/cgi-bin httpdir=/whatever/htdocs
+     ./configure cgiexecdir=/whatever/cgi-bin httpdir=/whatever/htdocs
 
    See README.client for setting up a standalone client (or read the
    disobedience man page).
@@ -229,9 +229,13 @@ You need to configure a number of things to make this work:
 
      disorder setup-guest --no-online-registration
 
-3. Try it out.  You should be able to perform read-only operations straight
-   away, and after visiting the 'Login' page to authenticate, perform other
-   operations like adding a track to the queue.
+3. Try it out.  The url will be (something like):
+
+     http://localhost/cgi-bin/disorder
+
+   You should be able to perform read-only operations straight away, and after
+   visiting the 'Login' page to authenticate, perform other operations like
+   adding a track to the queue.
 
 4. If you run into problems, always look at the appropriate error log; the
    message you see in your web browser will usually not be sufficient to
index 5a5dd8908acba9035dc3aea11ccc4f3d2d9ffbd9..d0c1cdc1a8103e3bce9f5a946fd584fe42a08ce5 100644 (file)
@@ -14,6 +14,8 @@ Dependencies:
                     libao-dev libmad0-dev libasound2-dev libdb4.3-dev \
                     libflac-dev
 
+     (Use the bzr from backports, the one in etch is obsolete.)
+
    * On FreeBSD you'll need at least these packages:
         autotools
         bash
@@ -138,10 +140,7 @@ Web Interface:
      keep it that way.  Clever use of CSS is OK provided it works well on the
      mainstream browsers.
 
-   * I know that the web template syntax is rather nasty.  Perhaps it will be
-     improved in a future version.
-
-   * Update templates/help.html for any changes you make.
+   * Update templates/help.tmpl for any changes you make.
 
 Disobedience:
 
@@ -188,12 +187,9 @@ Code And Patches:
      (But if your new feature only makes sense on a given platform then
      obviously its new dependencies don't need to be available elsewhere.)
 
-   * GCC is stated as a dependency.  In fact the code is mostly standard C,
-     with C99 initializers, long long and possibly the occasional // comment as
-     the main departures from C89.  Additional GCCisms will be accepted if it's
-     impractical to avoid them.  At least one active user is still using GCC
-     2.95, so extensions that only appear in later versions are to be avoided
-     for the time being.
+   * GCCisms such as typeof and C99isms such as mixed declarations and named
+     structure initializers are used; the configure script asks for -std=gnu99
+     by default.  Some supported platforms are still on GCC 4.0.
 
    * Please submit patches either using 'diff -u', or by publishing a bzr
      branch somewhere I can get at it.
index 63b7852e09dfb7fbc6bfd3666b6c3cbac3cb1b51..185ea4fe05af86f5bcc783d29691a749a74f0b16 100644 (file)
@@ -17,7 +17,7 @@ all 1.1.x versions.
 
 If you install from .deb files then much of this work is automated.
 
-* 3.0 -> 3.1
+* 3.0 -> 4.0
 
 If you customized any of the templates, you will pretty much have to start from
 scratch as the web interface has been rewritten.  See disorder.cgi(8) for a
index e2fa8955e7061ac90b3cec5274dea8d3d83edf9a..72ea195a8acfa55827498d2276ab9b351db6464f 100644 (file)
@@ -18,7 +18,7 @@
 # USA
 #
 
-cgi_PROGRAMS=disorder
+cgiexec_PROGRAMS=disorder
 
 AM_CPPFLAGS=-I${top_srcdir}/lib -I../lib
 
@@ -28,3 +28,6 @@ disorder_LDADD=../lib/libdisorder.a \
        $(LIBPCRE) $(LIBGCRYPT) $(LIBDL) $(LIBDB)
 disorder_LDFLAGS=-export-dynamic
 disorder_DEPENDENCIES=../lib/libdisorder.a
+
+install-exec-hook:
+       $(LIBTOOL) --mode=finish $(DESTDIR)$(cgiexecdir)
index 611e9a4a26990139a40e979c414478bc06ceec36..4728503de41bd4f03507ba7d3ab0763a9c5d2ec2 100644 (file)
@@ -240,7 +240,7 @@ static void act_play(void) {
   const char *track, *dir;
   char **tracks;
   int ntracks, n;
-  struct dcgi_entry *e;
+  struct tracksort_data *tsd;
   
   if(dcgi_client) {
     if((track = cgi_get("track"))) {
@@ -248,16 +248,9 @@ static void act_play(void) {
     } else if((dir = cgi_get("dir"))) {
       if(disorder_files(dcgi_client, dir, 0, &tracks, &ntracks))
         ntracks = 0;
-      /* TODO use tracksort_init */
-      e = xmalloc(ntracks * sizeof (struct dcgi_entry));
-      for(n = 0; n < ntracks; ++n) {
-        e[n].track = tracks[n];
-        e[n].sort = trackname_transform("track", tracks[n], "sort");
-        e[n].display = trackname_transform("track", tracks[n], "display");
-      }
-      qsort(e, ntracks, sizeof (struct dcgi_entry), dcgi_compare_entry);
+      tsd = tracksort_init(ntracks, tracks, "track");
       for(n = 0; n < ntracks; ++n)
-        disorder_play(dcgi_client, e[n].track);
+        disorder_play(dcgi_client, tsd[n].track);
     }
   }
   redirect(0);
@@ -435,8 +428,7 @@ static void act_register(void) {
   }
   /* We could well do better address validation but for now we'll just do the
    * minimum */
-   /* TODO use email_valid() */
-  if(!strchr(email, '@')) {
+  if(!email_valid(email)) {
     login_error("bademail");
     return;
   }
@@ -525,8 +517,7 @@ static void act_edituser(void) {
     }
   } else
     password = password2 = 0;
-  /* TODO use email_valid() */
-  if(email && !strchr(email, '@')) {
+  if(email && !email_valid(email)) {
     login_error("bademail");
     return;
   }
@@ -745,7 +736,7 @@ void dcgi_expand(const char *name, int header) {
   if(!(found = mx_find(p, 0/*report*/)))
     fatal(errno, "cannot find %s", p);
   if(header) {
-    if(printf("Content-Type: text/html\n"
+    if(printf("Content-Type: text/html; charset=UTF-8\n"
               "%s\n"
               "\n", dcgi_cookie_header()) < 0)
       fatal(errno, "error writing to stdout");
index 253f82eac02a61a0edd69ab93eeb3192ac28c194..e96bbab77b2235ea61593496e0aa783e916f8cda 100644 (file)
@@ -33,7 +33,7 @@ int main(int argc, char **argv) {
   /* TODO we could make disorder/ACTION equivalent to disorder?action=ACTION */
   if(getenv("PATH_INFO")) {
     /* TODO it might be nice to link back to the right place... */
-    printf("Content-Type: text/html\n");
+    printf("Content-Type: text/html; charset=UTF-8\n");
     printf("Status: 404\n");
     printf("\n");
     printf("<p>Sorry, PATH_INFO not supported.</p>\n");
index 3f19fe2cd6f5c89576ec0f9a3680206dbf36cf92..3cabe9b13f806658b71725be447757760eb006c5 100644 (file)
@@ -60,16 +60,6 @@ extern char *dcgi_cookie;
 extern const char *dcgi_error_string;
 extern const char *dcgi_status_string;
 
-/** @brief Entry in a list of tracks or directories */
-struct dcgi_entry {
-  /** @brief Track name */
-  const char *track;
-  /** @brief Sort key */
-  const char *sort;
-  /** @brief Display key */
-  const char *display;
-};
-
 /** @brief Compare two @ref entry objects */
 int dcgi_compare_entry(const void *a, const void *b);
 
index 8e891d6afcb2b3c8a066dc6f0171db783727fa08..5ea74138275f3ad77f2e078f9bdafb05f9fd2e51 100644 (file)
@@ -814,15 +814,6 @@ static int exp_image(int attribute((unused)) nargs,
   return sink_writes(output, cgi_sgmlquote(url)) < 0 ? -1 : 0;
 }
 
-/** @brief Compare two @ref entry objects */
-int dcgi_compare_entry(const void *a, const void *b) {
-  const struct dcgi_entry *ea = a, *eb = b;
-
-  return compare_tracks(ea->sort, eb->sort,
-                       ea->display, eb->display,
-                       ea->track, eb->track);
-}
-
 /** @brief Implementation of exp_tracks() and exp_dirs() */
 static int exp__files_dirs(int nargs,
                            const struct mx_node **args,
@@ -837,7 +828,7 @@ static int exp__files_dirs(int nargs,
   char **tracks, *dir, *re;
   int n, ntracks, rc;
   const struct mx_node *m;
-  struct dcgi_entry *e;
+  struct tracksort_data *tsd;
 
   if((rc = mx_expandstr(args[0], &dir, u, "argument #0 (DIR)")))
     return rc;
@@ -855,25 +846,18 @@ static int exp__files_dirs(int nargs,
   if(fn(dcgi_client, dir, re, &tracks, &ntracks))
     return 0;
   /* Sort it.  NB trackname_transform() does not go to the server. */
-  /* TODO use tracksort_init */
-  e = xcalloc(ntracks, sizeof *e);
-  for(n = 0; n < ntracks; ++n) {
-    e[n].track = tracks[n];
-    e[n].sort = trackname_transform(type, tracks[n], "sort");
-    e[n].display = trackname_transform(type, tracks[n], "display");
-  }
-  qsort(e, ntracks, sizeof (struct dcgi_entry), dcgi_compare_entry);
+  tsd = tracksort_init(ntracks, tracks, type);
   /* Expand the subsiduary templates.  We chuck in @sort and @display because
    * it is particularly easy to do so. */
   for(n = 0; n < ntracks; ++n)
     if((rc = mx_expand(mx_rewritel(m,
                                    "index", make_index(n),
                                    "parity", n % 2 ? "odd" : "even",
-                                   "track", e[n].track,
+                                   "track", tsd[n].track,
                                    "first", n == 0 ? "true" : "false",
                                    "last", n + 1 == ntracks ? "false" : "true",
-                                   "sort", e[n].sort,
-                                   "display", e[n].display,
+                                   "sort", tsd[n].sort,
+                                   "display", tsd[n].display,
                                    (char *)0),
                        output, u)))
       return rc;
index 44ca4b2683252fa622181ce6ba255f4116c38138..9e9a7c75d7d5162d90c7f1ff9baeecc8e2aa260b 100644 (file)
@@ -20,9 +20,9 @@
 # USA
 #
 
-AC_INIT([disorder], [3.0+], [richard+disorder@sfere.greenend.org.uk])
+AC_INIT([disorder], [4.1], [richard+disorder@sfere.greenend.org.uk])
 AC_CONFIG_AUX_DIR([config.aux])
-AM_INIT_AUTOMAKE(disorder, [3.0+])
+AM_INIT_AUTOMAKE(disorder, [4.1])
 AC_CONFIG_SRCDIR([server/disorderd.c])
 AM_CONFIG_HEADER([config.h])
 
@@ -226,14 +226,18 @@ if test $want_cgi = yes; then
         fi
       done
     ])
-    if test "$rjk_cv_cgidir" = "not found"; then
+    if test "$rjk_cv_cgiexecdir" = "not found"; then
       AC_MSG_ERROR([cannot identify httpd documentroot.  Set httpdir on configure command line])
     fi
     httpdir="$rjk_cv_httpdir"
   fi
-  if test -z "$cgidir"; then
-    AC_CACHE_CHECK([for CGI directory],[rjk_cv_cgidir],[
-      rjk_cv_cgidir="not found"
+  if test ! -z "$cgidir"; then
+    # This is a bit harsh but should stop any disasters
+    AC_MSG_ERROR([cgidir has been renamed to cgiexecdir])
+  fi
+  if test -z "$cgiexecdir"; then
+    AC_CACHE_CHECK([for CGI directory],[rjk_cv_cgiexecdir],[
+      rjk_cv_cgiexecdir="not found"
       for dir in /usr/lib/cgi-bin \
                  /Library/WebServer/CGI-Executables \
                  /srv/www/cgi-bin \
@@ -244,18 +248,18 @@ if test $want_cgi = yes; then
                  /usr/local/www/cgi-bin \
                  /usr/local/www/*/cgi-bin; do
         if test -d "$dir"; then
-          rjk_cv_cgidir="$dir"
+          rjk_cv_cgiexecdir="$dir"
           break
         fi
       done
     ])
-    if test "$rjk_cv_cgidir" = "not found"; then
-      AC_MSG_ERROR([cannot identify CGI install directory.  Set cgidir on configure command line])
+    if test "$rjk_cv_cgiexecdir" = "not found"; then
+      AC_MSG_ERROR([cannot identify CGI install directory.  Set cgiexecdir on configure command line])
     fi
-    cgidir="$rjk_cv_cgidir"
+    cgiexecdir="$rjk_cv_cgiexecdir"
   fi
 fi
-AC_ARG_VAR([cgidir], [location of cgi-bin directory, e.g. /usr/lib/cgi-bin])
+AC_ARG_VAR([cgiexecdir], [location of cgi-bin directory, e.g. /usr/lib/cgi-bin])
 AC_ARG_VAR([httpdir], [location of http document root, e.g. /var/www/htdocs])
 if test -z "$pkghttpdir"; then
   pkghttpdir='$(httpdir)/disorder'
index a670319475af4e8f9f7680f667780ada49153dc8..e7bf6f7b83007c1b1f1b11ba23e4c920fdff5f72 100644 (file)
@@ -1,8 +1,13 @@
 Debian package for DisOrder
 ===========================
 
-The web interface should now start working automatically.  Test it at
-http://YOURHOSTNAME/cgi-bin/disorder/disorder If it doesn't work,
-always look at the web server error log.
+The web interface should now start working automatically.  Test it at:
 
- -- Richard Kettlewell <rjk@greenend.org.uk>, Sat, 29 Mar 2008 16:20:20 +0000
+    http://YOURHOSTNAME/cgi-bin/disorder
+
+You will need to create yourself a user.
+
+If it doesn't work, always look at the web server error log and at
+/var/log/daemon.log.
+
+ -- Richard Kettlewell <rjk@greenend.org.uk>, Sun, 15 Jun 2008 12:15:50 +0100
index ab46f89e4258cd9d57f95da379d99594d04fb3f7..7799d2c0bd58420553fef7105d3607c3510128db 100644 (file)
@@ -1,8 +1,29 @@
-disorder (3.0.99.dev) unstable; urgency=low
+disorder (4.1) unstable; urgency=low
 
-  * Bodge version number
+  * DisOrder 4.1
 
- -- Richard Kettlewell <rjk@greenend.org.uk>  Sun, 18 May 2008 21:30:14 +0100
+ -- Richard Kettlewell <rjk@greenend.org.uk>  Sat, 28 Jun 2008 14:35:20 +0100
+
+disorder (4.0.2) unstable; urgency=low
+
+  * Correct web browser linkage from Disobedience.
+
+ -- Richard Kettlewell <rjk@greenend.org.uk>  Sun, 15 Jun 2008 14:44:22 +0100
+
+disorder (4.0.1) unstable; urgency=low
+
+  * Version 4.0.1
+  * Update READMEs
+  * Make group modification noisier
+  * Makefile fiddling for Automake/Libtool's benefit
+
+ -- Richard Kettlewell <richard@dekabrach.anjou.terraraq.org.uk>  Sun, 15 Jun 2008 12:22:52 +0100
+
+disorder (4.0) unstable; urgency=low
+
+  * Version 4.0
+
+ -- Richard Kettlewell <rjk@greenend.org.uk>  Sun,  8 Jun 2008 14:44:05 +0100
 
 disorder (3.0) unstable; urgency=low
 
index 9e516489741f71615102798b518d192e3884b522..458cc791c996fba490bf95ae979459b379f52ab5 100755 (executable)
@@ -28,7 +28,7 @@ add_jukebox_user() {
     --no-create-home jukebox
   # If it happens that there's no audio group we don't fail; perhaps only
   # network play was required.
-  adduser --quiet jukebox audio || true
+  adduser jukebox audio || true
 }
 
 configure_init_d() {
index 000be812d5b9e782db6bc4641d6c3e750b85bbab..0c14444f4670b20317517f82eecf223848a2f589 100755 (executable)
 # USA
 #
 
-cgidir=/usr/lib/cgi-bin
+cgiexecdir=/usr/lib/cgi-bin
 httpdir=/var/www
 browser=x-www-browser
 
 # Options to configure.  This can be overridden by the caller if necessary.
-CONFIGURE=--prefix=/usr --sysconfdir=/etc --localstatedir=/var/lib --mandir=/usr/share/man --with-browser=$browser cgidir="${cgidir}" httpdir="${httpdir}"
+CONFIGURE=--prefix=/usr --sysconfdir=/etc --localstatedir=/var/lib --mandir=/usr/share/man --with-browser=${browser} cgiexecdir="${cgiexecdir}" httpdir="${httpdir}"
 
 # Set DEB_BUILD_OPTIONS=noopt to produce a non-optimized build.
 ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
@@ -149,7 +149,7 @@ pkg-disorder-server: build
        rm -f debian/disorder-server/usr/share/man/man5/disorder_protocol.5
        $(MKDIR) debian/disorder-server/etc/disorder
        $(MKDIR) debian/disorder-server/etc/init.d
-       $(MKDIR) debian/disorder-server/usr/lib/cgi-bin
+       $(MKDIR) debian/disorder-server${cgiexecdir}
        $(MKDIR) debian/disorder-server/var/lib/disorder
        $(INSTALL_SCRIPT) examples/disorder.init \
                debian/disorder-server/etc/init.d/disorder
@@ -160,9 +160,9 @@ pkg-disorder-server: build
        $(INSTALL_DATA) debian/etc.disorder.options.user \
                debian/disorder-server/etc/disorder/options.user
        $(LIBTOOL) --mode=install $(INSTALL_PROGRAM) cgi/disorder \
-               $(shell pwd)/debian/disorder-server/usr/lib/cgi-bin/disorder
+               $(shell pwd)/debian/disorder-server${cgiexecdir}/disorder
        dpkg-shlibdeps -Tdebian/substvars.disorder-server \
-               debian/disorder-server/usr/lib/cgi-bin/disorder \
+               debian/disorder-server${cgiexecdir}/disorder \
                debian/disorder-server/usr/sbin/* \
                debian/disorder-server/usr/lib/disorder/*.so*
        rm -rf debian/disorder-server/usr/share/doc/disorder-server
index 1a9bf10e5d41658a7ed1e0ab03d874232fd5e682..c101e6bcf303aaeacddda06ff53bca691eee7897 100644 (file)
@@ -62,7 +62,7 @@ gboolean choose_button_event(GtkWidget *widget,
                              GdkEventButton *event,
                              gpointer user_data);
 void choose_play_completed(void attribute((unused)) *v,
-                           const char *error);
+                           const char *err);
 char *choose_get_track(GtkTreeIter *iter);
 char *choose_get_sort(GtkTreeIter *iter);
 char *choose_get_display(GtkTreeIter *iter);
index ce39124182737ef025bdad99d83d97435479e825..6ed9fb3564db522c00afc5e0e5f3ceb00df196e3 100644 (file)
@@ -275,6 +275,7 @@ GtkWidget *control_widget(void) {
     char **events = split(icons[n].events, 0, 0, 0, 0);
     while(*events)
       event_register(*events++, icon_changed, &icons[n]);
+    event_register("connected-changed", icon_changed, &icons[n]);
   }
   /* create the adjustments for the volume control */
   volume_adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, goesupto,
index f709a27fb31e4cbfb269fb54f1ea1df6525cf0fb..1f631088e4503a8969bf90c8ccb659255b62adf5 100644 (file)
@@ -232,7 +232,7 @@ void choose_update(void);
 /* Called when we think the choose tree might need updating */
 
 void play_completed(void *v,
-                    const char *error);
+                    const char *err);
 
 /* Login details */
 
index a4db0cf73eb743894998b791a20113f297fe94fd..6779cbd3cc4c081a64ba8fb4a8c9693d734ef0a1 100644 (file)
@@ -151,6 +151,7 @@ static const struct {
   { DISORDER_RANDOM_ENABLED, "random-changed" },
   { DISORDER_TRACK_PAUSED, "pause-changed" },
   { DISORDER_PLAYING, "playing-changed" },
+  { DISORDER_CONNECTED, "connected-changed" },
 };
 #define NSTATE_EVENTS (sizeof state_events / sizeof *state_events)
 
index 868c9dce8f2358f2a668d359bc38a8f747d41efc..142857c742295197791a7420ad48f6edbf0fb953 100644 (file)
@@ -31,7 +31,7 @@ static GtkWidget *properties_widget;
 GtkItemFactory *mainmenufactory;
 
 static void about_popup_got_version(void *v,
-                                    const char *error,
+                                    const char *err,
                                     const char *value);
 
 /** @brief Called when the quit option is activated
index 8b461090999fd26934bd456917f33933a269593f..16ee97cbaaf59d352ac599757666f273bbc50dcd 100644 (file)
@@ -32,7 +32,7 @@ static void completed_namepart(struct prefdata *f);
 static const char *get_edited_namepart(struct prefdata *f);
 static void set_edited_namepart(struct prefdata *f, const char *value);
 static void set_namepart(struct prefdata *f, const char *value);
-static void set_namepart_completed(void *v, const char *error);
+static void set_namepart_completed(void *v, const char *err);
 
 static void kickoff_string(struct prefdata *f);
 static void completed_string(struct prefdata *f);
@@ -46,7 +46,7 @@ static const char *get_edited_boolean(struct prefdata *f);
 static void set_edited_boolean(struct prefdata *f, const char *value);
 static void set_boolean(struct prefdata *f, const char *value);
 
-static void prefdata_completed(void *v, const char *error, const char *value);
+static void prefdata_completed(void *v, const char *err, const char *value);
 static void prefdata_onerror(struct callbackdata *cbd,
                              int code,
                              const char *msg);
index 7dbe563cb4cb35153e0fc25aea5e7173f8dcce0f..d69b34dd3e444218af2ce0936c8b9b6b9886fe26 100644 (file)
@@ -32,10 +32,10 @@ struct queue_entry *playing_track;
 time_t last_playing;
 
 static void queue_completed(void *v,
-                            const char *error,
+                            const char *err,
                             struct queue_entry *q);
 static void playing_completed(void *v,
-                              const char *error,
+                              const char *err,
                               struct queue_entry *q);
 
 /** @brief Called when either the actual queue or the playing track change */
index 109ee5ab3579daf95e5e0fda498e13aa959d849a..788b824a805a406360da2800b89622708b504f42 100644 (file)
@@ -78,6 +78,7 @@ static const struct queue_column recent_columns[] = {
 /** @brief Pop-up menu for recently played list */
 static struct menuitem recent_menuitems[] = {
   { "Track properties", ql_properties_activate, ql_properties_sensitive,0, 0 },
+  { "Play track", ql_play_activate, ql_play_sensitive, 0, 0 },
   { "Select all tracks", ql_selectall_activate, ql_selectall_sensitive, 0, 0 },
   { "Deselect all tracks", ql_selectnone_activate, ql_selectnone_sensitive, 0, 0 },
 };
index 60bdef64ba719d6287043901b8c4fe52716c4063..a835ced63f0d26115f958685796dbcc8b0cadb6a 100644 (file)
@@ -645,6 +645,8 @@ static GtkWidget *users_make_reporter() {
     users_reporter = gtk_label_new("");
     gtk_label_set_ellipsize(GTK_LABEL(users_reporter), PANGO_ELLIPSIZE_END);
     gtk_misc_set_alignment(GTK_MISC(users_reporter), 0.99, 0);
+    g_signal_connect(users_reporter, "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), &users_reporter);
   }
   return users_reporter;
 }
@@ -681,6 +683,9 @@ void manage_users(void) {
     gtk_window_present(GTK_WINDOW(users_window));
     return;
   }
+  /* Destroy old widgets */
+  if(users_reporter)
+    gtk_widget_destroy(users_reporter);
   /* Create the window */
   users_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
   gtk_widget_set_style(users_window, tool_style);
index be5730cb5eb19367bf71f9ce361de320f0c1dacb..efb6b2d87936d89303557dcc7232bcc89cf29878 100644 (file)
@@ -209,14 +209,14 @@ struct sink;
 
 /** @brief Trivial completion callback
  * @param v User data
- * @param error Error string or NULL on succes
+ * @param err Error string or NULL on succes
  */
 typedef void disorder_eclient_no_response(void *v,
-                                          const char *error);
+                                          const char *err);
 
 /** @brief String result completion callback
  * @param v User data
- * @param error Error string or NULL on succes
+ * @param err Error string or NULL on succes
  * @param value Result or NULL
  *
  * @p error will be NULL on success.  In this case @p value will be the result
@@ -226,12 +226,12 @@ typedef void disorder_eclient_no_response(void *v,
  * @p error will be non-NULL on failure.  In this case @p value is always NULL.
  */
 typedef void disorder_eclient_string_response(void *v,
-                                              const char *error,
+                                              const char *err,
                                               const char *value);
 
 /** @brief String result completion callback
  * @param v User data
- * @param error Error string or NULL on succes
+ * @param err Error string or NULL on succes
  * @param value Result or 0
  *
  * @p error will be NULL on success.  In this case @p value will be the result.
@@ -239,11 +239,11 @@ typedef void disorder_eclient_string_response(void *v,
  * @p error will be non-NULL on failure.  In this case @p value is always 0.
  */
 typedef void disorder_eclient_integer_response(void *v,
-                                               const char *error,
+                                               const char *err,
                                                long value);
 /** @brief Volume completion callback
  * @param v User data
- * @param error Error string or NULL on success
+ * @param err Error string or NULL on success
  * @param l Left channel volume
  * @param r Right channel volume
  *
@@ -254,12 +254,12 @@ typedef void disorder_eclient_integer_response(void *v,
  * 0.
  */
 typedef void disorder_eclient_volume_response(void *v,
-                                              const char *error,
+                                              const char *err,
                                               int l, int r);
 
 /** @brief Queue request completion callback
  * @param v User data
- * @param error Error string or NULL on success
+ * @param err Error string or NULL on success
  * @param q Head of queue data list
  *
  * @p error will be NULL on success.  In this case @p q will be the (head of
@@ -270,12 +270,12 @@ typedef void disorder_eclient_volume_response(void *v,
  * be ignored in the error case.
  */
 typedef void disorder_eclient_queue_response(void *v,
-                                             const char *error,
+                                             const char *err,
                                              struct queue_entry *q);
 
 /** @brief List request completion callback
  * @param v User data
- * @param error Error string or NULL on success
+ * @param err Error string or NULL on success
  * @param nvec Number of elements in response list
  * @param vec Pointer to response list
  *
@@ -286,7 +286,7 @@ typedef void disorder_eclient_queue_response(void *v,
  * be 0 and NULL.
  */
 typedef void disorder_eclient_list_response(void *v,
-                                            const char *error,
+                                            const char *err,
                                             int nvec, char **vec);
 
 disorder_eclient *disorder_eclient_new(const disorder_eclient_callbacks *cb,
index 42871046a4a68aea6a5dfa1730615bacb6354a17..718970a417c0c976da070a36b890f4cc6d4e6db6 100644 (file)
@@ -60,6 +60,7 @@
 #include "unicode.h"
 #include "unidata.h"
 #include "base64.h"
+#include "sendmail.h"
 
 #define RESCAN "disorder-rescan"
 #define DEADLOCK "disorder-deadlock"
@@ -508,8 +509,13 @@ void trackdb_close(void) {
 
 /* generic db routines *******************************************************/
 
-/* fetch and decode a database entry.  Returns 0, DB_NOTFOUND or
- * DB_LOCK_DEADLOCK. */
+/** @brief Fetch and decode a database entry
+ * @param db Database
+ * @param track Track name
+ * @param kp Where to put decoded list (or NULL if you don't care)
+ * @param tid Owning transaction
+ * @return 0, @c DB_NOTFOUND or @c DB_LOCK_DEADLOCK
+ */
 int trackdb_getdata(DB *db,
                     const char *track,
                     struct kvp **kp,
@@ -520,10 +526,12 @@ int trackdb_getdata(DB *db,
   switch(err = db->get(db, tid, make_key(&key, track),
                        prepare_data(&data), 0)) {
   case 0:
-    *kp = kvp_urldecode(data.data, data.size);
+    if(kp)
+      *kp = kvp_urldecode(data.data, data.size);
     return 0;
   case DB_NOTFOUND:
-    *kp = 0;
+    if(kp)
+      *kp = 0;
     return err;
   case DB_LOCK_DEADLOCK:
     error(0, "error querying database: %s", db_strerror(err));
@@ -1852,7 +1860,7 @@ static int do_list(struct vector *v, const char *dir,
   char *ptr;
   int err;
   size_t l, last_dir_len = 0;
-  char *last_dir = 0, *track, *alias;
+  char *last_dir = 0, *track;
   struct kvp *p;
 
   dl = strlen(dir);
@@ -1885,12 +1893,35 @@ static int do_list(struct vector *v, const char *dir,
         if((err = trackdb_getdata(trackdb_prefsdb,
                                   track, &p, tid)) == DB_LOCK_DEADLOCK)
           goto deadlocked;
+        /* There's an awkward question here...
+         *
+         * If a track shares a directory with its alias then we could
+         * do one of three things:
+         * - report both.  Looks ridiculuous in most UIs.
+         * - report just the alias.  Remarkably inconvenient to write
+         *   UI code for!
+         * - report just the real name.  Ugly if the UI doesn't prettify
+         *   names via the name parts.
+         */
+#if 1
+        /* If this file is an alias for a track in the same directory then we
+         * skip it */
+        struct kvp *t = kvp_urldecode(d.data, d.size);
+        const char *alias_target = kvp_get(t, "_alias_for");
+        if(!(alias_target
+             && !strcmp(d_dirname(alias_target),
+                        d_dirname(track))))
+         if(track_matches(dl, k.data, k.size, re))
+           vector_append(v, track);
+#else
        /* if this file has an alias in the same directory then we skip it */
+           char *alias;
         if((err = compute_alias(&alias, track, p, tid)))
           goto deadlocked;
         if(!(alias && !strcmp(d_dirname(alias), d_dirname(track))))
          if(track_matches(dl, k.data, k.size, re))
            vector_append(v, track);
+#endif
       }
     }
     err = cursor->c_get(cursor, &k, &d, DB_NEXT);
@@ -2383,9 +2414,6 @@ char **trackdb_new(int *ntracksp,
  * @return null-terminated array of track names, or NULL on deadlock
  *
  * The most recently added track is first in the array.
- *
- * TODO: exclude tracks that have been deleted again.
- *
  */
 static char **trackdb_new_tid(int *ntracksp,
                               int maxtracks,
@@ -2394,12 +2422,24 @@ static char **trackdb_new_tid(int *ntracksp,
   DBT k, d;
   int err = 0;
   struct vector tracks[1];
+  hash *h = hash_new(1);
 
   vector_init(tracks);
   c = trackdb_opencursor(trackdb_noticeddb, tid);
   while((maxtracks <= 0 || tracks->nvec < maxtracks)
-        && !(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_PREV)))
-    vector_append(tracks, xstrndup(d.data, d.size));
+        && !(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_PREV))) {
+    char *const track = xstrndup(d.data, d.size);
+    /* Don't add any track more than once */
+    if(hash_add(h, track, "", HASH_INSERT))
+      continue;
+    /* See if the track still exists */
+    err = trackdb_getdata(trackdb_tracksdb, track, NULL/*kp*/, tid);
+    if(err == DB_NOTFOUND)
+      continue;                         /* It doesn't, skip it */
+    if(err == DB_LOCK_DEADLOCK)
+      break;                            /* Doh */
+    vector_append(tracks, track);
+  }
   switch(err) {
   case 0:                               /* hit maxtracks */
   case DB_NOTFOUND:                     /* ran out of tracks */
@@ -2761,8 +2801,8 @@ int trackdb_edituserinfo(const char *user,
     }
   } else if(!strcmp(key, "email")) {
     if(*value) {
-      if(!strchr(value, '@')) {
-        error(0, "invalid email address '%s' for user '%s'", user, value);
+      if(!email_valid(value)) {
+        error(0, "invalid email address '%s' for user '%s'", value, user);
         return -1;
       }
     } else
index 48f87c2c5ac3a7362a04ccca1d9ebc12d27413fb..68d9a4a0a4271b6f5eef3774565e6a2b9988cfb0 100755 (executable)
@@ -151,23 +151,31 @@ if [ -z "$roots" ]; then
     echo "(enter one or more directories separated by spaces)"
     read -r roots
     ok=true
+    anyroots=false
     for root in $roots; do
       if [ ! -d $root ]; then
        echo "'$root' does not exist"
        ok=false
+      else
+        anyroots=true
       fi
     done
-    if $ok; then
+    if $anyroots && $ok; then
       break
     fi
   done
 fi
 
 if [ -z "$encoding" ]; then
-  echo 
-  echo "What filesystem encoding should I assume for track names?"
-  echo "(e.g. UTF-8, ISO-8859-1, ...)"
-  read -r encoding
+  while :; do
+    echo 
+    echo "What filesystem encoding should I assume for track names?"
+    echo "(e.g. UTF-8, ISO-8859-1, ...)"
+    read -r encoding
+    if [ ! -z "$encoding" ]; then
+      break
+    fi
+  done
 fi
 
 if [ -z "$port" ]; then
@@ -180,7 +188,7 @@ if [ -z "$port" ]; then
     none )
       break
       ;;
-    [^0-9] )
+    [^0-9] | "" )
       echo "'$port' is not a valid port number"
       continue
       ;;
@@ -225,7 +233,7 @@ if [ "x$play" = xnetwork ]; then
       none )
        break
        ;;
-      [^0-9] )
+      [^0-9] | "" )
        echo "'$mcast_port' is not a valid port number"
        continue
        ;;
@@ -269,7 +277,7 @@ fi
 echo
 echo "Proposed DisOrder setup:"
 echo " Music directory:       $roots"
-if [ $port = none ]; then
+if [ "$port" = none ]; then
   echo " Do not listen on a TCP port"
 else
   echo " TCP port to listen on: $port"
index ad3a7ab9e773663ad6989fc883fe03339fa68426..c4c889a448f909d6b9bcff26dc14d90d6760735b 100644 (file)
@@ -1426,9 +1426,8 @@ static int c_reminder(struct conn *c,
     sink_writes(ev_writer_sink(c->w), "550 Cannot send a reminder email\n");
     return 1;
   }
-  /* TODO use email_valid() */
   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;
index 41e53b24b971eaa211ff8753399a1d2610becc77..21a9a75889e0835a312475d030d26b58a06d1b6d 100644 (file)
@@ -49,7 +49,6 @@ static void alsa_init(void) {
 static void log_params(snd_pcm_hw_params_t *hwparams,
                        snd_pcm_sw_params_t *swparams) {
   snd_pcm_uframes_t f;
-  unsigned u;
 
   return;                               /* too verbose */
   if(hwparams) {
@@ -60,14 +59,22 @@ static void log_params(snd_pcm_hw_params_t *hwparams,
     info("sw silence_size=%lu", (unsigned long)f);
     snd_pcm_sw_params_get_silence_threshold(swparams, &f);
     info("sw silence_threshold=%lu", (unsigned long)f);
-    snd_pcm_sw_params_get_sleep_min(swparams, &u);
-    info("sw sleep_min=%lu", (unsigned long)u);
+#if HAVE_SND_PCM_SW_PARAMS_GET_SLEEP_MIN
+    {
+      unsigned u;
+
+      snd_pcm_sw_params_get_sleep_min(swparams, &u);
+      info("sw sleep_min=%lu", (unsigned long)u);
+    }
+#endif
     snd_pcm_sw_params_get_start_threshold(swparams, &f);
     info("sw start_threshold=%lu", (unsigned long)f);
     snd_pcm_sw_params_get_stop_threshold(swparams, &f);
     info("sw stop_threshold=%lu", (unsigned long)f);
+#if HAVE_SND_PCM_SW_PARAMS_GET_XFER_ALIGN
     snd_pcm_sw_params_get_xfer_align(swparams, &f);
     info("sw xfer_align=%lu", (unsigned long)f);
+#endif
   }
 }
 
index 4c2547bffbb9410d254266a506d918bf86d1f6fd..11d0e9585ef305f467c1b830001e2eb99ccd22b3 100644 (file)
@@ -167,7 +167,7 @@ USA
       <p class=entry>
        <a href="@url?action=choose&#38;dir=@urlquote{@track}">
         <img class=button src="@image{directory}" alt="">
-        @display
+        @quote{@display}
        </a>
       </p>}
     </div>
@@ -191,7 +191,7 @@ USA
        }@#
        <a href="@url?action=play&#38;track=@urlquote{@track}&#38;back=@urlquote{@thisurl}"
           title="@label{choose.play}">
-        @display
+        @quote{@display}
        </a>
        @if{@eq{@trackstate{@track}}{playing}}
           {[<b>playing</b>]}
index 9358b196b8609e0d8ce3ce8f1c3b7aa3a0f8ff7c..e8e8507dedbc04e1a4854ea76fc2ac1a01078938 100644 (file)
@@ -22,8 +22,8 @@ noinst_PROGRAMS=disorder-udplog
 
 AM_CPPFLAGS=-I${top_srcdir}/lib -I../lib
 
-disorder_udplog_SOURCES=udplog.c
-disorder_udplog_LDADD=$(LIBOBJS) ../lib/libdisorder.a
+disorder_udplog_SOURCES=udplog.c ../lib/memgc.c
+disorder_udplog_LDADD=$(LIBOBJS) ../lib/libdisorder.a $(LIBGC)
 disorder_udplog_DEPENDENCIES=../lib/libdisorder.a
 
 TESTS=cookie.py dbversion.py dump.py files.py play.py queue.py \