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