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