#include "unicode.h"
#include "unidata.h"
#include "base64.h"
+#include "sendmail.h"
#define RESCAN "disorder-rescan"
#define DEADLOCK "disorder-deadlock"
*/
DB *trackdb_usersdb;
+/** @brief The playlists database
+ * - Keys are playlist names
+ * - Values are encoded key-value pairs
+ * - Data is user data and cannot be reconstructed
+ */
+DB *trackdb_playlistsdb;
+
static pid_t db_deadlock_pid = -1; /* deadlock manager PID */
static pid_t rescan_pid = -1; /* rescanner PID */
static int initialized, opened; /* state */
trackdb_noticeddb = open_db("noticed.db",
DB_DUPSORT, DB_BTREE, dbflags, 0666);
trackdb_scheduledb = open_db("schedule.db", 0, DB_HASH, dbflags, 0666);
+ trackdb_playlistsdb = open_db("playlists.db", 0, DB_HASH, dbflags, 0666);
if(!trackdb_existing_database) {
/* Stash the database version */
char buf[32];
fatal(0, "error closing schedule.db: %s", db_strerror(err));
if((err = trackdb_usersdb->close(trackdb_usersdb, 0)))
fatal(0, "error closing users.db: %s", db_strerror(err));
+ if((err = trackdb_playlistsdb->close(trackdb_playlistsdb, 0)))
+ fatal(0, "error closing playlists.db: %s", db_strerror(err));
trackdb_tracksdb = trackdb_searchdb = trackdb_prefsdb = 0;
trackdb_tagsdb = trackdb_globaldb = 0;
D(("closed databases"));
/* generic db routines *******************************************************/
-/* fetch and decode a database entry. Returns 0, DB_NOTFOUND or
- * DB_LOCK_DEADLOCK. */
+/** @brief Fetch and decode a database entry
+ * @param db Database
+ * @param track Track name
+ * @param kp Where to put decoded list (or NULL if you don't care)
+ * @param tid Owning transaction
+ * @return 0, @c DB_NOTFOUND or @c DB_LOCK_DEADLOCK
+ */
int trackdb_getdata(DB *db,
const char *track,
struct kvp **kp,
switch(err = db->get(db, tid, make_key(&key, track),
prepare_data(&data), 0)) {
case 0:
- *kp = kvp_urldecode(data.data, data.size);
+ if(kp)
+ *kp = kvp_urldecode(data.data, data.size);
return 0;
case DB_NOTFOUND:
- *kp = 0;
+ if(kp)
+ *kp = 0;
return err;
case DB_LOCK_DEADLOCK:
error(0, "error querying database: %s", db_strerror(err));
DBT k, d;
int err = 0;
struct vector tracks[1];
+ hash *h = hash_new(1);
vector_init(tracks);
c = trackdb_opencursor(trackdb_noticeddb, tid);
while((maxtracks <= 0 || tracks->nvec < maxtracks)
- && !(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_PREV)))
- vector_append(tracks, xstrndup(d.data, d.size));
+ && !(err = c->c_get(c, prepare_data(&k), prepare_data(&d), DB_PREV))) {
+ char *const track = xstrndup(d.data, d.size);
+ /* Don't add any track more than once */
+ if(hash_add(h, track, "", HASH_INSERT))
+ continue;
+ /* See if the track still exists */
+ err = trackdb_getdata(trackdb_tracksdb, track, NULL/*kp*/, tid);
+ if(err == DB_NOTFOUND)
+ continue; /* It doesn't, skip it */
+ if(err == DB_LOCK_DEADLOCK)
+ break; /* Doh */
+ vector_append(tracks, track);
+ }
switch(err) {
case 0: /* hit maxtracks */
case DB_NOTFOUND: /* ran out of tracks */
* Currently we only allow the letters and digits in ASCII. We could be more
* liberal than this but it is a nice simple test. It is critical that
* semicolons are never allowed.
+ *
+ * NB also used by playlist_parse_name() to validate playlist names!
*/
-static int valid_username(const char *user) {
+int valid_username(const char *user) {
if(!*user)
return 0;
while(*user) {
user, rights, email);
else
info("created user '%s' with rights '%s'", user, rights);
+ eventlog("user_add", user, (char *)0);
return 0;
}
}
return -1;
}
info("deleted user '%s'", user);
+ eventlog("user_delete", user, (char *)0);
return 0;
}
}
} else if(!strcmp(key, "email")) {
if(*value) {
- if(!strchr(value, '@')) {
- error(0, "invalid email address '%s' for user '%s'", user, value);
+ if(!email_valid(value)) {
+ error(0, "invalid email address '%s' for user '%s'", value, user);
return -1;
}
} else
if(e) {
error(0, "unknown user '%s'", user);
return -1;
- } else
+ } else {
+ eventlog("user_edit", user, key, (char *)0);
return 0;
+ }
}
/** @brief List all users
switch(e) {
case 0:
info("registration confirmed for user '%s'", user);
+ eventlog("user_confirm", user, (char *)0);
return 0;
case DB_NOTFOUND:
error(0, "confirmation for nonexistent user '%s'", user);