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