chiark / gitweb /
c82bf10d44ab6c5cd2d382020c9eb8e7d2d5e152
[disorder] / lib / trackdb.c
1 /*
2  * This file is part of DisOrder
3  * Copyright (C) 2005-2008 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file lib/trackdb.c
19  * @brief Track database
20  *
21  * This file is getting in desparate need of splitting up...
22  */
23
24 #include "common.h"
25
26 #include <db.h>
27 #include <sys/socket.h>
28 #include <pcre.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <stddef.h>
32 #include <sys/time.h>
33 #include <sys/resource.h>
34 #include <time.h>
35 #include <arpa/inet.h>
36 #include <dirent.h>
37 #include <sys/stat.h>
38 #include <gcrypt.h>
39
40 #include "event.h"
41 #include "mem.h"
42 #include "kvp.h"
43 #include "log.h"
44 #include "vector.h"
45 #include "rights.h"
46 #include "trackdb.h"
47 #include "configuration.h"
48 #include "syscalls.h"
49 #include "wstat.h"
50 #include "printf.h"
51 #include "filepart.h"
52 #include "trackname.h"
53 #include "trackdb-int.h"
54 #include "logfd.h"
55 #include "cache.h"
56 #include "eventlog.h"
57 #include "hash.h"
58 #include "unicode.h"
59 #include "unidata.h"
60 #include "base64.h"
61 #include "sendmail.h"
62
63 #define RESCAN "disorder-rescan"
64 #define DEADLOCK "disorder-deadlock"
65
66 static const char *getpart(const char *track,
67                            const char *context,
68                            const char *part,
69                            const struct kvp *p,
70                            int *used_db);
71 static char **trackdb_new_tid(int *ntracksp,
72                               int maxtracks,
73                               DB_TXN *tid);
74 static int trackdb_expire_noticed_tid(time_t earliest, DB_TXN *tid);
75 static char *normalize_tag(const char *s, size_t ns);
76
77 const struct cache_type cache_files_type = { 86400 };
78 unsigned long cache_files_hits, cache_files_misses;
79
80 /** @brief Set by trackdb_open() */
81 int trackdb_existing_database;
82
83 /* setup and teardown ********************************************************/
84
85 static const char *home;                /* home had better not change */
86 DB_ENV *trackdb_env;                    /* db environment */
87
88 /** @brief The tracks database
89  * - Keys are UTF-8(NFC(unicode(path name)))
90  * - Values are encoded key-value pairs
91  * - Data is reconstructable data about tracks that currently exist
92  */
93 DB *trackdb_tracksdb;
94
95 /** @brief The preferences database
96  *
97  * - Keys are UTF-8(NFC(unicode(path name)))
98  * - Values are encoded key-value pairs
99  * - Data is user data about tracks (that might not exist any more)
100  * and cannot be reconstructed
101  */
102 DB *trackdb_prefsdb;
103
104 /** @brief The search database
105  *
106  * - Keys are UTF-8(NFKC(casefold(search term)))
107  * - Values are UTF-8(NFC(unicode(path name)))
108  * - There can be more than one value per key
109  * - Presence of key,value means that path matches the search terms
110  * - Only tracks fond in @ref trackdb_tracksdb are represented here
111  * - This database can be reconstructed, it contains no user data
112  */
113 DB *trackdb_searchdb;
114
115 /** @brief The tags database
116  *
117  * - Keys are UTF-8(NFKC(casefold(tag)))
118  * - Values are UTF-8(NFC(unicode(path name)))
119  * - There can be more than one value per key
120  * - Presence of key,value means that path matches the tag
121  * - This is always in sync with the tags preference
122  * - This database can be reconstructed, it contains no user data
123  */
124 DB *trackdb_tagsdb;                     /* the tags database */
125
126 /** @brief The global preferences database
127  * - Keys are UTF-8(NFC(preference))
128  * - Values are global preference values
129  * - Data is user data and cannot be reconstructed
130  */
131 DB *trackdb_globaldb;                   /* global preferences */
132
133 /** @brief The noticed database
134  * - Keys are 64-bit big-endian timestamps
135  * - Values are UTF-8(NFC(unicode(path name)))
136  * - There can be more than one value per key
137  * - Presence of key,value means that path was added at the given time
138  * - Data cannot be reconstructed (but isn't THAT important)
139  */
140 DB *trackdb_noticeddb;                   /* when track noticed */
141
142 /** @brief The schedule database
143  *
144  * - Keys are ID strings, generated at random
145  * - Values are encoded key-value pairs
146  * - There can be more than one value per key
147  * - Data cannot be reconstructed
148  *
149  * See @ref server/schedule.c for further information.
150  */
151 DB *trackdb_scheduledb;
152
153 /** @brief The user database
154  * - Keys are usernames
155  * - Values are encoded key-value pairs
156  * - Data is user data and cannot be reconstructed
157  */
158 DB *trackdb_usersdb;
159
160 /** @brief The playlists database
161  * - Keys are playlist names
162  * - Values are encoded key-value pairs
163  * - Data is user data and cannot be reconstructed
164  */
165 DB *trackdb_playlistsdb;
166
167 static pid_t db_deadlock_pid = -1;      /* deadlock manager PID */
168 static pid_t rescan_pid = -1;           /* rescanner PID */
169 static int initialized, opened;         /* state */
170
171 /* comparison function for keys */
172 static int compare(DB attribute((unused)) *db_,
173                    const DBT *a, const DBT *b) {
174   return compare_path_raw(a->data, a->size, b->data, b->size);
175 }
176
177 /** @brief Test whether the track database can be read
178  * @return 1 if it can, 0 if it cannot
179  */
180 int trackdb_readable(void) {
181   char *usersdb;
182
183   byte_xasprintf(&usersdb, "%s/users.db", config->home);
184   return access(usersdb, R_OK) == 0;
185 }
186
187 /** @brief Open database environment
188  * @param flags Flags word
189  *
190  * Flags should be one of:
191  * - @ref TRACKDB_NO_RECOVER
192  * - @ref TRACKDB_NORMAL_RECOVER
193  * - @ref TRACKDB_FATAL_RECOVER
194  * - @ref TRACKDB_MAY_CREATE
195  */
196 void trackdb_init(int flags) {
197   int err;
198   const int recover = flags & TRACKDB_RECOVER_MASK;
199   static int recover_type[] = { 0, DB_RECOVER, DB_RECOVER_FATAL };
200
201   /* sanity checks */
202   assert(initialized == 0);
203   ++initialized;
204   if(home) {
205     if(strcmp(home, config->home))
206       fatal(0, "cannot change db home without server restart");
207     home = config->home;
208   }
209
210   if(flags & TRACKDB_MAY_CREATE) {
211     DIR *dp;
212     struct dirent *de;
213     struct stat st;
214     char *p;
215
216     /* Remove world/group permissions on any regular files already in the
217      * database directory.  Actually we don't care about all of them but it's
218      * easier to just do the lot.  This can be revisited if it's a serious
219      * practical inconvenience for anyone.
220      *
221      * The socket, not being a regular file, is excepted.
222      */
223     if(!(dp = opendir(config->home)))
224       fatal(errno, "error reading %s", config->home);
225     while((de = readdir(dp))) {
226       byte_xasprintf(&p, "%s/%s", config->home, de->d_name);
227       if(lstat(p, &st) == 0
228          && S_ISREG(st.st_mode)
229          && (st.st_mode & 077)) {
230         if(chmod(p, st.st_mode & 07700) < 0)
231           fatal(errno, "cannot chmod %s", p);
232       }
233       xfree(p);
234     }
235     closedir(dp);
236   }
237
238   /* create environment */
239   if((err = db_env_create(&trackdb_env, 0))) fatal(0, "db_env_create: %s",
240                                                    db_strerror(err));
241   if((err = trackdb_env->set_alloc(trackdb_env,
242                                    xmalloc_noptr, xrealloc_noptr, xfree)))
243     fatal(0, "trackdb_env->set_alloc: %s", db_strerror(err));
244   if((err = trackdb_env->set_lk_max_locks(trackdb_env, 10000)))
245     fatal(0, "trackdb_env->set_lk_max_locks: %s", db_strerror(err));
246   if((err = trackdb_env->set_lk_max_objects(trackdb_env, 10000)))
247     fatal(0, "trackdb_env->set_lk_max_objects: %s", db_strerror(err));
248   if((err = trackdb_env->open(trackdb_env, config->home,
249                               DB_INIT_LOG
250                               |DB_INIT_LOCK
251                               |DB_INIT_MPOOL
252                               |DB_INIT_TXN
253                               |DB_CREATE
254                               |recover_type[recover],
255                               0600)))
256     fatal(0, "trackdb_env->open %s: %s", config->home, db_strerror(err));
257   trackdb_env->set_errpfx(trackdb_env, "DB");
258   trackdb_env->set_errfile(trackdb_env, stderr);
259   trackdb_env->set_verbose(trackdb_env, DB_VERB_DEADLOCK, 1);
260   trackdb_env->set_verbose(trackdb_env, DB_VERB_RECOVERY, 1);
261   trackdb_env->set_verbose(trackdb_env, DB_VERB_REPLICATION, 1);
262   D(("initialized database environment"));
263 }
264
265 /* called when deadlock manager terminates */
266 static int reap_db_deadlock(ev_source attribute((unused)) *ev,
267                             pid_t attribute((unused)) pid,
268                             int status,
269                             const struct rusage attribute((unused)) *rusage,
270                             void attribute((unused)) *u) {
271   db_deadlock_pid = -1;
272   if(initialized)
273     fatal(0, "deadlock manager unexpectedly terminated: %s",
274           wstat(status));
275   else
276     D(("deadlock manager terminated: %s", wstat(status)));
277   return 0;
278 }
279
280 static pid_t subprogram(ev_source *ev, int outputfd, const char *prog,
281                         ...) {
282   pid_t pid;
283   va_list ap;
284   const char *args[1024], **argp, *a;
285
286   argp = args;
287   *argp++ = prog;
288   *argp++ = "--config";
289   *argp++ = configfile;
290   *argp++ = debugging ? "--debug" : "--no-debug";
291   *argp++ = log_default == &log_syslog ? "--syslog" : "--no-syslog";
292   va_start(ap, prog);
293   while((a = va_arg(ap, const char *)))
294     *argp++ = a;
295   va_end(ap);
296   *argp = 0;
297   /* If we're in the background then trap subprocess stdout/stderr */
298   if(!(pid = xfork())) {
299     exitfn = _exit;
300     if(ev)
301       ev_signal_atfork(ev);
302     signal(SIGPIPE, SIG_DFL);
303     if(outputfd != -1) {
304       xdup2(outputfd, 1);
305       xclose(outputfd);
306     }
307     /* ensure we don't leak privilege anywhere */
308     if(setuid(geteuid()) < 0)
309       fatal(errno, "error calling setuid");
310     /* If we were negatively niced, undo it.  We don't bother checking for 
311     * error, it's not that important. */
312     setpriority(PRIO_PROCESS, 0, 0);
313     execvp(prog, (char **)args);
314     fatal(errno, "error invoking %s", prog);
315   }
316   return pid;
317 }
318
319 /* start deadlock manager */
320 void trackdb_master(ev_source *ev) {
321   assert(db_deadlock_pid == -1);
322   db_deadlock_pid = subprogram(ev, -1, DEADLOCK, (char *)0);
323   ev_child(ev, db_deadlock_pid, 0, reap_db_deadlock, 0);
324   D(("started deadlock manager"));
325 }
326
327 /* close environment */
328 void trackdb_deinit(ev_source *ev) {
329   int err;
330
331   /* sanity checks */
332   assert(initialized == 1);
333   --initialized;
334
335   /* close the environment */
336   if((err = trackdb_env->close(trackdb_env, 0)))
337     fatal(0, "trackdb_env->close: %s", db_strerror(err));
338
339   if(rescan_pid != -1) {
340     /* shut down the rescanner */
341     if(kill(rescan_pid, SIGTERM) < 0)
342       fatal(errno, "error killing rescanner");
343     /* wait for the rescanner to finish */
344     while(waitpid(rescan_pid, &err, 0) == -1 && errno == EINTR)
345       ;
346     if(ev)
347       ev_child_cancel(ev, rescan_pid);
348     rescan_pid = -1;
349   }
350
351   /* TODO kill any stats subprocesses */
352
353   if(db_deadlock_pid != -1) {
354     /* shut down the deadlock manager */
355     if(kill(db_deadlock_pid, SIGTERM) < 0)
356       fatal(errno, "error killing deadlock manager");
357     while(waitpid(db_deadlock_pid, &err, 0) == -1 && errno == EINTR)
358       ;
359     if(ev)
360       ev_child_cancel(ev, db_deadlock_pid);
361     db_deadlock_pid = -1;
362   }
363
364   D(("deinitialized database environment"));
365 }
366
367 /* open a specific database */
368 static DB *open_db(const char *path,
369                    u_int32_t dbflags,
370                    DBTYPE dbtype,
371                    u_int32_t openflags,
372                    int mode) {
373   int err, err2;
374   DB *db;
375
376   D(("open %s", path));
377   path = config_get_file(path);
378   if((err = db_create(&db, trackdb_env, 0)))
379     fatal(0, "db_create %s: %s", path, db_strerror(err));
380   if(dbflags)
381     if((err = db->set_flags(db, dbflags)))
382       fatal(0, "db->set_flags %s: %s", path, db_strerror(err));
383   if(dbtype == DB_BTREE)
384     if((err = db->set_bt_compare(db, compare)))
385       fatal(0, "db->set_bt_compare %s: %s", path, db_strerror(err));
386   if((err = db->open(db, 0, path, 0, dbtype,
387                      openflags | DB_AUTO_COMMIT, mode))) {
388     if((openflags & DB_CREATE) || errno != ENOENT) {
389       if((err2 = db->close(db, 0)))
390         error(0, "db->close: %s", db_strerror(err2));
391       trackdb_close();
392       trackdb_env->close(trackdb_env,0);
393       trackdb_env = 0;
394       fatal(0, "db->open %s: %s", path, db_strerror(err));
395     }
396     db->close(db, 0);
397     db = 0;
398   }
399   return db;
400 }
401
402 /** @brief Open track databases
403  * @param flags Flags flags word
404  *
405  * @p flags should have one of:
406  * - @p TRACKDB_NO_UPGRADE, if no upgrade should be attempted
407  * - @p TRACKDB_CAN_UPGRADE, if an upgrade may be attempted
408  * - @p TRACKDB_OPEN_FOR_UPGRADE, if this is disorder-dbupgrade
409  * Also it may have:
410  * - @p TRACKDB_READ_ONLY, read only access
411  */
412 void trackdb_open(int flags) {
413   int err;
414   pid_t pid;
415   uint32_t dbflags = flags & TRACKDB_READ_ONLY ? DB_RDONLY : DB_CREATE;
416
417   /* sanity checks */
418   assert(opened == 0);
419   ++opened;
420   /* check the database version first */
421   trackdb_globaldb = open_db("global.db", 0, DB_HASH, DB_RDONLY, 0666);
422   if(trackdb_globaldb) {
423     /* This is an existing database */
424     const char *s;
425     long oldversion;
426
427     s = trackdb_get_global("_dbversion");
428     /* Close the database again,  we'll open it property below */
429     if((err = trackdb_globaldb->close(trackdb_globaldb, 0)))
430       fatal(0, "error closing global.db: %s", db_strerror(err));
431     trackdb_globaldb = 0;
432     /* Convert version string to an integer */
433     oldversion = s ? atol(s) : 1;
434     if(oldversion > config->dbversion) {
435       /* Database is from the future; we never allow this. */
436       fatal(0, "this version of DisOrder is too old for database version %ld",
437             oldversion);
438     }
439     if(oldversion < config->dbversion) {
440       /* Database version is out of date */
441       switch(flags & TRACKDB_UPGRADE_MASK) {
442       case TRACKDB_NO_UPGRADE:
443         /* This database needs upgrading but this is not permitted */
444         fatal(0, "database needs upgrading from %ld to %ld",
445               oldversion, config->dbversion);
446       case TRACKDB_CAN_UPGRADE:
447         /* This database needs upgrading */
448         info("invoking disorder-dbupgrade to upgrade from %ld to %ld",
449              oldversion, config->dbversion);
450         pid = subprogram(0, -1, "disorder-dbupgrade", (char *)0);
451         while(waitpid(pid, &err, 0) == -1 && errno == EINTR)
452           ;
453         if(err)
454           fatal(0, "disorder-dbupgrade %s", wstat(err));
455         info("disorder-dbupgrade succeeded");
456         break;
457       case TRACKDB_OPEN_FOR_UPGRADE:
458         break;
459       default:
460         abort();
461       }
462     }
463     if(oldversion == config->dbversion && (flags & TRACKDB_OPEN_FOR_UPGRADE)) {
464       /* This doesn't make any sense */
465       fatal(0, "database is already at current version");
466     }
467     trackdb_existing_database = 1;
468   } else {
469     if(flags & TRACKDB_OPEN_FOR_UPGRADE) {
470       /* Cannot upgrade a new database */
471       fatal(0, "cannot upgrade a database that does not exist");
472     }
473     /* This is a brand new database */
474     trackdb_existing_database = 0;
475   }
476   /* open the databases */
477   if(!(trackdb_usersdb = open_db("users.db",
478                                  0, DB_HASH, dbflags, 0600)))
479     fatal(0, "cannot open users.db");
480   trackdb_tracksdb = open_db("tracks.db",
481                              DB_RECNUM, DB_BTREE, dbflags, 0666);
482   trackdb_searchdb = open_db("search.db",
483                              DB_DUP|DB_DUPSORT, DB_HASH, dbflags, 0666);
484   trackdb_tagsdb = open_db("tags.db",
485                            DB_DUP|DB_DUPSORT, DB_HASH, dbflags, 0666);
486   trackdb_prefsdb = open_db("prefs.db", 0, DB_HASH, dbflags, 0666);
487   trackdb_globaldb = open_db("global.db", 0, DB_HASH, dbflags, 0666);
488   trackdb_noticeddb = open_db("noticed.db",
489                              DB_DUPSORT, DB_BTREE, dbflags, 0666);
490   trackdb_scheduledb = open_db("schedule.db", 0, DB_HASH, dbflags, 0666);
491   trackdb_playlistsdb = open_db("playlists.db", 0, DB_HASH, dbflags, 0666);
492   if(!trackdb_existing_database && !(flags & TRACKDB_READ_ONLY)) {
493     /* Stash the database version */
494     char buf[32];
495
496     assert(!(flags & TRACKDB_OPEN_FOR_UPGRADE));
497     snprintf(buf, sizeof buf, "%ld", config->dbversion);
498     trackdb_set_global("_dbversion", buf, 0);
499   }
500   D(("opened databases"));
501 }
502
503 /* close track databases */
504 void trackdb_close(void) {
505   int err;
506
507   /* sanity checks */
508   assert(opened == 1);
509   --opened;
510 #define CLOSE(N, V) do {                                        \
511   if(V && (err = V->close(V, 0)))                               \
512     fatal(0, "error closing %s: %s", N, db_strerror(err));      \
513   V = 0;                                                        \
514 } while(0)
515   CLOSE("tracks.db", trackdb_tracksdb);
516   CLOSE("search.db", trackdb_searchdb);
517   CLOSE("tags.db", trackdb_tagsdb);
518   CLOSE("prefs.db", trackdb_prefsdb);
519   CLOSE("global.db", trackdb_globaldb);
520   CLOSE("noticed.db", trackdb_noticeddb);
521   CLOSE("schedule.db", trackdb_scheduledb);
522   CLOSE("users.db", trackdb_usersdb);
523   CLOSE("playlists.db", trackdb_playlistsdb);
524   D(("closed databases"));
525 }
526
527 /* generic db routines *******************************************************/
528
529 /** @brief Fetch and decode a database entry
530  * @param db Database
531  * @param track Track name
532  * @param kp Where to put decoded list (or NULL if you don't care)
533  * @param tid Owning transaction
534  * @return 0, @c DB_NOTFOUND or @c DB_LOCK_DEADLOCK
535  */
536 int trackdb_getdata(DB *db,
537                     const char *track,
538                     struct kvp **kp,
539                     DB_TXN *tid) {
540   int err;
541   DBT key, data;
542
543   switch(err = db->get(db, tid, make_key(&key, track),
544                        prepare_data(&data), 0)) {
545   case 0:
546     if(kp)
547       *kp = kvp_urldecode(data.data, data.size);
548     return 0;
549   case DB_NOTFOUND:
550     if(kp)
551       *kp = 0;
552     return err;
553   case DB_LOCK_DEADLOCK:
554     error(0, "error querying database: %s", db_strerror(err));
555     return err;
556   default:
557     fatal(0, "error querying database: %s", db_strerror(err));
558   }
559 }
560
561 /* encode and store a database entry.  Returns 0, DB_KEYEXIST or
562  * DB_LOCK_DEADLOCK. */
563 int trackdb_putdata(DB *db,
564                     const char *track,
565                     const struct kvp *k,
566                     DB_TXN *tid,
567                     u_int32_t flags) {
568   int err;
569   DBT key, data;
570
571   switch(err = db->put(db, tid, make_key(&key, track),
572                        encode_data(&data, k), flags)) {
573   case 0:
574   case DB_KEYEXIST:
575     return err;
576   case DB_LOCK_DEADLOCK:
577     error(0, "error updating database: %s", db_strerror(err));
578     return err;
579   default:
580     fatal(0, "error updating database: %s", db_strerror(err));
581   }
582 }
583
584 /** @brief Delete a database entry
585  * @param db Database
586  * @param track Key to delete
587  * @param tid Transaction ID
588  * @return 0, DB_NOTFOUND or DB_LOCK_DEADLOCK
589  */
590 int trackdb_delkey(DB *db,
591                    const char *track,
592                    DB_TXN *tid) {
593   int err;
594
595   DBT key;
596   switch(err = db->del(db, tid, make_key(&key, track), 0)) {
597   case 0:
598   case DB_NOTFOUND:
599     return 0;
600   case DB_LOCK_DEADLOCK:
601     error(0, "error updating database: %s", db_strerror(err));
602     return err;
603   default:
604     fatal(0, "error updating database: %s", db_strerror(err));
605   }
606 }
607
608 /* open a database cursor */
609 DBC *trackdb_opencursor(DB *db, DB_TXN *tid) {
610   int err;
611   DBC *c;
612
613   switch(err = db->cursor(db, tid, &c, 0)) {
614   case 0: break;
615   default: fatal(0, "error creating cursor: %s", db_strerror(err));
616   }
617   return c;
618 }
619
620 /* close a database cursor; returns 0 or DB_LOCK_DEADLOCK */
621 int trackdb_closecursor(DBC *c) {
622   int err;
623
624   if(!c) return 0;
625   switch(err = c->c_close(c)) {
626   case 0:
627     return err;
628   case DB_LOCK_DEADLOCK:
629     error(0, "error closing cursor: %s", db_strerror(err));
630     return err;
631   default:
632     fatal(0, "error closing cursor: %s", db_strerror(err));
633   }
634 }
635
636 /* delete a (key,data) pair.  Returns 0, DB_NOTFOUND or DB_LOCK_DEADLOCK. */
637 int trackdb_delkeydata(DB *db,
638                        const char *word,
639                        const char *track,
640                        DB_TXN *tid) {
641   int err;
642   DBC *c;
643   DBT key, data;
644
645   c = trackdb_opencursor(db, tid);
646   switch(err = c->c_get(c, make_key(&key, word),
647                         make_key(&data, track), DB_GET_BOTH)) {
648   case 0:
649     switch(err = c->c_del(c, 0)) {
650     case 0:
651       break;
652     case DB_KEYEMPTY:
653       err = 0;
654       break;
655     case DB_LOCK_DEADLOCK:
656       error(0, "error updating database: %s", db_strerror(err));
657       break;
658     default:
659       fatal(0, "c->c_del: %s", db_strerror(err));
660     }
661     break;
662   case DB_NOTFOUND:
663     break;
664   case DB_LOCK_DEADLOCK:
665     error(0, "error updating database: %s", db_strerror(err));
666     break;
667   default:
668     fatal(0, "c->c_get: %s", db_strerror(err));
669   }
670   if(trackdb_closecursor(c)) err = DB_LOCK_DEADLOCK;
671   return err;
672 }
673
674 /* start a transaction */
675 DB_TXN *trackdb_begin_transaction(void) {
676   DB_TXN *tid;
677   int err;
678
679   if((err = trackdb_env->txn_begin(trackdb_env, 0, &tid, 0)))
680     fatal(0, "trackdb_env->txn_begin: %s", db_strerror(err));
681   return tid;
682 }
683
684 /* abort transaction */
685 void trackdb_abort_transaction(DB_TXN *tid) {
686   int err;
687
688   if(tid)
689     if((err = tid->abort(tid)))
690       fatal(0, "tid->abort: %s", db_strerror(err));
691 }
692
693 /* commit transaction */
694 void trackdb_commit_transaction(DB_TXN *tid) {
695   int err;
696
697   if((err = tid->commit(tid, 0)))
698     fatal(0, "tid->commit: %s", db_strerror(err));
699 }
700
701 /* search/tags shared code ***************************************************/
702
703 /* comparison function used by dedupe() */
704 static int wordcmp(const void *a, const void *b) {
705   return strcmp(*(const char **)a, *(const char **)b);
706 }
707
708 /* sort and de-dupe VEC */
709 static char **dedupe(char **vec, int nvec) {
710   int m, n;
711
712   qsort(vec, nvec, sizeof (char *), wordcmp);
713   m = n = 0;
714   if(nvec) {
715     vec[m++] = vec[0];
716     for(n = 1; n < nvec; ++n)
717       if(strcmp(vec[n], vec[m - 1]))
718         vec[m++] = vec[n];
719   }
720   vec[m] = 0;
721   return vec;
722 }
723
724 /* update a key/track database.  Returns 0 or DB_DEADLOCK. */
725 static int register_word(DB *db, const char *what,
726                          const char *track, const char *word,
727                          DB_TXN *tid) {
728   int err;
729   DBT key, data;
730
731   switch(err = db->put(db, tid, make_key(&key, word),
732                        make_key(&data, track), DB_NODUPDATA)) {
733   case 0:
734   case DB_KEYEXIST:
735     return 0;
736   case DB_LOCK_DEADLOCK:
737     error(0, "error updating %s.db: %s", what, db_strerror(err));
738     return err;
739   default:
740     fatal(0, "error updating %s.db: %s", what,  db_strerror(err));
741   }
742 }
743
744 /* search primitives *********************************************************/
745
746 /* return true iff NAME is a trackname_display_ pref */
747 static int is_display_pref(const char *name) {
748   static const char prefix[] = "trackname_display_";
749   return !strncmp(name, prefix, (sizeof prefix) - 1);
750 }
751
752 /** @brief Word_Break property tailor that treats underscores as spaces */
753 static int tailor_underscore_Word_Break_Other(uint32_t c) {
754   switch(c) {
755   default:
756     return -1;
757   case 0x005F: /* LOW LINE (SPACING UNDERSCORE) */
758     return unicode_Word_Break_Other;
759   }
760 }
761
762 /** @brief Remove all combining characters in-place
763  * @param s Pointer to start of string
764  * @param ns Length of string
765  * @return New, possiblby reduced, length
766  */
767 static size_t remove_combining_chars(uint32_t *s, size_t ns) {
768   uint32_t *start = s, *t = s, *end = s + ns;
769
770   while(s < end) {
771     const uint32_t c = *s++;
772     if(!utf32_combining_class(c))
773       *t++ = c;
774   }
775   return t - start;
776 }
777
778 /** @brief Normalize and split a string using a given tailoring */
779 static void word_split(struct vector *v,
780                        const char *s,
781                        unicode_property_tailor *pt) {
782   size_t nw, nt32, i;
783   uint32_t *t32, **w32;
784
785   /* Convert to UTF-32 */
786   if(!(t32 = utf8_to_utf32(s, strlen(s), &nt32)))
787     return;
788   /* Erase case distinctions */
789   if(!(t32 = utf32_casefold_compat(t32, nt32, &nt32)))
790     return;
791   /* Drop combining characters */
792   nt32 = remove_combining_chars(t32, nt32);
793   /* Split into words, treating _ as a space */
794   w32 = utf32_word_split(t32, nt32, &nw, pt);
795   /* Convert words back to UTF-8 and append to result */
796   for(i = 0; i < nw; ++i)
797     vector_append(v, utf32_to_utf8(w32[i], utf32_len(w32[i]), 0));
798 }
799
800 /** @brief Normalize a tag
801  * @param s Tag
802  * @param ns Length of tag
803  * @return Normalized string or NULL on error
804  *
805  * The return value will be:
806  * - case-folded
807  * - have no leading or trailing space
808  * - have no combining characters
809  * - all spacing between words will be a single U+0020 SPACE
810  */
811 static char *normalize_tag(const char *s, size_t ns) {
812   uint32_t *s32, **w32;
813   size_t ns32, nw32, i;
814   struct dynstr d[1];
815
816   if(!(s32 = utf8_to_utf32(s, ns, &ns32)))
817     return 0;
818   if(!(s32 = utf32_casefold_compat(s32, ns32, &ns32))) /* ->NFKD */
819     return 0;
820   ns32 = remove_combining_chars(s32, ns32);
821   /* Split into words, no Word_Break tailoring */
822   w32 = utf32_word_split(s32, ns32, &nw32, 0);
823   /* Compose back into a string */
824   dynstr_init(d);
825   for(i = 0; i < nw32; ++i) {
826     if(i)
827       dynstr_append(d, ' ');
828     dynstr_append_string(d, utf32_to_utf8(w32[i], utf32_len(w32[i]), 0));
829   }
830   dynstr_terminate(d);
831   return d->vec;
832 }
833
834 /* compute the words of a track name */
835 static char **track_to_words(const char *track,
836                              const struct kvp *p) {
837   struct vector v;
838   const char *rootless = track_rootless(track);
839
840   if(!rootless)
841     rootless = track;                   /* bodge */
842   vector_init(&v);
843   rootless = strip_extension(rootless);
844   word_split(&v, strip_extension(rootless), tailor_underscore_Word_Break_Other);
845   for(; p; p = p->next)
846     if(is_display_pref(p->name))
847       word_split(&v, p->value, 0);
848   vector_terminate(&v);
849   return dedupe(v.vec, v.nvec);
850 }
851
852 /* return nonzero iff WORD is a stopword */
853 static int stopword(const char *word) {
854   int n;
855
856   for(n = 0; n < config->stopword.n
857         && strcmp(word, config->stopword.s[n]); ++n)
858     ;
859   return n < config->stopword.n;
860 }
861
862 /* record that WORD appears in TRACK.  Returns 0 or DB_LOCK_DEADLOCK. */
863 static int register_search_word(const char *track, const char *word,
864                                 DB_TXN *tid) {
865   if(stopword(word)) return 0;
866   return register_word(trackdb_searchdb, "search", track, word, tid);
867 }
868
869 /* Tags **********************************************************************/
870
871 /* Return nonzero if C is a valid tag character */
872 static int tagchar(int c) {
873   switch(c) {
874   case ',':
875     return 0;
876   default:
877     return c >= ' ';
878   }
879 }
880
881 /* Parse and de-dupe a tag list.  If S=0 then assumes "". */
882 char **parsetags(const char *s) {
883   const char *t;
884   struct vector v;
885
886   vector_init(&v);
887   if(s) {
888     /* skip initial separators */
889     while(*s && (!tagchar(*s) || *s == ' '))
890       ++s;
891     while(*s) {
892       /* find the extent of the tag */
893       t = s;
894       while(*s && tagchar(*s))
895         ++s;
896       /* strip trailing spaces */
897       while(s > t && s[-1] == ' ')
898         --s;
899       /* add tag to list */
900       vector_append(&v, normalize_tag(t, (size_t)(s - t)));
901       /* skip intermediate and trailing separators */
902       while(*s && (!tagchar(*s) || *s == ' '))
903         ++s;
904     }
905   }
906   vector_terminate(&v);
907   return dedupe(v.vec, v.nvec);
908 }
909
910 /* Record that TRACK has TAG.  Returns 0 or DB_LOCK_DEADLOCK. */
911 static int register_tag(const char *track, const char *tag, DB_TXN *tid) {
912   return register_word(trackdb_tagsdb, "tags", track, tag, tid);
913 }
914
915 /* aliases *******************************************************************/
916
917 /* compute the alias and store at aliasp.  Returns 0 or DB_LOCK_DEADLOCK.  If
918  * there is no alias sets *aliasp to 0. */
919 static int compute_alias(char **aliasp,
920                          const char *track,
921                          const struct kvp *p,
922                          DB_TXN *tid) {
923   struct dynstr d;
924   const char *s = config->alias, *t, *expansion, *part;
925   int c, used_db = 0, slash_prefix, err;
926   struct kvp *at;
927   const char *const root = find_track_root(track);
928
929   if(!root) {
930     /* Bodge for tracks with no root */
931     *aliasp = 0;
932     return 0;
933   }
934   dynstr_init(&d);
935   dynstr_append_string(&d, root);
936   while((c = (unsigned char)*s++)) {
937     if(c != '{') {
938       dynstr_append(&d, c);
939       continue;
940     }
941     if((slash_prefix = (*s == '/')))
942       s++;
943     t = strchr(s, '}');
944     assert(t != 0);                     /* validated at startup */
945     part = xstrndup(s, t - s);
946     expansion = getpart(track, "display", part, p, &used_db);
947     if(*expansion) {
948       if(slash_prefix) dynstr_append(&d, '/');
949       dynstr_append_string(&d, expansion);
950     }
951     s = t + 1;                          /* skip {part} */
952   }
953   /* only admit to the alias if we used the db... */
954   if(!used_db) {
955     *aliasp = 0;
956     return 0;
957   }
958   dynstr_terminate(&d);
959   /* ...and the answer differs from the original... */
960   if(!strcmp(track, d.vec)) {
961     *aliasp = 0;
962     return 0;
963   }
964   /* ...and there isn't already a different track with that name (including as
965    * an alias) */
966   switch(err = trackdb_getdata(trackdb_tracksdb, d.vec, &at, tid)) {
967   case 0:
968     if((s = kvp_get(at, "_alias_for"))
969        && !strcmp(s, track)) {
970     case DB_NOTFOUND:
971       *aliasp = d.vec;
972     } else {
973       *aliasp = 0;
974     }
975     return 0;
976   default:
977     return err;
978   }
979 }
980
981 /* get track and prefs data (if tp/pp not null pointers).  Returns 0 on
982  * success, DB_NOTFOUND if the track does not exist or DB_LOCK_DEADLOCK.
983  * Always sets the return values, even if only to null pointers. */
984 static int gettrackdata(const char *track,
985                         struct kvp **tp,
986                         struct kvp **pp,
987                         const char **actualp,
988                         unsigned flags,
989 #define GTD_NOALIAS 0x0001
990                         DB_TXN *tid) {
991   int err;
992   const char *actual = track;
993   struct kvp *t = 0, *p = 0;
994
995   if((err = trackdb_getdata(trackdb_tracksdb, track, &t, tid))) goto done;
996   if((actual = kvp_get(t, "_alias_for"))) {
997     if(flags & GTD_NOALIAS) {
998       error(0, "alias passed to gettrackdata where real path required");
999       abort();
1000     }
1001     if((err = trackdb_getdata(trackdb_tracksdb, actual, &t, tid))) goto done;
1002   } else
1003     actual = track;
1004   assert(actual != 0);
1005   if(pp) {
1006     if((err = trackdb_getdata(trackdb_prefsdb, actual, &p, tid)) == DB_LOCK_DEADLOCK)
1007       goto done;
1008   }
1009   err = 0;
1010 done:
1011   if(actualp) *actualp = actual;
1012   if(tp) *tp = t;
1013   if(pp) *pp = p;
1014   return err;
1015 }
1016
1017 /* trackdb_notice() **********************************************************/
1018
1019 /** @brief notice a possibly new track
1020  * @return @c DB_NOTFOUND if new, 0 if already known
1021  */
1022 int trackdb_notice(const char *track,
1023                    const char *path) {
1024   int err;
1025   DB_TXN *tid;
1026
1027   for(;;) {
1028     tid = trackdb_begin_transaction();
1029     err = trackdb_notice_tid(track, path, tid);
1030     if((err == DB_LOCK_DEADLOCK)) goto fail;
1031     break;
1032   fail:
1033     trackdb_abort_transaction(tid);
1034   }
1035   trackdb_commit_transaction(tid);
1036   return err;
1037 }
1038
1039 /** @brief notice a possibly new track
1040  * @param track NFC UTF-8 track name
1041  * @param path Raw path name
1042  * @param tid Transaction ID
1043  * @return @c DB_NOTFOUND if new, 0 if already known, @c DB_LOCK_DEADLOCK also
1044  */
1045 int trackdb_notice_tid(const char *track,
1046                        const char *path,
1047                        DB_TXN *tid) {
1048   int err, n;
1049   struct kvp *t, *a, *p;
1050   int t_changed, ret;
1051   char *alias, **w, *noticed;
1052   time_t now;
1053
1054   /* notice whether the tracks.db entry changes */
1055   t_changed = 0;
1056   /* get any existing tracks entry */
1057   if((err = gettrackdata(track, &t, &p, 0, 0, tid)) == DB_LOCK_DEADLOCK)
1058     return err;
1059   ret = err;                            /* 0 or DB_NOTFOUND */
1060   /* this is a real track */
1061   t_changed += kvp_set(&t, "_alias_for", 0);
1062   t_changed += kvp_set(&t, "_path", path);
1063   xtime(&now);
1064   if(ret == DB_NOTFOUND) {
1065     /* It's a new track; record the time */
1066     byte_xasprintf(&noticed, "%lld", (long long)now);
1067     t_changed += kvp_set(&t, "_noticed", noticed);
1068   }
1069   /* if we have an alias record it in the database */
1070   if((err = compute_alias(&alias, track, p, tid))) return err;
1071   if(alias) {
1072     /* won't overwrite someone else's alias as compute_alias() checks */
1073     D(("%s: alias %s", track, alias));
1074     a = 0;
1075     kvp_set(&a, "_alias_for", track);
1076     if((err = trackdb_putdata(trackdb_tracksdb, alias, a, tid, 0))) return err;
1077   }
1078   /* update search.db */
1079   w = track_to_words(track, p);
1080   for(n = 0; w[n]; ++n)
1081     if((err = register_search_word(track, w[n], tid)))
1082       return err;
1083   /* update tags.db */
1084   w = parsetags(kvp_get(p, "tags"));
1085   for(n = 0; w[n]; ++n)
1086     if((err = register_tag(track, w[n], tid)))
1087       return err;
1088   /* only store the tracks.db entry if it has changed */
1089   if(t_changed && (err = trackdb_putdata(trackdb_tracksdb, track, t, tid, 0)))
1090     return err;
1091   if(ret == DB_NOTFOUND) {
1092     uint32_t timestamp[2];
1093     DBT key, data;
1094
1095     timestamp[0] = htonl((uint64_t)now >> 32);
1096     timestamp[1] = htonl((uint32_t)now);
1097     memset(&key, 0, sizeof key);
1098     key.data = timestamp;
1099     key.size = sizeof timestamp;
1100     switch(err = trackdb_noticeddb->put(trackdb_noticeddb, tid, &key,
1101                                         make_key(&data, track), 0)) {
1102     case 0: break;
1103     case DB_LOCK_DEADLOCK: return err;
1104     default: fatal(0, "error updating noticed.db: %s", db_strerror(err));
1105     }
1106   }
1107   return ret;
1108 }
1109
1110 /* trackdb_obsolete() ********************************************************/
1111
1112 /* obsolete a track */
1113 int trackdb_obsolete(const char *track, DB_TXN *tid) {
1114   int err, n;
1115   struct kvp *p;
1116   char *alias, **w;
1117
1118   if((err = gettrackdata(track, 0, &p, 0,
1119                          GTD_NOALIAS, tid)) == DB_LOCK_DEADLOCK)
1120     return err;
1121   else if(err == DB_NOTFOUND) return 0;
1122   /* compute the alias, if any, and delete it */
1123   if((err = compute_alias(&alias, track, p, tid))) return err;
1124   if(alias) {
1125     /* if the alias points to some other track then compute_alias won't
1126      * return it */
1127     if((err = trackdb_delkey(trackdb_tracksdb, alias, tid))
1128        && err != DB_NOTFOUND)
1129       return err;
1130   }
1131   /* update search.db */
1132   w = track_to_words(track, p);
1133   for(n = 0; w[n]; ++n)
1134     if(trackdb_delkeydata(trackdb_searchdb,
1135                           w[n], track, tid) == DB_LOCK_DEADLOCK)
1136       return err;
1137   /* update tags.db */
1138   w = parsetags(kvp_get(p, "tags"));
1139   for(n = 0; w[n]; ++n)
1140     if(trackdb_delkeydata(trackdb_tagsdb,
1141                           w[n], track, tid) == DB_LOCK_DEADLOCK)
1142       return err;
1143   /* update tracks.db */
1144   if(trackdb_delkey(trackdb_tracksdb, track, tid) == DB_LOCK_DEADLOCK)
1145     return err;
1146   /* We don't delete the prefs, so they survive temporary outages of the
1147    * (possibly virtual) track filesystem */
1148   return 0;
1149 }
1150
1151 /* trackdb_stats() ***********************************************************/
1152
1153 #define H(name) { #name, offsetof(DB_HASH_STAT, name) }
1154 #define B(name) { #name, offsetof(DB_BTREE_STAT, name) }
1155
1156 static const struct statinfo {
1157   const char *name;
1158   size_t offset;
1159 } statinfo_hash[] = {
1160   H(hash_magic),
1161   H(hash_version),
1162   H(hash_nkeys),
1163   H(hash_ndata),
1164   H(hash_pagesize),
1165   H(hash_ffactor),
1166   H(hash_buckets),
1167   H(hash_free),
1168   H(hash_bfree),
1169   H(hash_bigpages),
1170   H(hash_big_bfree),
1171   H(hash_overflows),
1172   H(hash_ovfl_free),
1173   H(hash_dup),
1174   H(hash_dup_free),
1175 }, statinfo_btree[] = {
1176   B(bt_magic),
1177   B(bt_version),
1178   B(bt_nkeys),
1179   B(bt_ndata),
1180   B(bt_pagesize),
1181   B(bt_minkey),
1182   B(bt_re_len),
1183   B(bt_re_pad),
1184   B(bt_levels),
1185   B(bt_int_pg),
1186   B(bt_leaf_pg),
1187   B(bt_dup_pg),
1188   B(bt_over_pg),
1189   B(bt_free),
1190   B(bt_int_pgfree),
1191   B(bt_leaf_pgfree),
1192   B(bt_dup_pgfree),
1193   B(bt_over_pgfree),
1194 };
1195
1196 /* look up stats for DB */
1197 static int get_stats(struct vector *v,
1198                      DB *database,
1199                      const struct statinfo *si,
1200                      size_t nsi,
1201                      DB_TXN *tid) {
1202   void *sp;
1203   size_t n;
1204   char *str;
1205   int err;
1206
1207   if(database) {
1208     switch(err = database->stat(database, tid, &sp, 0)) {
1209     case 0:
1210       break;
1211     case DB_LOCK_DEADLOCK:
1212       error(0, "error querying database: %s", db_strerror(err));
1213       return err;
1214     default:
1215       fatal(0, "error querying database: %s", db_strerror(err));
1216     }
1217     for(n = 0; n < nsi; ++n) {
1218       byte_xasprintf(&str, "%s=%"PRIuMAX, si[n].name,
1219                      (uintmax_t)*(u_int32_t *)((char *)sp + si[n].offset));
1220       vector_append(v, str);
1221     }
1222   }
1223   return 0;
1224 }
1225
1226 /** @brief One entry in the search league */
1227 struct search_entry {
1228   char *word;
1229   int n;
1230 };
1231
1232 /** @brief Add a word to the search league
1233  * @param se Pointer to search league
1234  * @param count Maximum size for search league
1235  * @param nse Current size of search league
1236  * @param word New word, or NULL
1237  * @param n How often @p word appears
1238  * @return New size of search league
1239  */
1240 static int register_search_entry(struct search_entry *se,
1241                                  int count,
1242                                  int nse,
1243                                  char *word,
1244                                  int n) {
1245   int i;
1246
1247   if(word && (nse < count || n > se[nse - 1].n)) {
1248     /* Find the starting point */
1249     if(nse == count)
1250       i = nse - 1;
1251     else
1252       i = nse++;
1253     /* Find the insertion point */
1254     while(i > 0 && n > se[i - 1].n)
1255       --i;
1256     memmove(&se[i + 1], &se[i], (nse - i - 1) * sizeof *se);
1257     se[i].word = word;
1258     se[i].n = n;
1259   }
1260   return nse;
1261 }
1262
1263 /* find the top COUNT words in the search database */
1264 static int search_league(struct vector *v, int count, DB_TXN *tid) {
1265   struct search_entry *se;
1266   DBT k, d;
1267   DBC *cursor;
1268   int err, n = 0, nse = 0, i;
1269   char *word = 0;
1270   size_t wl = 0;
1271   char *str;
1272
1273   cursor = trackdb_opencursor(trackdb_searchdb, tid);
1274   se = xmalloc(count * sizeof *se);
1275   /* Walk across the whole database counting up the number of times each
1276    * word appears. */
1277   while(!(err = cursor->c_get(cursor, prepare_data(&k), prepare_data(&d),
1278                               DB_NEXT))) {
1279     if(word && wl == k.size && !strncmp(word, k.data, wl))
1280       ++n;                              /* same word again */
1281     else {
1282       nse = register_search_entry(se, count, nse, word, n);
1283       word = xstrndup(k.data, wl = k.size);
1284       n = 1;
1285     }
1286   }
1287   switch(err) {
1288   case DB_NOTFOUND:
1289     err = 0;
1290     break;
1291   case DB_LOCK_DEADLOCK:
1292     error(0, "error querying search database: %s", db_strerror(err));
1293     break;
1294   default:
1295     fatal(0, "error querying search database: %s", db_strerror(err));
1296   }
1297   if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
1298   if(err) return err;
1299   nse = register_search_entry(se, count, nse, word, n);
1300   byte_xasprintf(&str, "Top %d search words:", nse);
1301   vector_append(v, str);
1302   for(i = 0; i < nse; ++i) {
1303     byte_xasprintf(&str, "%4d: %5d %s", i + 1, se[i].n, se[i].word);
1304     vector_append(v, str);
1305   }
1306   return 0;
1307 }
1308
1309 #define SI(what) statinfo_##what, \
1310                  sizeof statinfo_##what / sizeof (struct statinfo)
1311
1312 /* return a list of database stats */
1313 char **trackdb_stats(int *nstatsp) {
1314   DB_TXN *tid;
1315   struct vector v;
1316
1317   vector_init(&v);
1318   for(;;) {
1319     tid = trackdb_begin_transaction();
1320     v.nvec = 0;
1321     vector_append(&v, (char *)"Tracks database stats:");
1322     if(get_stats(&v, trackdb_tracksdb, SI(btree), tid)) goto fail;
1323     vector_append(&v, (char *)"");
1324     vector_append(&v, (char *)"Search database stats:");
1325     if(get_stats(&v, trackdb_searchdb, SI(hash), tid)) goto fail;
1326     vector_append(&v, (char *)"");
1327     vector_append(&v, (char *)"Prefs database stats:");
1328     if(get_stats(&v, trackdb_prefsdb, SI(hash), tid)) goto fail;
1329     vector_append(&v, (char *)"");
1330     if(search_league(&v, 10, tid)) goto fail;
1331     vector_terminate(&v);
1332     break;
1333 fail:
1334     trackdb_abort_transaction(tid);
1335   }
1336   trackdb_commit_transaction(tid);
1337   if(nstatsp) *nstatsp = v.nvec;
1338   return v.vec;
1339 }
1340
1341 struct stats_details {
1342   void (*done)(char *data, void *u);
1343   void *u;
1344   int exited;                           /* subprocess exited */
1345   int closed;                           /* pipe close */
1346   int wstat;                            /* wait status from subprocess */
1347   struct dynstr data[1];                /* data read from pipe */
1348 };
1349
1350 static void stats_complete(struct stats_details *d) {
1351   char *s;
1352
1353   if(!(d->exited && d->closed))
1354     return;
1355   byte_xasprintf(&s, "\n"
1356                  "Server stats:\n"
1357                  "track lookup cache hits: %lu\n"
1358                  "track lookup cache misses: %lu\n",
1359                  cache_files_hits,
1360                  cache_files_misses);
1361   dynstr_append_string(d->data, s);
1362   dynstr_terminate(d->data);
1363   d->done(d->data->vec, d->u);
1364 }
1365
1366 static int stats_finished(ev_source attribute((unused)) *ev,
1367                           pid_t attribute((unused)) pid,
1368                           int status,
1369                           const struct rusage attribute((unused)) *rusage,
1370                           void *u) {
1371   struct stats_details *const d = u;
1372
1373   d->exited = 1;
1374   if(status)
1375     error(0, "disorder-stats %s", wstat(status));
1376   stats_complete(d);
1377   return 0;
1378 }
1379
1380 static int stats_read(ev_source attribute((unused)) *ev,
1381                       ev_reader *reader,
1382                       void *ptr,
1383                       size_t bytes,
1384                       int eof,
1385                       void *u) {
1386   struct stats_details *const d = u;
1387
1388   dynstr_append_bytes(d->data, ptr, bytes);
1389   ev_reader_consume(reader, bytes);
1390   if(eof)
1391     d->closed = 1;
1392   stats_complete(d);
1393   return 0;
1394 }
1395
1396 static int stats_error(ev_source attribute((unused)) *ev,
1397                        int errno_value,
1398                        void *u) {
1399   struct stats_details *const d = u;
1400
1401   error(errno_value, "error reading from pipe to disorder-stats");
1402   d->closed = 1;
1403   stats_complete(d);
1404   return 0;
1405 }
1406
1407 void trackdb_stats_subprocess(ev_source *ev,
1408                               void (*done)(char *data, void *u),
1409                               void *u) {
1410   int p[2];
1411   pid_t pid;
1412   struct stats_details *d = xmalloc(sizeof *d);
1413
1414   dynstr_init(d->data);
1415   d->done = done;
1416   d->u = u;
1417   xpipe(p);
1418   pid = subprogram(ev, p[1], "disorder-stats", (char *)0);
1419   xclose(p[1]);
1420   ev_child(ev, pid, 0, stats_finished, d);
1421   if(!ev_reader_new(ev, p[0], stats_read, stats_error, d,
1422                     "disorder-stats reader"))
1423     fatal(0, "ev_reader_new for disorder-stats reader failed");
1424 }
1425
1426 /** @brief Parse a track name part preference
1427  * @param name Preference name
1428  * @param partp Where to store part name
1429  * @param contextp Where to store context name
1430  * @return 0 on success, non-0 if parse fails
1431  */
1432 static int trackdb__parse_namepref(const char *name,
1433                                    char **partp,
1434                                    char **contextp) {
1435   char *c;
1436   static const char prefix[] = "trackname_";
1437   
1438   if(strncmp(name, prefix, strlen(prefix)))
1439     return -1;                          /* not trackname_* at all */
1440   name += strlen(prefix);
1441   /* There had better be a _ between context and part */
1442   c = strchr(name, '_');
1443   if(!c)
1444     return -1;
1445   /* Context is first in the pref name even though most APIs have the part
1446    * first.  Confusing; sorry. */
1447   *contextp = xstrndup(name, c - name);
1448   ++c;
1449   /* There had better NOT be a second _ */
1450   if(strchr(c, '_'))
1451     return -1;
1452   *partp = xstrdup(c);
1453   return 0;
1454 }
1455
1456 /** @brief Compute the default value for a track preference
1457  * @param track Track name
1458  * @param name Preference name
1459  * @return Default value or 0 if none/not known
1460  */
1461 static const char *trackdb__default(const char *track, const char *name) {
1462   char *context, *part;
1463   
1464   if(!trackdb__parse_namepref(name, &part, &context)) {
1465     /* We can work out the default for a trackname_ pref */
1466     return trackname_part(track, context, part);
1467   } else if(!strcmp(name, "weight")) {
1468     /* We know the default weight */
1469     return "90000";
1470   } else if(!strcmp(name, "pick_at_random")) {
1471     /* By default everything is eligible for picking at random */
1472     return "1";
1473   } else if(!strcmp(name, "tags")) {
1474     /* By default everything no track has any tags */
1475     return "";
1476   }
1477   return 0;
1478 }
1479
1480 /* set a pref (remove if value=0) */
1481 int trackdb_set(const char *track,
1482                 const char *name,
1483                 const char *value) {
1484   struct kvp *t, *p, *a;
1485   DB_TXN *tid;
1486   int err, cmp;
1487   char *oldalias, *newalias, **oldtags = 0, **newtags;
1488   const char *def;
1489
1490   /* If the value matches the default then unset instead, to keep the database
1491    * tidy.  Older versions did not have this feature so your database may yet
1492    * have some default values stored in it. */
1493   if(value) {
1494     def = trackdb__default(track, name);
1495     if(def && !strcmp(value, def))
1496       value = 0;
1497   }
1498
1499   for(;;) {
1500     tid = trackdb_begin_transaction();
1501     if((err = gettrackdata(track, &t, &p, 0,
1502                            0, tid)) == DB_LOCK_DEADLOCK)
1503       goto fail;
1504     if(err == DB_NOTFOUND) break;
1505     if(name[0] == '_') {
1506       if(kvp_set(&t, name, value))
1507         if(trackdb_putdata(trackdb_tracksdb, track, t, tid, 0))
1508           goto fail;
1509     } else {
1510       /* get the old alias name */
1511       if(compute_alias(&oldalias, track, p, tid)) goto fail;
1512       /* get the old tags */
1513       if(!strcmp(name, "tags"))
1514         oldtags = parsetags(kvp_get(p, "tags"));
1515       /* set the value */
1516       if(kvp_set(&p, name, value))
1517         if(trackdb_putdata(trackdb_prefsdb, track, p, tid, 0))
1518           goto fail;
1519       /* compute the new alias name */
1520       if((err = compute_alias(&newalias, track, p, tid))) goto fail;
1521       /* check whether alias has changed */
1522       if(!(oldalias == newalias
1523            || (oldalias && newalias && !strcmp(oldalias, newalias)))) {
1524         /* adjust alias records to fit change */
1525         if(oldalias
1526            && trackdb_delkey(trackdb_tracksdb, oldalias, tid) == DB_LOCK_DEADLOCK)
1527           goto fail;
1528         if(newalias) {
1529           a = 0;
1530           kvp_set(&a, "_alias_for", track);
1531           if(trackdb_putdata(trackdb_tracksdb, newalias, a, tid, 0)) goto fail;
1532         }
1533       }
1534       /* check whether tags have changed */
1535       if(!strcmp(name, "tags")) {
1536         newtags = parsetags(value);
1537         while(*oldtags || *newtags) {
1538           if(*oldtags && *newtags) {
1539             cmp = strcmp(*oldtags, *newtags);
1540             if(!cmp) {
1541               /* keeping this tag */
1542               ++oldtags;
1543               ++newtags;
1544             } else if(cmp < 0)
1545               /* old tag fits into a gap in the new list, so delete old */
1546               goto delete_old;
1547             else
1548               /* new tag fits into a gap in the old list, so insert new */
1549               goto insert_new;
1550           } else if(*oldtags) {
1551             /* we've run out of new tags, so remaining old ones are to be
1552              * deleted */
1553           delete_old:
1554             if(trackdb_delkeydata(trackdb_tagsdb,
1555                                   *oldtags, track, tid) == DB_LOCK_DEADLOCK)
1556               goto fail;
1557             ++oldtags;
1558           } else {
1559             /* we've run out of old tags, so remainig new ones are to be
1560              * inserted */
1561           insert_new:
1562             if(register_tag(track, *newtags, tid)) goto fail;
1563             ++newtags;
1564           }
1565         }
1566       }
1567     }
1568     err = 0;
1569     break;
1570 fail:
1571     trackdb_abort_transaction(tid);
1572   }
1573   trackdb_commit_transaction(tid);
1574   return err == 0 ? 0 : -1;
1575 }
1576
1577 /* get a pref */
1578 const char *trackdb_get(const char *track,
1579                         const char *name) {
1580   return kvp_get(trackdb_get_all(track), name);
1581 }
1582
1583 /* get all prefs as a 0-terminated array */
1584 struct kvp *trackdb_get_all(const char *track) {
1585   struct kvp *t, *p, **pp;
1586   DB_TXN *tid;
1587
1588   for(;;) {
1589     tid = trackdb_begin_transaction();
1590     if(gettrackdata(track, &t, &p, 0, 0, tid) == DB_LOCK_DEADLOCK)
1591       goto fail;
1592     break;
1593 fail:
1594     trackdb_abort_transaction(tid);
1595   }
1596   trackdb_commit_transaction(tid);
1597   for(pp = &p; *pp; pp = &(*pp)->next)
1598     ;
1599   *pp = t;
1600   return p;
1601 }
1602
1603 /* resolve alias */
1604 const char *trackdb_resolve(const char *track) {
1605   DB_TXN *tid;
1606   const char *actual;
1607
1608   for(;;) {
1609     tid = trackdb_begin_transaction();
1610     if(gettrackdata(track, 0, 0, &actual, 0, tid) == DB_LOCK_DEADLOCK)
1611       goto fail;
1612     break;
1613 fail:
1614     trackdb_abort_transaction(tid);
1615   }
1616   trackdb_commit_transaction(tid);
1617   return actual;
1618 }
1619
1620 int trackdb_isalias(const char *track) {
1621   const char *actual = trackdb_resolve(track);
1622
1623   return strcmp(actual, track);
1624 }
1625
1626 /* test whether a track exists (perhaps an alias) */
1627 int trackdb_exists(const char *track) {
1628   DB_TXN *tid;
1629   int err;
1630
1631   for(;;) {
1632     tid = trackdb_begin_transaction();
1633     /* unusually, here we want the return value */
1634     if((err = gettrackdata(track, 0, 0, 0, 0, tid)) == DB_LOCK_DEADLOCK)
1635       goto fail;
1636     break;
1637 fail:
1638     trackdb_abort_transaction(tid);
1639   }
1640   trackdb_commit_transaction(tid);
1641   return (err == 0);
1642 }
1643
1644 /* return the list of tags */
1645 char **trackdb_alltags(void) {
1646   int e;
1647   struct vector v[1];
1648
1649   vector_init(v);
1650   WITH_TRANSACTION(trackdb_listkeys(trackdb_tagsdb, v, tid));
1651   return v->vec;
1652 }
1653
1654 /** @brief List all the keys in @p db
1655  * @param db Database
1656  * @param v Vector to store keys in
1657  * @param tid Transaction ID
1658  * @return 0 or DB_LOCK_DEADLOCK
1659  */
1660 int trackdb_listkeys(DB *db, struct vector *v, DB_TXN *tid) {
1661   int e;
1662   DBT k, d;
1663   DBC *const c = trackdb_opencursor(db, tid);
1664
1665   v->nvec = 0;
1666   memset(&k, 0, sizeof k);
1667   while(!(e = c->c_get(c, &k, prepare_data(&d), DB_NEXT_NODUP)))
1668     vector_append(v, xstrndup(k.data, k.size));
1669   switch(e) {
1670   case DB_NOTFOUND:
1671     break;
1672   case DB_LOCK_DEADLOCK:
1673     return e;
1674   default:
1675     fatal(0, "c->c_get: %s", db_strerror(e));
1676   }
1677   if((e = trackdb_closecursor(c)))
1678     return e;
1679   vector_terminate(v);
1680   return 0;
1681 }
1682
1683 /* return 1 iff sorted tag lists A and B have at least one member in common */
1684 int tag_intersection(char **a, char **b) {
1685   int cmp;
1686
1687   /* Same sort of logic as trackdb_set() above */
1688   while(*a && *b) {
1689     if(!(cmp = strcmp(*a, *b))) return 1;
1690     else if(cmp < 0) ++a;
1691     else ++b;
1692   }
1693   return 0;
1694 }
1695
1696 static pid_t choose_pid = -1;
1697 static int choose_fd;
1698 static random_callback *choose_callback;
1699 static struct dynstr choose_output;
1700 static unsigned choose_complete;
1701 static int choose_status;
1702 #define CHOOSE_RUNNING 1
1703 #define CHOOSE_READING 2
1704
1705 static void choose_finished(ev_source *ev, unsigned which) {
1706   choose_complete |= which;
1707   if(choose_complete != (CHOOSE_RUNNING|CHOOSE_READING))
1708     return;
1709   choose_pid = -1;
1710   if(choose_status == 0 && choose_output.nvec > 0) {
1711     dynstr_terminate(&choose_output);
1712     choose_callback(ev, xstrdup(choose_output.vec));
1713   } else
1714     choose_callback(ev, 0);
1715 }
1716
1717 /** @brief Called when @c disorder-choose terminates */
1718 static int choose_exited(ev_source *ev,
1719                          pid_t attribute((unused)) pid,
1720                          int status,
1721                          const struct rusage attribute((unused)) *rusage,
1722                          void attribute((unused)) *u) {
1723   if(status)
1724     error(0, "disorder-choose %s", wstat(status));
1725   choose_status = status;
1726   choose_finished(ev, CHOOSE_RUNNING);
1727   return 0;
1728 }
1729
1730 /** @brief Called with data from @c disorder-choose pipe */
1731 static int choose_readable(ev_source *ev,
1732                            ev_reader *reader,
1733                            void *ptr,
1734                            size_t bytes,
1735                            int eof,
1736                            void attribute((unused)) *u) {
1737   dynstr_append_bytes(&choose_output, ptr, bytes);
1738   ev_reader_consume(reader, bytes);
1739   if(eof)
1740     choose_finished(ev, CHOOSE_READING);
1741   return 0;
1742 }
1743
1744 static int choose_read_error(ev_source *ev,
1745                              int errno_value,
1746                              void attribute((unused)) *u) {
1747   error(errno_value, "error reading disorder-choose pipe");
1748   choose_finished(ev, CHOOSE_READING);
1749   return 0;
1750 }
1751
1752 /** @brief Request a random track
1753  * @param ev Event source
1754  * @param callback Called with random track or NULL
1755  * @return 0 if a request was initiated, else -1
1756  *
1757  * Initiates a random track choice.  @p callback will later be called back with
1758  * the choice (or NULL on error).  If a choice is already underway then -1 is
1759  * returned and there will be no additional callback.
1760  *
1761  * The caller shouldn't assume that the track returned actually exists (it
1762  * might be removed between the choice and the callback, or between being added
1763  * to the queue and being played).
1764  */
1765 int trackdb_request_random(ev_source *ev,
1766                            random_callback *callback) {
1767   int p[2];
1768   
1769   if(choose_pid != -1)
1770     return -1;                          /* don't run concurrent chooses */
1771   xpipe(p);
1772   cloexec(p[0]);
1773   choose_pid = subprogram(ev, p[1], "disorder-choose", (char *)0);
1774   choose_fd = p[0];
1775   xclose(p[1]);
1776   choose_callback = callback;
1777   choose_output.nvec = 0;
1778   choose_complete = 0;
1779   if(!ev_reader_new(ev, p[0], choose_readable, choose_read_error, 0,
1780                     "disorder-choose reader")) /* owns p[0] */
1781     fatal(0, "ev_reader_new for disorder-choose reader failed");
1782   ev_child(ev, choose_pid, 0, choose_exited, 0); /* owns the subprocess */
1783   return 0;
1784 }
1785
1786 /* get a track name given the prefs.  Set *used_db to 1 if we got the answer
1787  * from the prefs. */
1788 static const char *getpart(const char *track,
1789                            const char *context,
1790                            const char *part,
1791                            const struct kvp *p,
1792                            int *used_db) {
1793   const char *result;
1794   char *pref;
1795
1796   byte_xasprintf(&pref, "trackname_%s_%s", context, part);
1797   if((result = kvp_get(p, pref)))
1798     *used_db = 1;
1799   else
1800     result = trackname_part(track, context, part);
1801   assert(result != 0);
1802   return result;
1803 }
1804
1805 /* get a track name part, like trackname_part(), but taking the database into
1806  * account. */
1807 const char *trackdb_getpart(const char *track,
1808                             const char *context,
1809                             const char *part) {
1810   struct kvp *p;
1811   DB_TXN *tid;
1812   char *pref;
1813   const char *actual;
1814   int used_db, err;
1815
1816   /* construct the full pref */
1817   byte_xasprintf(&pref, "trackname_%s_%s", context, part);
1818   for(;;) {
1819     tid = trackdb_begin_transaction();
1820     if((err = gettrackdata(track, 0, &p, &actual, 0, tid)) == DB_LOCK_DEADLOCK)
1821       goto fail;
1822     break;
1823 fail:
1824     trackdb_abort_transaction(tid);
1825   }
1826   trackdb_commit_transaction(tid);
1827   return getpart(actual, context, part, p, &used_db);
1828 }
1829
1830 /* get the raw path name for @track@ (might be an alias) */
1831 const char *trackdb_rawpath(const char *track) {
1832   DB_TXN *tid;
1833   struct kvp *t;
1834   const char *path;
1835
1836   for(;;) {
1837     tid = trackdb_begin_transaction();
1838     if(gettrackdata(track, &t, 0, 0, 0, tid) == DB_LOCK_DEADLOCK)
1839       goto fail;
1840     break;
1841 fail:
1842     trackdb_abort_transaction(tid);
1843   }
1844   trackdb_commit_transaction(tid);
1845   if(!(path = kvp_get(t, "_path"))) path = track;
1846   return path;
1847 }
1848
1849 /* trackdb_list **************************************************************/
1850
1851 /* this is incredibly ugly, sorry, perhaps it will be rewritten to be actually
1852  * readable at some point */
1853
1854 /* return true if the basename of TRACK[0..TL-1], as defined by DL, matches RE.
1855  * If RE is a null pointer then it matches everything. */
1856 static int track_matches(size_t dl, const char *track, size_t tl,
1857                          const pcre *re) {
1858   int ovec[3], rc;
1859
1860   if(!re)
1861     return 1;
1862   track += dl + 1;
1863   tl -= (dl + 1);
1864   switch(rc = pcre_exec(re, 0, track, tl, 0, 0, ovec, 3)) {
1865   case PCRE_ERROR_NOMATCH: return 0;
1866   default:
1867     if(rc < 0) {
1868       error(0, "pcre_exec returned %d, subject '%s'", rc, track);
1869       return 0;
1870     }
1871     return 1;
1872   }
1873 }
1874
1875 static int do_list(struct vector *v, const char *dir,
1876                    enum trackdb_listable what, const pcre *re, DB_TXN *tid) {
1877   DBC *cursor;
1878   DBT k, d;
1879   size_t dl;
1880   char *ptr;
1881   int err;
1882   size_t l, last_dir_len = 0;
1883   char *last_dir = 0, *track;
1884   struct kvp *p;
1885
1886   dl = strlen(dir);
1887   cursor = trackdb_opencursor(trackdb_tracksdb, tid);
1888   make_key(&k, dir);
1889   prepare_data(&d);
1890   /* find the first key >= dir */
1891   err = cursor->c_get(cursor, &k, &d, DB_SET_RANGE);
1892   /* keep going while we're dealing with <dir/anything> */
1893   while(err == 0
1894         && k.size > dl
1895         && ((char *)k.data)[dl] == '/'
1896         && !memcmp(k.data, dir, dl)) {
1897     ptr = memchr((char *)k.data + dl + 1, '/', k.size - (dl + 1));
1898     if(ptr) {
1899       /* we have <dir/component/anything>, so <dir/component> is a directory */
1900       l = ptr - (char *)k.data;
1901       if(what & trackdb_directories)
1902         if(!(last_dir
1903              && l == last_dir_len
1904              && !memcmp(last_dir, k.data, l))) {
1905           last_dir = xstrndup(k.data, last_dir_len = l);
1906           if(track_matches(dl, k.data, l, re))
1907             vector_append(v, last_dir);
1908         }
1909     } else {
1910       /* found a plain file */
1911       if((what & trackdb_files)) {
1912         track = xstrndup(k.data, k.size);
1913         if((err = trackdb_getdata(trackdb_prefsdb,
1914                                   track, &p, tid)) == DB_LOCK_DEADLOCK)
1915           goto deadlocked;
1916         /* There's an awkward question here...
1917          *
1918          * If a track shares a directory with its alias then we could
1919          * do one of three things:
1920          * - report both.  Looks ridiculuous in most UIs.
1921          * - report just the alias.  Remarkably inconvenient to write
1922          *   UI code for!
1923          * - report just the real name.  Ugly if the UI doesn't prettify
1924          *   names via the name parts.
1925          */
1926 #if 1
1927         /* If this file is an alias for a track in the same directory then we
1928          * skip it */
1929         struct kvp *t = kvp_urldecode(d.data, d.size);
1930         const char *alias_target = kvp_get(t, "_alias_for");
1931         if(!(alias_target
1932              && !strcmp(d_dirname(alias_target),
1933                         d_dirname(track))))
1934           if(track_matches(dl, k.data, k.size, re))
1935             vector_append(v, track);
1936 #else
1937         /* if this file has an alias in the same directory then we skip it */
1938            char *alias;
1939         if((err = compute_alias(&alias, track, p, tid)))
1940           goto deadlocked;
1941         if(!(alias && !strcmp(d_dirname(alias), d_dirname(track))))
1942           if(track_matches(dl, k.data, k.size, re))
1943             vector_append(v, track);
1944 #endif
1945       }
1946     }
1947     err = cursor->c_get(cursor, &k, &d, DB_NEXT);
1948   }
1949   switch(err) {
1950   case 0:
1951     break;
1952   case DB_NOTFOUND:
1953     err = 0;
1954     break;
1955   case DB_LOCK_DEADLOCK:
1956     error(0, "error querying database: %s", db_strerror(err));
1957     break;
1958   default:
1959     fatal(0, "error querying database: %s", db_strerror(err));
1960   }
1961 deadlocked:
1962   if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
1963   return err;
1964 }
1965
1966 /* return the directories or files below @dir@ */
1967 char **trackdb_list(const char *dir, int *np, enum trackdb_listable what,
1968                     const pcre *re) {
1969   DB_TXN *tid;
1970   int n;
1971   struct vector v;
1972
1973   vector_init(&v);
1974   for(;;) {
1975     tid = trackdb_begin_transaction();
1976     v.nvec = 0;
1977     if(dir) {
1978       if(do_list(&v, dir, what, re, tid))
1979         goto fail;
1980     } else {
1981       for(n = 0; n < config->collection.n; ++n)
1982         if(do_list(&v, config->collection.s[n].root, what, re, tid))
1983           goto fail;
1984     }
1985     break;
1986 fail:
1987     trackdb_abort_transaction(tid);
1988   }
1989   trackdb_commit_transaction(tid);
1990   vector_terminate(&v);
1991   if(np)
1992     *np = v.nvec;
1993   return v.vec;
1994 }
1995
1996 /* If S is tag:something, return something.  Else return 0. */
1997 static const char *checktag(const char *s) {
1998   if(!strncmp(s, "tag:", 4))
1999     return s + 4;
2000   else
2001     return 0;
2002 }
2003
2004 /* return a list of tracks containing all of the words given.  If you
2005  * ask for only stopwords you get no tracks. */
2006 char **trackdb_search(char **wordlist, int nwordlist, int *ntracks) {
2007   const char **w, *best = 0, *tag;
2008   char **twords, **tags;
2009   char *istag;
2010   int i, j, n, err, what;
2011   DBC *cursor = 0;
2012   DBT k, d;
2013   struct vector u, v;
2014   DB_TXN *tid;
2015   struct kvp *p;
2016   int ntags = 0;
2017   DB *db;
2018   const char *dbname;
2019
2020   *ntracks = 0;                         /* for early returns */
2021   /* normalize all the words */
2022   w = xmalloc(nwordlist * sizeof (char *));
2023   istag = xmalloc_noptr(nwordlist);
2024   for(n = 0; n < nwordlist; ++n) {
2025     uint32_t *w32;
2026     size_t nw32;
2027
2028     w[n] = utf8_casefold_compat(wordlist[n], strlen(wordlist[n]), 0);
2029     if(checktag(w[n])) {
2030       ++ntags;         /* count up tags */
2031       /* Normalize the tag */
2032       w[n] = normalize_tag(w[n] + 4, strlen(w[n] + 4));
2033       istag[n] = 1;
2034     } else {
2035       /* Normalize the search term by removing combining characters */
2036       if(!(w32 = utf8_to_utf32(w[n], strlen(w[n]), &nw32)))
2037         return 0;
2038       nw32 = remove_combining_chars(w32, nw32);
2039       if(!(w[n] = utf32_to_utf8(w32, nw32, 0)))
2040         return 0;
2041       istag[n] = 0;
2042     }
2043   }
2044   /* find the longest non-stopword */
2045   for(n = 0; n < nwordlist; ++n)
2046     if(!istag[n] && !stopword(w[n]))
2047       if(!best || strlen(w[n]) > strlen(best))
2048         best = w[n];
2049   /* TODO: we should at least in principal be able to identify the word or tag
2050    * with the least matches in log time, and choose that as our primary search
2051    * term. */
2052   if(ntags && !best) {
2053     /* Only tags are listed.  We limit to the first and narrow down with the
2054      * rest. */
2055     best = istag[0] ? w[0] : 0;
2056     db = trackdb_tagsdb;
2057     dbname = "tags";
2058   } else if(best) {
2059     /* We can limit to some word. */
2060     db = trackdb_searchdb;
2061     dbname = "search";
2062   } else {
2063     /* Only stopwords */
2064     return 0;
2065   }
2066   vector_init(&u);
2067   vector_init(&v);
2068   for(;;) {
2069     tid = trackdb_begin_transaction();
2070     /* find all the tracks that have that word */
2071     make_key(&k, best);
2072     prepare_data(&d);
2073     what = DB_SET;
2074     v.nvec = 0;
2075     cursor = trackdb_opencursor(db, tid);
2076     while(!(err = cursor->c_get(cursor, &k, &d, what))) {
2077       vector_append(&v, xstrndup(d.data, d.size));
2078       what = DB_NEXT_DUP;
2079     }
2080     switch(err) {
2081     case DB_NOTFOUND:
2082       err = 0;
2083       break;
2084     case DB_LOCK_DEADLOCK:
2085       error(0, "error querying %s database: %s", dbname, db_strerror(err));
2086       break;
2087     default:
2088       fatal(0, "error querying %s database: %s", dbname, db_strerror(err));
2089     }
2090     if(trackdb_closecursor(cursor)) err = DB_LOCK_DEADLOCK;
2091     cursor = 0;
2092     /* do a naive search over that (hopefuly fairly small) list of tracks */
2093     u.nvec = 0;
2094     for(n = 0; n < v.nvec; ++n) {
2095       if((err = gettrackdata(v.vec[n], 0, &p, 0, 0, tid) == DB_LOCK_DEADLOCK))
2096         goto fail;
2097       else if(err) {
2098         error(0, "track %s unexpected error: %s", v.vec[n], db_strerror(err));
2099         continue;
2100       }
2101       twords = track_to_words(v.vec[n], p);
2102       tags = parsetags(kvp_get(p, "tags"));
2103       for(i = 0; i < nwordlist; ++i) {
2104         if(istag[i]) {
2105           tag = w[i];
2106           /* Track must have this tag */
2107           for(j = 0; tags[j]; ++j)
2108             if(!strcmp(tag, tags[j])) break; /* tag found */
2109           if(!tags[j]) break;           /* tag not found */
2110         } else {
2111           /* Track must contain this word */
2112           for(j = 0; twords[j]; ++j)
2113             if(!strcmp(w[i], twords[j])) break; /* word found */
2114           if(!twords[j]) break;         /* word not found */
2115         }
2116       }
2117       if(i >= nwordlist)                /* all words found */
2118         vector_append(&u, v.vec[n]);
2119     }
2120     break;
2121   fail:
2122     trackdb_closecursor(cursor);
2123     cursor = 0;
2124     trackdb_abort_transaction(tid);
2125     info("retrying search");
2126   }
2127   trackdb_commit_transaction(tid);
2128   vector_terminate(&u);
2129   if(ntracks)
2130     *ntracks = u.nvec;
2131   return u.vec;
2132 }
2133
2134 /* trackdb_scan **************************************************************/
2135
2136 int trackdb_scan(const char *root,
2137                  int (*callback)(const char *track,
2138                                  struct kvp *data,
2139                                  struct kvp *prefs,
2140                                  void *u,
2141                                  DB_TXN *tid),
2142                  void *u,
2143                  DB_TXN *tid) {
2144   DBC *cursor;
2145   DBT k, d, pd;
2146   const size_t root_len = root ? strlen(root) : 0;
2147   int err, cberr;
2148   struct kvp *data, *prefs;
2149   const char *track;
2150
2151   cursor = trackdb_opencursor(trackdb_tracksdb, tid);
2152   if(root)
2153     err = cursor->c_get(cursor, make_key(&k, root), prepare_data(&d),
2154                         DB_SET_RANGE);
2155   else {
2156     memset(&k, 0, sizeof k);
2157     err = cursor->c_get(cursor, &k, prepare_data(&d),
2158                         DB_FIRST);
2159   }
2160   while(!err) {
2161     if(!root
2162        || (k.size > root_len
2163            && !strncmp(k.data, root, root_len)
2164            && ((char *)k.data)[root_len] == '/')) {
2165       data = kvp_urldecode(d.data, d.size);
2166       if(kvp_get(data, "_path")) {
2167         track = xstrndup(k.data, k.size);
2168         /* TODO: trackdb_prefsdb is currently a DB_HASH.  This means we have to
2169          * do a lookup for every single track.  In fact this is quite quick:
2170          * with around 10,000 tracks a complete scan is around 0.3s on my
2171          * 2.2GHz Athlon.  However, if it were a DB_BTREE, we could do the same
2172          * linear walk as we already do over trackdb_tracksdb, and probably get
2173          * even higher performance.  That would require upgrade logic to
2174          * translate old databases though.
2175          */
2176         switch(err = trackdb_prefsdb->get(trackdb_prefsdb, tid, &k,
2177                                           prepare_data(&pd), 0)) {
2178         case 0:
2179           prefs = kvp_urldecode(pd.data, pd.size);
2180           break;
2181         case DB_NOTFOUND:
2182           prefs = 0;
2183           break;
2184         case DB_LOCK_DEADLOCK:
2185           error(0, "getting prefs: %s", db_strerror(err));
2186           trackdb_closecursor(cursor);
2187           return err;
2188         default:
2189           fatal(0, "getting prefs: %s", db_strerror(err));
2190         }
2191         /* Advance to the next track before the callback so that the callback
2192          * may safely delete the track */
2193         err = cursor->c_get(cursor, &k, &d, DB_NEXT);
2194         if((cberr = callback(track, data, prefs, u, tid))) {
2195           err = cberr;
2196           break;
2197         }
2198       } else
2199         err = cursor->c_get(cursor, &k, &d, DB_NEXT);
2200     } else
2201       break;
2202   }
2203   trackdb_closecursor(cursor);
2204   switch(err) {
2205   case EINTR:
2206     return err;
2207   case 0:
2208   case DB_NOTFOUND:
2209     return 0;
2210   case DB_LOCK_DEADLOCK:
2211     error(0, "c->c_get: %s", db_strerror(err));
2212     return err;
2213   default:
2214     fatal(0, "c->c_get: %s", db_strerror(err));
2215   }
2216 }
2217
2218 /* trackdb_rescan ************************************************************/
2219
2220 /** @brief Node in the list of rescan-complete callbacks */
2221 struct rescanned_node {
2222   struct rescanned_node *next;
2223   void (*rescanned)(void *ru);
2224   void *ru;
2225 };
2226
2227 /** @brief List of rescan-complete callbacks */
2228 static struct rescanned_node *rescanned_list;
2229
2230 /** @brief Add a rescan completion callback */
2231 void trackdb_add_rescanned(void (*rescanned)(void *ru),
2232                            void *ru) {
2233   if(rescanned) {
2234     struct rescanned_node *n = xmalloc(sizeof *n);
2235     n->next = rescanned_list;
2236     n->rescanned = rescanned;
2237     n->ru = ru;
2238     rescanned_list = n;
2239   }
2240 }
2241
2242 /* called when the rescanner terminates */
2243 static int reap_rescan(ev_source attribute((unused)) *ev,
2244                        pid_t pid,
2245                        int status,
2246                        const struct rusage attribute((unused)) *rusage,
2247                        void attribute((unused)) *u) {
2248   if(pid == rescan_pid) rescan_pid = -1;
2249   if(status)
2250     error(0, RESCAN": %s", wstat(status));
2251   else
2252     D((RESCAN" terminated: %s", wstat(status)));
2253   /* Our cache of file lookups is out of date now */
2254   cache_clean(&cache_files_type);
2255   eventlog("rescanned", (char *)0);
2256   /* Call rescanned callbacks */
2257   while(rescanned_list) {
2258     void (*rescanned)(void *u_) = rescanned_list->rescanned;
2259     void *ru = rescanned_list->ru;
2260
2261     rescanned_list = rescanned_list->next;
2262     rescanned(ru);
2263   }
2264   return 0;
2265 }
2266
2267 /** @brief Initiate a rescan
2268  * @param ev Event loop or 0 to block
2269  * @param recheck 1 to recheck lengths, 0 to suppress check
2270  * @param rescanned Called on completion (if not NULL)
2271  * @param ru Passed to @p rescanned
2272  */
2273 void trackdb_rescan(ev_source *ev, int recheck,
2274                     void (*rescanned)(void *ru),
2275                     void *ru) {
2276   int w;
2277
2278   if(rescan_pid != -1) {
2279     trackdb_add_rescanned(rescanned, ru);
2280     error(0, "rescan already underway");
2281     return;
2282   }
2283   rescan_pid = subprogram(ev, -1, RESCAN,
2284                           recheck ? "--check" : "--no-check",
2285                           (char *)0);
2286   trackdb_add_rescanned(rescanned, ru);
2287   if(ev) {
2288     ev_child(ev, rescan_pid, 0, reap_rescan, 0);
2289     D(("started rescanner"));
2290   } else {
2291     /* This is the first rescan, we block until it is complete */
2292     while(waitpid(rescan_pid, &w, 0) < 0 && errno == EINTR)
2293       ;
2294     reap_rescan(0, rescan_pid, w, 0, 0);
2295   }
2296 }
2297
2298 int trackdb_rescan_cancel(void) {
2299   if(rescan_pid == -1) return 0;
2300   if(kill(rescan_pid, SIGTERM) < 0)
2301     fatal(errno, "error killing rescanner");
2302   rescan_pid = -1;
2303   return 1;
2304 }
2305
2306 /** @brief Return true if a rescan is underway */
2307 int trackdb_rescan_underway(void) {
2308   return rescan_pid != -1;
2309 }
2310
2311 /* global prefs **************************************************************/
2312
2313 void trackdb_set_global(const char *name,
2314                         const char *value,
2315                         const char *who) {
2316   DB_TXN *tid;
2317   int err;
2318   int state;
2319
2320   for(;;) {
2321     tid = trackdb_begin_transaction();
2322     if(!(err = trackdb_set_global_tid(name, value, tid)))
2323       break;
2324     trackdb_abort_transaction(tid);
2325   }
2326   trackdb_commit_transaction(tid);
2327   /* log important state changes */
2328   if(!strcmp(name, "playing")) {
2329     state = !value || !strcmp(value, "yes");
2330     info("playing %s by %s",
2331          state ? "enabled" : "disabled",
2332          who ? who : "-");
2333     eventlog("state", state ? "enable_play" : "disable_play", (char *)0);
2334   }
2335   if(!strcmp(name, "random-play")) {
2336     state = !value || !strcmp(value, "yes");
2337     info("random play %s by %s",
2338          state ? "enabled" : "disabled",
2339          who ? who : "-");
2340     eventlog("state", state ? "enable_random" : "disable_random", (char *)0);
2341   }
2342 }
2343
2344 int trackdb_set_global_tid(const char *name,
2345                            const char *value,
2346                            DB_TXN *tid) {
2347   DBT k, d;
2348   int err;
2349
2350   memset(&k, 0, sizeof k);
2351   memset(&d, 0, sizeof d);
2352   k.data = (void *)name;
2353   k.size = strlen(name);
2354   if(value) {
2355     d.data = (void *)value;
2356     d.size = strlen(value);
2357   }
2358   if(value)
2359     err = trackdb_globaldb->put(trackdb_globaldb, tid, &k, &d, 0);
2360   else
2361     err = trackdb_globaldb->del(trackdb_globaldb, tid, &k, 0);
2362   if(err == DB_LOCK_DEADLOCK) return err;
2363   if(err)
2364     fatal(0, "error updating database: %s", db_strerror(err));
2365   return 0;
2366 }
2367
2368 const char *trackdb_get_global(const char *name) {
2369   DB_TXN *tid;
2370   int err;
2371   const char *r;
2372
2373   for(;;) {
2374     tid = trackdb_begin_transaction();
2375     if(!(err = trackdb_get_global_tid(name, tid, &r)))
2376       break;
2377     trackdb_abort_transaction(tid);
2378   }
2379   trackdb_commit_transaction(tid);
2380   return r;
2381 }
2382
2383 int trackdb_get_global_tid(const char *name,
2384                            DB_TXN *tid,
2385                            const char **rp) {
2386   DBT k, d;
2387   int err;
2388
2389   memset(&k, 0, sizeof k);
2390   k.data = (void *)name;
2391   k.size = strlen(name);
2392   switch(err = trackdb_globaldb->get(trackdb_globaldb, tid, &k,
2393                                      prepare_data(&d), 0)) {
2394   case 0:
2395     *rp = xstrndup(d.data, d.size);
2396     return 0;
2397   case DB_NOTFOUND:
2398     *rp = 0;
2399     return 0;
2400   case DB_LOCK_DEADLOCK:
2401     return err;
2402   default:
2403     fatal(0, "error reading database: %s", db_strerror(err));
2404   }
2405 }
2406
2407 /** @brief Retrieve the most recently added tracks
2408  * @param ntracksp Where to put count, or 0
2409  * @param maxtracks Maximum number of tracks to retrieve
2410  * @return null-terminated array of track names
2411  *
2412  * The most recently added track is first in the array.
2413  */
2414 char **trackdb_new(int *ntracksp,
2415                    int maxtracks) {
2416   DB_TXN *tid;
2417   char **tracks;
2418
2419   for(;;) {
2420     tid = trackdb_begin_transaction();
2421     tracks = trackdb_new_tid(ntracksp, maxtracks, tid);
2422     if(tracks)
2423       break;
2424     trackdb_abort_transaction(tid);
2425   }
2426   trackdb_commit_transaction(tid);
2427   return tracks;
2428 }
2429
2430 /** @brief Retrieve the most recently added tracks
2431  * @param ntracksp Where to put count, or 0
2432  * @param maxtracks Maximum number of tracks to retrieve, or 0 for all
2433  * @param tid Transaction ID
2434  * @return null-terminated array of track names, or NULL on deadlock
2435  *
2436  * The most recently added track is first in the array.
2437  */
2438 static char **trackdb_new_tid(int *ntracksp,
2439                               int maxtracks,
2440                               DB_TXN *tid) {
2441   DBC *c;
2442   DBT k, d;
2443   int err = 0;
2444   struct vector tracks[1];
2445   hash *h = hash_new(1);
2446
2447   vector_init(tracks);
2448   c = trackdb_opencursor(trackdb_noticeddb, tid);
2449   while((maxtracks <= 0 || tracks->nvec < maxtracks)
2450         && !(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_PREV))) {
2451     char *const track = xstrndup(d.data, d.size);
2452     /* Don't add any track more than once */
2453     if(hash_add(h, track, "", HASH_INSERT))
2454       continue;
2455     /* See if the track still exists */
2456     err = trackdb_getdata(trackdb_tracksdb, track, NULL/*kp*/, tid);
2457     if(err == DB_NOTFOUND)
2458       continue;                         /* It doesn't, skip it */
2459     if(err == DB_LOCK_DEADLOCK)
2460       break;                            /* Doh */
2461     vector_append(tracks, track);
2462   }
2463   switch(err) {
2464   case 0:                               /* hit maxtracks */
2465   case DB_NOTFOUND:                     /* ran out of tracks */
2466     break;
2467   case DB_LOCK_DEADLOCK:
2468     trackdb_closecursor(c);
2469     return 0;
2470   default:
2471     fatal(0, "error reading noticed.db: %s", db_strerror(err));
2472   }
2473   if((err = trackdb_closecursor(c)))
2474     return 0;                           /* deadlock */
2475   vector_terminate(tracks);
2476   if(ntracksp)
2477     *ntracksp = tracks->nvec;
2478   return tracks->vec;
2479 }
2480
2481 /** @brief Expire noticed.db
2482  * @param earliest Earliest timestamp to keep
2483  */
2484 void trackdb_expire_noticed(time_t earliest) {
2485   DB_TXN *tid;
2486
2487   for(;;) {
2488     tid = trackdb_begin_transaction();
2489     if(!trackdb_expire_noticed_tid(earliest, tid))
2490       break;
2491     trackdb_abort_transaction(tid);
2492   }
2493   trackdb_commit_transaction(tid);
2494 }
2495
2496 /** @brief Expire noticed.db
2497  * @param earliest Earliest timestamp to keep
2498  * @param tid Transaction ID
2499  * @return 0 or DB_LOCK_DEADLOCK
2500  */
2501 static int trackdb_expire_noticed_tid(time_t earliest, DB_TXN *tid) {
2502   DBC *c;
2503   DBT k, d;
2504   int err = 0, ret;
2505   time_t when;
2506   uint32_t *kk;
2507   int count = 0;
2508
2509   c = trackdb_opencursor(trackdb_noticeddb, tid);
2510   while(!(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_NEXT))) {
2511     kk = k.data;
2512     when = (time_t)(((uint64_t)ntohl(kk[0]) << 32) + ntohl(kk[1]));
2513     if(when >= earliest)
2514       break;
2515     if((err = c->c_del(c, 0))) {
2516       if(err != DB_LOCK_DEADLOCK)
2517         fatal(0, "error deleting expired noticed.db entry: %s",
2518               db_strerror(err));
2519       break;
2520     }
2521     ++count;
2522   }
2523   if(err == DB_NOTFOUND)
2524     err = 0;
2525   if(err && err != DB_LOCK_DEADLOCK)
2526     fatal(0, "error expiring noticed.db: %s", db_strerror(err));
2527   ret = err;
2528   if((err = trackdb_closecursor(c))) {
2529     if(err != DB_LOCK_DEADLOCK)
2530       fatal(0, "error closing cursor: %s", db_strerror(err));
2531     ret = err;
2532   }
2533   if(!ret && count)
2534     info("expired %d tracks from noticed.db", count);
2535   return ret;
2536 }
2537
2538 /* tidying up ****************************************************************/
2539
2540 void trackdb_gc(void) {
2541   int err;
2542   char **logfiles;
2543
2544   if((err = trackdb_env->txn_checkpoint(trackdb_env,
2545                                         config->checkpoint_kbyte,
2546                                         config->checkpoint_min,
2547                                         0)))
2548     fatal(0, "trackdb_env->txn_checkpoint: %s", db_strerror(err));
2549   if((err = trackdb_env->log_archive(trackdb_env, &logfiles, DB_ARCH_REMOVE)))
2550     fatal(0, "trackdb_env->log_archive: %s", db_strerror(err));
2551   /* This makes catastrophic recovery impossible.  However, the user can still
2552    * preserve the important data by using disorder-dump to snapshot their
2553    * prefs, and later to restore it.  This is likely to have much small
2554    * long-term storage requirements than record the db logfiles. */
2555 }
2556
2557 /* user database *************************************************************/
2558
2559 /** @brief Return true if @p user is trusted */
2560 static int trusted(const char *user) {
2561   int n;
2562
2563   for(n = 0; (n < config->trust.n
2564               && strcmp(config->trust.s[n], user)); ++n)
2565     ;
2566   return n < config->trust.n;
2567 }
2568
2569 /** @brief Return non-zero for a valid username
2570  *
2571  * Currently we only allow the letters and digits in ASCII.  We could be more
2572  * liberal than this but it is a nice simple test.  It is critical that
2573  * semicolons are never allowed.
2574  *
2575  * NB also used by playlist_parse_name() to validate playlist names!
2576  */
2577 int valid_username(const char *user) {
2578   if(!*user)
2579     return 0;
2580   while(*user) {
2581     const uint8_t c = *user++;
2582     /* For now we are very strict */
2583     if((c >= 'a' && c <= 'z')
2584        || (c >= 'A' && c <= 'Z')
2585        || (c >= '0' && c <= '9'))
2586       /* ok */;
2587     else
2588       return 0;
2589   }
2590   return 1;
2591 }
2592
2593 /** @brief Add a user */
2594 static int create_user(const char *user,
2595                        const char *password,
2596                        const char *rights,
2597                        const char *email,
2598                        const char *confirmation,
2599                        DB_TXN *tid,
2600                        uint32_t flags) {
2601   struct kvp *k = 0;
2602   char s[64];
2603
2604   /* sanity check user */
2605   if(!valid_username(user)) {
2606     error(0, "invalid username '%s'", user);
2607     return -1;
2608   }
2609   if(parse_rights(rights, 0, 1)) {
2610     error(0, "invalid rights string");
2611     return -1;
2612   }
2613   /* data for this user */
2614   if(password)
2615     kvp_set(&k, "password", password);
2616   kvp_set(&k, "rights", rights);
2617   if(email)
2618     kvp_set(&k, "email", email);
2619   if(confirmation)
2620     kvp_set(&k, "confirmation", confirmation);
2621   snprintf(s, sizeof s, "%jd", (intmax_t)xtime(0));
2622   kvp_set(&k, "created", s);
2623   return trackdb_putdata(trackdb_usersdb, user, k, tid, flags);
2624 }
2625
2626 /** @brief Add one pre-existing user */
2627 static int one_old_user(const char *user, const char *password,
2628                         DB_TXN *tid) {
2629   const char *rights;
2630
2631   /* www-data doesn't get added */
2632   if(!strcmp(user, "www-data")) {
2633     info("not adding www-data to user database");
2634     return 0;
2635   }
2636   /* pick rights */
2637   if(!strcmp(user, "root"))
2638     rights = "all";
2639   else if(trusted(user)) {
2640     rights_type r;
2641
2642     parse_rights(config->default_rights, &r, 1);
2643     r &= ~(rights_type)(RIGHT_SCRATCH__MASK|RIGHT_MOVE__MASK|RIGHT_REMOVE__MASK);
2644     r |= (RIGHT_ADMIN|RIGHT_RESCAN
2645           |RIGHT_SCRATCH_ANY|RIGHT_MOVE_ANY|RIGHT_REMOVE_ANY);
2646     rights = rights_string(r);
2647   } else
2648     rights = config->default_rights;
2649   return create_user(user, password, rights, 0/*email*/, 0/*confirmation*/,
2650                      tid, DB_NOOVERWRITE);
2651 }
2652
2653 static int trackdb_old_users_tid(DB_TXN *tid) {
2654   int n;
2655
2656   for(n = 0; n < config->allow.n; ++n) {
2657     switch(one_old_user(config->allow.s[n].s[0], config->allow.s[n].s[1],
2658                         tid)) {
2659     case 0:
2660       info("created user %s from 'allow' directive", config->allow.s[n].s[0]);
2661       break;
2662     case DB_KEYEXIST:
2663       error(0, "user %s already exists, delete 'allow' directive",
2664             config->allow.s[n].s[0]);
2665           /* This won't ever become fatal - eventually 'allow' will be
2666            * disabled. */
2667       break;
2668     case DB_LOCK_DEADLOCK:
2669       return DB_LOCK_DEADLOCK;
2670     }
2671   }
2672   return 0;
2673 }
2674
2675 /** @brief Read old 'allow' directives and copy them to the users database */
2676 void trackdb_old_users(void) {
2677   int e;
2678
2679   if(config->allow.n)
2680     WITH_TRANSACTION(trackdb_old_users_tid(tid));
2681 }
2682
2683 /** @brief Create a root user in the user database if there is none */
2684 void trackdb_create_root(void) {
2685   int e;
2686   uint8_t pwbin[12];
2687   char *pw;
2688
2689   /* Choose a new root password */
2690   gcry_randomize(pwbin, sizeof pwbin, GCRY_STRONG_RANDOM);
2691   pw = mime_to_base64(pwbin, sizeof pwbin);
2692   /* Create the root user if it does not exist */
2693   WITH_TRANSACTION(create_user("root", pw, "all",
2694                                0/*email*/, 0/*confirmation*/,
2695                                tid, DB_NOOVERWRITE));
2696   if(e == 0)
2697     info("created root user");
2698 }
2699
2700 /** @brief Find a user's password from the database
2701  * @param user Username
2702  * @return Password or NULL
2703  *
2704  * Only works if running as a user that can read the database!
2705  *
2706  * If the user exists but has no password, "" is returned.
2707  */
2708 const char *trackdb_get_password(const char *user) {
2709   int e;
2710   struct kvp *k;
2711   const char *password;
2712
2713   WITH_TRANSACTION(trackdb_getdata(trackdb_usersdb, user, &k, tid));
2714   if(e)
2715     return 0;
2716   password = kvp_get(k, "password");
2717   return password ? password : "";
2718 }
2719
2720 /** @brief Add a new user
2721  * @param user Username
2722  * @param password Password or NULL
2723  * @param rights Initial rights
2724  * @param email Email address or NULL
2725  * @param confirmation Confirmation string or NULL
2726  * @return 0 on success, non-0 on error
2727  */
2728 int trackdb_adduser(const char *user,
2729                     const char *password,
2730                     const char *rights,
2731                     const char *email,
2732                     const char *confirmation) {
2733   int e;
2734
2735   WITH_TRANSACTION(create_user(user, password, rights, email, confirmation,
2736                                tid, DB_NOOVERWRITE));
2737   if(e) {
2738     error(0, "cannot create user '%s' because they already exist", user);
2739     return -1;
2740   } else {
2741     if(email)
2742       info("created user '%s' with rights '%s' and email address '%s'",
2743            user, rights, email);
2744     else
2745       info("created user '%s' with rights '%s'", user, rights);
2746     eventlog("user_add", user, (char *)0);
2747     return 0;
2748   }
2749 }
2750
2751 /** @brief Delete a user
2752  * @param user User to delete
2753  * @return 0 on success, non-0 if the user didn't exist anyway
2754  */
2755 int trackdb_deluser(const char *user) {
2756   int e;
2757
2758   WITH_TRANSACTION(trackdb_delkey(trackdb_usersdb, user, tid));
2759   if(e) {
2760     error(0, "cannot delete user '%s' because they do not exist", user);
2761     return -1;
2762   }
2763   info("deleted user '%s'", user);
2764   eventlog("user_delete", user, (char *)0);
2765   return 0;
2766 }
2767
2768 /** @brief Get user information
2769  * @param user User to query
2770  * @return Linked list of user information or NULL if user does not exist
2771  *
2772  * Every user has at least a @c rights entry so NULL can be used to mean no
2773  * such user safely.
2774  */
2775 struct kvp *trackdb_getuserinfo(const char *user) {
2776   int e;
2777   struct kvp *k;
2778
2779   WITH_TRANSACTION(trackdb_getdata(trackdb_usersdb, user, &k, tid));
2780   if(e)
2781     return 0;
2782   else
2783     return k;
2784 }
2785
2786 /** @brief Edit user information
2787  * @param user User to edit
2788  * @param key Key to change
2789  * @param value Value to set, or NULL to remove
2790  * @param tid Transaction ID
2791  * @return 0, DB_LOCK_DEADLOCK or DB_NOTFOUND
2792  */
2793 static int trackdb_edituserinfo_tid(const char *user, const char *key,
2794                                     const char *value, DB_TXN *tid) {
2795   struct kvp *k;
2796   int e;
2797
2798   if((e = trackdb_getdata(trackdb_usersdb, user, &k, tid)))
2799     return e;
2800   if(!kvp_set(&k, key, value))
2801     return 0;                           /* no change */
2802   return trackdb_putdata(trackdb_usersdb, user, k, tid, 0);
2803 }
2804
2805 /** @brief Edit user information
2806  * @param user User to edit
2807  * @param key Key to change
2808  * @param value Value to set, or NULL to remove
2809  * @return 0 on success, non-0 on error
2810  */
2811 int trackdb_edituserinfo(const char *user,
2812                          const char *key, const char *value) {
2813   int e;
2814
2815   if(!strcmp(key, "rights")) {
2816     if(!value) {
2817       error(0, "cannot remove 'rights' key from user '%s'", user);
2818       return -1;
2819     }
2820     if(parse_rights(value, 0, 1)) {
2821       error(0, "invalid rights string");
2822       return -1;
2823     }
2824   } else if(!strcmp(key, "email")) {
2825     if(*value) {
2826       if(!email_valid(value)) {
2827         error(0, "invalid email address '%s' for user '%s'", value, user);
2828         return -1;
2829       }
2830     } else
2831       value = 0;                        /* no email -> remove key */
2832   } else if(!strcmp(key, "created")) {
2833     error(0, "cannot change creation date for user '%s'", user);
2834     return -1;
2835   } else if(strcmp(key, "password")
2836             && !strcmp(key, "confirmation")) {
2837     error(0, "unknown user info key '%s' for user '%s'", key, user);
2838     return -1;
2839   }
2840   WITH_TRANSACTION(trackdb_edituserinfo_tid(user, key, value, tid));
2841   if(e) {
2842     error(0, "unknown user '%s'", user);
2843     return -1;
2844   } else {
2845     eventlog("user_edit", user, key, (char *)0);
2846     return 0;
2847   }
2848 }
2849
2850 /** @brief List all users
2851  * @return NULL-terminated list of users
2852  */
2853 char **trackdb_listusers(void) {
2854   int e;
2855   struct vector v[1];
2856
2857   vector_init(v);
2858   WITH_TRANSACTION(trackdb_listkeys(trackdb_usersdb, v, tid));
2859   return v->vec;
2860 }
2861
2862 /** @brief Confirm a user registration
2863  * @param user Username
2864  * @param confirmation Confirmation string
2865  * @param rightsp Where to put user rights
2866  * @param tid Transaction ID
2867  * @return 0 on success, non-0 on error
2868  */
2869 static int trackdb_confirm_tid(const char *user, const char *confirmation,
2870                                rights_type *rightsp,
2871                                DB_TXN *tid) {
2872   const char *stored_confirmation;
2873   struct kvp *k;
2874   int e;
2875   const char *rights;
2876   
2877   if((e = trackdb_getdata(trackdb_usersdb, user, &k, tid)))
2878     return e;
2879   if(!(stored_confirmation = kvp_get(k, "confirmation"))) {
2880     error(0, "already confirmed user '%s'", user);
2881     /* DB claims -30,800 to -30,999 so -1 should be a safe bet */
2882     return -1;
2883   }
2884   if(!(rights = kvp_get(k, "rights"))) {
2885     error(0, "no rights for unconfirmed user '%s'", user);
2886     return -1;
2887   }
2888   if(parse_rights(rights, rightsp, 1))
2889     return -1;
2890   if(strcmp(confirmation, stored_confirmation)) {
2891     error(0, "wrong confirmation string for user '%s'", user);
2892     return -1;
2893   }
2894   /* 'sall good */
2895   kvp_set(&k, "confirmation", 0);
2896   return trackdb_putdata(trackdb_usersdb, user, k, tid, 0);
2897 }
2898
2899 /** @brief Confirm a user registration
2900  * @param user Username
2901  * @param confirmation Confirmation string
2902  * @param rightsp Where to put user rights
2903  * @return 0 on success, non-0 on error
2904  */
2905 int trackdb_confirm(const char *user, const char *confirmation,
2906                     rights_type *rightsp) {
2907   int e;
2908
2909   WITH_TRANSACTION(trackdb_confirm_tid(user, confirmation, rightsp, tid));
2910   switch(e) {
2911   case 0:
2912     info("registration confirmed for user '%s'", user);
2913     eventlog("user_confirm", user, (char *)0);
2914     return 0;
2915   case DB_NOTFOUND:
2916     error(0, "confirmation for nonexistent user '%s'", user);
2917     return -1;
2918   default:                              /* already reported */
2919     return -1;
2920   }
2921 }
2922
2923 /*
2924 Local Variables:
2925 c-basic-offset:2
2926 comment-column:40
2927 fill-column:79
2928 indent-tabs-mode:nil
2929 End:
2930 */