diff --git a/src/Makefile.am b/src/Makefile.am
index 8737505f..7cd940cb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -30,7 +30,7 @@ MUSEPACKSRC=scan-mpc.c
endif
if COND_SQLITE
-SQLITEDB=dbs-sqlite.c dbs-sqlite.h
+SQLITEDB=db-sql.c db-sql.h db-sql-sqlite2.c db-sql-sqlite2.h
endif
wavstreamer_SOURCES = wavstreamer.c
diff --git a/src/configfile.c b/src/configfile.c
index 2a57e8c2..080a5e83 100644
--- a/src/configfile.c
+++ b/src/configfile.c
@@ -22,7 +22,7 @@
/**
* \file configfile.c
- *
+ *
* Configfile and web interface handling.
*/
@@ -178,7 +178,7 @@ void config_content_type(WS_CONNINFO *pwsc, char *path) {
/**
* Try and create a directory, including parents.
- *
+ *
* \param path path to make
* \returns 0 on success, -1 otherwise, with errno set
*/
@@ -208,11 +208,11 @@ int config_makedir(char *path) {
err=errno;
free(pathdup);
errno=err;
- return -1;
+ return -1;
}
} else {
errno=ENAMETOOLONG;
- return -1;
+ return -1;
}
}
@@ -281,7 +281,7 @@ int config_read(char *file) {
return -1;
}
-#ifdef NSLU2
+#ifdef NSLU2
config.always_scan=0;
#else
config.always_scan=1;
@@ -334,7 +334,7 @@ int config_read(char *file) {
if((value) && (term) && (strlen(term))) {
while(strlen(value) && (strchr("\t ",*value)))
value++;
-
+
pce=config_elements;
handled=0;
while((!handled) && (pce->config_element != -1)) {
@@ -342,9 +342,9 @@ int config_read(char *file) {
/* valid config directive */
handled=1;
pce->changed=1;
-
+
DPRINTF(E_DBG,L_CONF,"Read %s: %s\n",pce->name,value);
-
+
switch(pce->type) {
case CONFIG_TYPE_STRING:
/* DWB: free space to prevent small leak */
@@ -359,7 +359,7 @@ int config_read(char *file) {
}
pce++;
}
-
+
if(!handled) {
fprintf(stderr,"Invalid config directive: %s\n",buffer);
fclose(fin);
@@ -396,7 +396,7 @@ int config_read(char *file) {
pce->changed=0;
pce++;
}
-
+
/* Set the directory components to realpaths */
realpath(config.web_root,path_buffer);
free(config.web_root);
@@ -471,7 +471,7 @@ int config_read(char *file) {
DPRINTF(E_FATAL,L_MISC,"Alloc error.\n");
currentterm=0;
-
+
term_begin=config.compdirs;
while(*term_begin && *term_begin ==' ')
term_begin++;
@@ -521,8 +521,8 @@ void config_close(void) {
pce=config_elements;
err=0;
while((pce->config_element != -1)) {
- if((pce->config_element) &&
- (pce->type == CONFIG_TYPE_STRING) &&
+ if((pce->config_element) &&
+ (pce->type == CONFIG_TYPE_STRING) &&
(*((char**)pce->var))) {
DPRINTF(E_DBG,L_CONF,"Freeing %s\n",pce->name);
free(*((char**)pce->var));
@@ -575,9 +575,9 @@ int config_write(WS_CONNINFO *pwsc) {
fprintf(configfile,"art_filename\t%s\n",ws_getvar(pwsc,"art_filename"));
if(ws_getvar(pwsc,"logfile") && strlen(ws_getvar(pwsc,"logfile")))
fprintf(configfile,"logfile\t\t%s\n",ws_getvar(pwsc,"logfile"));
- fprintf(configfile,"process_m3u\t%s\n",ws_getvar(pwsc,"process_m3u"));
+ fprintf(configfile,"process_m3u\t%s\n",ws_getvar(pwsc,"process_m3u"));
fprintf(configfile,"compress\t%s\n",ws_getvar(pwsc,"compress"));
-
+
fprintf(configfile,"debuglevel\t%s\n",ws_getvar(pwsc,"debuglevel"));
fprintf(configfile,"compdirs\t%s\n",ws_getvar(pwsc,"compdirs"));
@@ -668,12 +668,12 @@ void config_handler(WS_CONNINFO *pwsc) {
DPRINTF(E_DBG,L_CONF|L_WS,"Entering config_handler\n");
config_set_status(pwsc,0,"Serving admin pages");
-
+
pwsc->close=1;
ws_addresponseheader(pwsc,"Connection","close");
if(strcasecmp(pwsc->uri,"/xml-rpc")==0) {
- // perhaps this should get a separate handler
+ // perhaps this should get a separate handler
config_set_status(pwsc,0,"Serving xml-rpc method");
xml_handle(pwsc);
DPRINTF(E_DBG,L_CONF|L_XML,"Thread %d: xml-rpc served\n",pwsc->threadno);
@@ -717,7 +717,7 @@ void config_handler(WS_CONNINFO *pwsc) {
config_set_status(pwsc,0,NULL);
return;
}
-
+
if(strcasecmp(pwsc->uri,"/config-update.html")==0) {
/* don't update (and turn everything to (null)) the
configuration file if what the user's really trying to do is
@@ -763,10 +763,10 @@ void config_handler(WS_CONNINFO *pwsc) {
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
ws_emitheaders(pwsc);
-
+
if(strcasecmp(&resolved_path[strlen(resolved_path) - 5],".html") == 0) {
config_subst_stream(pwsc, file_fd);
- } else {
+ } else {
copyfile(file_fd,pwsc->fd);
}
@@ -800,7 +800,7 @@ int config_auth(char *user, char *password) {
void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg) {
char *host;
char *port;
-
+
if(ws_getrequestheader(pwsc,"host")) {
host = strdup(ws_getrequestheader(pwsc,"host"));
if((port = strrchr(host,':'))) {
@@ -868,6 +868,7 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
char buf[256];
int r_days, r_hours, r_mins, r_secs;
int scanning;
+ int song_count;
ws_writefd(pwsc,"
Service | ");
ws_writefd(pwsc,"Status | Control |
\n");
@@ -925,11 +926,11 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
r_secs -= 60 * r_mins;
memset(buf,0x0,sizeof(buf));
- if(r_days)
+ if(r_days)
sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
r_days == 1 ? "" : "s");
- if(r_days || r_hours)
+ if(r_days || r_hours)
sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours,
r_hours == 1 ? "" : "s");
@@ -939,13 +940,14 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs,
r_secs == 1 ? "" : "s");
-
+
ws_writefd(pwsc," %s | \n",buf);
ws_writefd(pwsc,"\n");
-
+
ws_writefd(pwsc,"\n");
ws_writefd(pwsc," Songs | \n");
- ws_writefd(pwsc," %d | \n",db_get_song_count());
+ db_get_song_count(NULL,&song_count);
+ ws_writefd(pwsc," %d | \n",song_count);
ws_writefd(pwsc,"
\n");
ws_writefd(pwsc,"\n");
@@ -975,7 +977,7 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
* Get the count of connected users. This is actually not totally accurate,
* as there may be a "connected" host that is in between requesting songs.
* It's marginally close though. It is really the number of connections
- * with non-zero session numbers.
+ * with non-zero session numbers.
*
* \returns connected user count
*/
@@ -1018,7 +1020,7 @@ void config_emit_threadstatus(WS_CONNINFO *pwsc, void *value, char *arg) {
WS_CONNINFO *pci;
SCAN_STATUS *pss;
WSTHREADENUM wste;
-
+
ws_writefd(pwsc,"Thread | ");
ws_writefd(pwsc,"Session | Host | ");
ws_writefd(pwsc,"Action |
\n");
@@ -1058,7 +1060,7 @@ void config_emit_ispage(WS_CONNINFO *pwsc, void *value, char *arg) {
first=last=arg;
strsep(&last,":");
-
+
if(last) {
page=strdup(first);
if(!page)
@@ -1152,7 +1154,7 @@ void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg) {
}
/**
- * implement the INCLUDE command. This is essentially a server
+ * implement the INCLUDE command. This is essentially a server
* side include.
*
* \param pwsc web connection
@@ -1166,7 +1168,7 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
struct stat sb;
DPRINTF(E_DBG,L_CONF|L_WS,"Preparing to include %s\n",arg);
-
+
snprintf(path,PATH_MAX,"%s/%s",config.web_root,arg);
if(!realpath(path,resolved_path)) {
pwsc->error=errno;
@@ -1203,7 +1205,7 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
ws_writefd(pwsc,"
error: cannot open %s: %s
",arg,strerror(errno));
return;
}
-
+
config_subst_stream(pwsc, file_fd);
r_close(file_fd);
@@ -1212,13 +1214,13 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
}
/**
- * free a SCAN_STATUS block
+ * free a SCAN_STATUS block
*
* @param vp pointer to SCAN_STATUS block
*/
void config_freescan(void *vp) {
SCAN_STATUS *pss = (SCAN_STATUS*)vp;
-
+
if(pss) {
if(pss->what)
free(pss->what);
@@ -1243,7 +1245,7 @@ void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
va_list ap;
SCAN_STATUS *pfirst;
char *newmsg = NULL;
-
+
DPRINTF(E_DBG,L_CONF,"Entering config_set_status\n");
if(fmt) {
@@ -1262,24 +1264,24 @@ void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
pfirst->what = NULL;
pfirst->thread = pwsc->threadno;
pfirst->host = strdup(pwsc->hostname);
- ws_set_local_storage(pwsc,pfirst,config_freescan);
+ ws_set_local_storage(pwsc,pfirst,config_freescan);
} else {
DPRINTF(E_FATAL,L_CONF,"Malloc Error\n");
}
}
-
+
if(pfirst) {
- /* just update */
- if(pfirst->what) {
- free(pfirst->what);
- }
- pfirst->what=newmsg;
- pfirst->session=session;
+ /* just update */
+ if(pfirst->what) {
+ free(pfirst->what);
+ }
+ pfirst->what=newmsg;
+ pfirst->session=session;
} else {
- if(newmsg)
- free(newmsg);
+ if(newmsg)
+ free(newmsg);
}
-
+
ws_unlock_local_storage(pwsc);
DPRINTF(E_DBG,L_CONF,"Exiting config_set_status\n");
}
@@ -1288,7 +1290,7 @@ void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
* Get the next available session id.
* This is vulnerable to races, but we don't track sessions,
* so there really isn't a point anyway.
- *
+ *
* @returns duh... the next available session id
*/
int config_get_next_session(void) {
diff --git a/src/db-generic.c b/src/db-generic.c
index 767e9379..f2438705 100644
--- a/src/db-generic.c
+++ b/src/db-generic.c
@@ -27,6 +27,7 @@
#include
#include
+#include
#include
#include
@@ -34,7 +35,7 @@
#include "err.h"
#include "mp3-scanner.h"
-#include "dbs-sqlite.h"
+#include "db-sql.h"
#define DB_VERSION 1
#define MAYBEFREE(a) { if((a)) free((a)); };
@@ -42,61 +43,59 @@
/** pointers to database-specific functions */
typedef struct tag_db_functions {
char *name;
- int(*dbs_open)(char *);
+ int(*dbs_open)(char **, char *);
int(*dbs_init)(int);
int(*dbs_deinit)(void);
- int(*dbs_add)(MP3FILE*);
- int(*dbs_add_playlist)(char *, int, char *,char *, int, int *);
- int(*dbs_add_playlist_item)(int, int);
- int(*dbs_delete_playlist)(int);
- int(*dbs_delete_playlist_item)(int, int);
- int(*dbs_edit_playlist)(int, char*, char*);
- int(*dbs_enum_start)(DBQUERYINFO *);
- int(*dbs_enum_size)(DBQUERYINFO *, int *);
- int(*dbs_enum_fetch)(DBQUERYINFO *, unsigned char **);
- int(*dbs_enum_reset)(DBQUERYINFO *);
- int(*dbs_enum_end)(void);
+ int(*dbs_add)(char **, MP3FILE*);
+ int(*dbs_add_playlist)(char **, char *, int, char *,char *, int, int *);
+ int(*dbs_add_playlist_item)(char **, int, int);
+ int(*dbs_delete_playlist)(char **, int);
+ int(*dbs_delete_playlist_item)(char **, int, int);
+ int(*dbs_edit_playlist)(char **, int, char*, char*);
+ int(*dbs_enum_start)(char **, DBQUERYINFO *);
+ int(*dbs_enum_size)(char **, DBQUERYINFO *, int *, int *);
+ int(*dbs_enum_fetch)(char **, DBQUERYINFO *, int *, unsigned char **);
+ int(*dbs_enum_reset)(char **, DBQUERYINFO *);
+ int(*dbs_enum_end)(char **);
int(*dbs_start_scan)(void);
int(*dbs_end_song_scan)(void);
int(*dbs_end_scan)(void);
- int(*dbs_get_count)(CountType_t);
- MP3FILE*(*dbs_fetch_item)(int);
- MP3FILE*(*dbs_fetch_path)(char *,int);
- M3UFILE*(*dbs_fetch_playlist)(char *, int);
+ int(*dbs_get_count)(char **, int *, CountType_t);
+ MP3FILE*(*dbs_fetch_item)(char **, int);
+ MP3FILE*(*dbs_fetch_path)(char **, char *,int);
+ M3UFILE*(*dbs_fetch_playlist)(char **, char *, int);
void(*dbs_dispose_item)(MP3FILE*);
void(*dbs_dispose_playlist)(M3UFILE*);
}DB_FUNCTIONS;
/** All supported backend databases, and pointers to the db specific implementations */
DB_FUNCTIONS db_functions[] = {
-#ifdef HAVE_LIBSQLITE
{
- "sqlite",
- db_sqlite_open,
- db_sqlite_init,
- db_sqlite_deinit,
- db_sqlite_add,
- db_sqlite_add_playlist,
- db_sqlite_add_playlist_item,
- db_sqlite_delete_playlist,
- db_sqlite_delete_playlist_item,
- db_sqlite_edit_playlist,
- db_sqlite_enum_start,
- db_sqlite_enum_size,
- db_sqlite_enum_fetch,
- db_sqlite_enum_reset,
- db_sqlite_enum_end,
- db_sqlite_start_scan,
- db_sqlite_end_song_scan,
- db_sqlite_end_scan,
- db_sqlite_get_count,
- db_sqlite_fetch_item,
- db_sqlite_fetch_path,
- db_sqlite_fetch_playlist,
- db_sqlite_dispose_item,
- db_sqlite_dispose_playlist
+ "sql",
+ db_sql_open,
+ db_sql_init,
+ db_sql_deinit,
+ db_sql_add,
+ db_sql_add_playlist,
+ db_sql_add_playlist_item,
+ db_sql_delete_playlist,
+ db_sql_delete_playlist_item,
+ db_sql_edit_playlist,
+ db_sql_enum_start,
+ db_sql_enum_size,
+ db_sql_enum_fetch,
+ db_sql_enum_reset,
+ db_sql_enum_end,
+ db_sql_start_scan,
+ db_sql_end_song_scan,
+ db_sql_end_scan,
+ db_sql_get_count,
+ db_sql_fetch_item,
+ db_sql_fetch_path,
+ db_sql_fetch_playlist,
+ db_sql_dispose_item,
+ db_sql_dispose_playlist
},
-#endif
{ NULL,NULL }
};
@@ -152,11 +151,11 @@ DAAP_ITEMS taglist[] = {
{ 0x0C, "adbs", "daap.databasesongs" },
{ 0x09, "asal", "daap.songalbum" },
{ 0x09, "asar", "daap.songartist" },
- { 0x03, "asbt", "daap.songbeatsperminute" },
+ { 0x03, "asbt", "daap.songbeatsperminute" },
{ 0x03, "asbr", "daap.songbitrate" },
- { 0x09, "ascm", "daap.songcomment" },
- { 0x01, "asco", "daap.songcompilation" },
- { 0x09, "ascp", "daap.songcomposer" },
+ { 0x09, "ascm", "daap.songcomment" },
+ { 0x01, "asco", "daap.songcompilation" },
+ { 0x09, "ascp", "daap.songcomposer" },
{ 0x0A, "asda", "daap.songdateadded" },
{ 0x0A, "asdm", "daap.songdatemodified" },
{ 0x03, "asdc", "daap.songdisccount" },
@@ -210,43 +209,43 @@ DAAP_ITEMS taglist[] = {
};
/** map the string names specified in the meta= tag to bit numbers */
-static METAMAP db_metamap[] = {
- { "dmap.itemid", metaItemId },
- { "dmap.itemname", metaItemName },
- { "dmap.itemkind", metaItemKind },
- { "dmap.persistentid", metaPersistentId },
+static METAMAP db_metamap[] = {
+ { "dmap.itemid", metaItemId },
+ { "dmap.itemname", metaItemName },
+ { "dmap.itemkind", metaItemKind },
+ { "dmap.persistentid", metaPersistentId },
{ "dmap.containeritemid", metaContainerItemId },
- { "dmap.parentcontainerid", metaParentContainerId },
+ { "dmap.parentcontainerid", metaParentContainerId },
/* end generics */
- { "daap.songalbum", metaSongAlbum },
- { "daap.songartist", metaSongArtist },
- { "daap.songbitrate", metaSongBitRate },
+ { "daap.songalbum", metaSongAlbum },
+ { "daap.songartist", metaSongArtist },
+ { "daap.songbitrate", metaSongBitRate },
{ "daap.songbeatsperminute", metaSongBPM },
- { "daap.songcomment", metaSongComment },
- { "daap.songcompilation", metaSongCompilation },
- { "daap.songcomposer", metaSongComposer },
- { "daap.songdatakind", metaSongDataKind },
+ { "daap.songcomment", metaSongComment },
+ { "daap.songcompilation", metaSongCompilation },
+ { "daap.songcomposer", metaSongComposer },
+ { "daap.songdatakind", metaSongDataKind },
{ "daap.songdataurl", metaSongDataURL },
- { "daap.songdateadded", metaSongDateAdded },
- { "daap.songdatemodified", metaSongDateModified },
- { "daap.songdescription", metaSongDescription },
- { "daap.songdisabled", metaSongDisabled },
- { "daap.songdisccount", metaSongDiscCount },
- { "daap.songdiscnumber", metaSongDiscNumber },
- { "daap.songeqpreset", metaSongEqPreset },
- { "daap.songformat", metaSongFormat },
- { "daap.songgenre", metaSongGenre },
- { "daap.songgrouping", metaSongGrouping },
+ { "daap.songdateadded", metaSongDateAdded },
+ { "daap.songdatemodified", metaSongDateModified },
+ { "daap.songdescription", metaSongDescription },
+ { "daap.songdisabled", metaSongDisabled },
+ { "daap.songdisccount", metaSongDiscCount },
+ { "daap.songdiscnumber", metaSongDiscNumber },
+ { "daap.songeqpreset", metaSongEqPreset },
+ { "daap.songformat", metaSongFormat },
+ { "daap.songgenre", metaSongGenre },
+ { "daap.songgrouping", metaSongGrouping },
{ "daap.songrelativevolume", metaSongRelativeVolume },
- { "daap.songsamplerate", metaSongSampleRate },
- { "daap.songsize", metaSongSize },
- { "daap.songstarttime", metaSongStartTime },
- { "daap.songstoptime", metaSongStopTime },
- { "daap.songtime", metaSongTime },
- { "daap.songtrackcount", metaSongTrackCount },
- { "daap.songtracknumber", metaSongTrackNumber },
- { "daap.songuserrating", metaSongUserRating },
- { "daap.songyear", metaSongYear },
+ { "daap.songsamplerate", metaSongSampleRate },
+ { "daap.songsize", metaSongSize },
+ { "daap.songstarttime", metaSongStartTime },
+ { "daap.songstoptime", metaSongStopTime },
+ { "daap.songtime", metaSongTime },
+ { "daap.songtrackcount", metaSongTrackCount },
+ { "daap.songtracknumber", metaSongTrackNumber },
+ { "daap.songuserrating", metaSongUserRating },
+ { "daap.songyear", metaSongYear },
/* iTunes 4.5+ (forgot exactly when) */
{ "daap.songcodectype", metaSongCodecType },
{ "daap.songcodecsubtype", metaSongCodecSubType },
@@ -261,9 +260,20 @@ static METAMAP db_metamap[] = {
/* mt-daapd specific */
{ "org.mt-daapd.smart-playlist-spec", metaMPlaylistSpec },
{ "org.mt-daapd.playlist-type", metaMPlaylistType },
- { 0, 0 }
+ { 0, 0 }
};
+char *db_error_list[] = {
+ "Success",
+ "Misc SQL Error: %s",
+ "Duplicate Playlist: %s",
+ "Missing playlist spec",
+ "Cannot add playlist items to a playlist of that type",
+ "No rows returned",
+ "Invalid playlist id: %d",
+ "Invalid song id: %d",
+ "Parse error"
+};
/* Globals */
static DB_FUNCTIONS *db_current=&db_functions[0]; /**< current database backend */
@@ -287,30 +297,30 @@ static void db_trim_string(char *string);
* \param meta meta string variable from GET request
*/
MetaField_t db_encode_meta(char *meta) {
- MetaField_t bits = 0;
+ MetaField_t bits = 0;
char *start;
char *end;
METAMAP *m;
for(start = meta ; *start ; start = end) {
- int len;
+ int len;
- if(0 == (end = strchr(start, ',')))
- end = start + strlen(start);
+ if(0 == (end = strchr(start, ',')))
+ end = start + strlen(start);
- len = end - start;
+ len = end - start;
- if(*end != 0)
- end++;
+ if(*end != 0)
+ end++;
- for(m = db_metamap ; m->tag ; ++m)
- if(!strncmp(m->tag, start, len))
- break;
+ for(m = db_metamap ; m->tag ; ++m)
+ if(!strncmp(m->tag, start, len))
+ break;
- if(m->tag)
- bits |= (((MetaField_t) 1) << m->bit);
- else
- DPRINTF(E_WARN,L_DAAP,"Unknown meta code: %.*s\n", len, start);
+ if(m->tag)
+ bits |= (((MetaField_t) 1) << m->bit);
+ else
+ DPRINTF(E_WARN,L_DAAP,"Unknown meta code: %.*s\n", len, start);
}
DPRINTF(E_DBG, L_DAAP, "meta codes: %llu\n", bits);
@@ -329,42 +339,58 @@ int db_wantsmeta(MetaField_t meta, MetaFieldName_t fieldNo) {
}
-/*
+/*
* db_readlock
*
- * If this fails, something is so amazingly hosed, we might just as well
- * terminate.
+ * If this fails, something is so amazingly hosed, we might just as well
+ * terminate.
*/
void db_readlock(void) {
int err;
if((err=pthread_rwlock_rdlock(&db_rwlock))) {
- DPRINTF(E_FATAL,L_DB,"cannot lock rdlock: %s\n",strerror(err));
+ DPRINTF(E_FATAL,L_DB,"cannot lock rdlock: %s\n",strerror(err));
}
}
-/*
+/*
* db_writelock
- *
+ *
* same as above
*/
void db_writelock(void) {
int err;
if((err=pthread_rwlock_wrlock(&db_rwlock))) {
- DPRINTF(E_FATAL,L_DB,"cannot lock rwlock: %s\n",strerror(err));
+ DPRINTF(E_FATAL,L_DB,"cannot lock rwlock: %s\n",strerror(err));
}
}
/*
* db_unlock
- *
- * useless, but symmetrical
+ *
+ * useless, but symmetrical
*/
int db_unlock(void) {
return pthread_rwlock_unlock(&db_rwlock);
}
+/**
+ * Build an error string
+ */
+void db_get_error(char **pe, int error, ...) {
+ va_list ap;
+ char errbuf[1024];
+
+ if(!pe)
+ return;
+
+ va_start(ap, error);
+ vsnprintf(errbuf, sizeof(errbuf), db_error_list[error], ap);
+ va_end(ap);
+
+ *pe = strdup(errbuf);
+}
/**
* Must dynamically initialize the rwlock, as Mac OSX 10.3 (at least)
@@ -385,17 +411,17 @@ extern int db_set_backend(char *type) {
DPRINTF(E_DBG,L_DB,"Setting backend database to %s\n",type);
if(!db_functions[0].name) {
- DPRINTF(E_FATAL,L_DB,"No database backends are available. Install sqlite!\n");
+ DPRINTF(E_FATAL,L_DB,"No database backends are available. Install sqlite!\n");
}
-
+
db_current=&db_functions[0];
while((db_current->name) && (strcasecmp(db_current->name,type))) {
- db_current++;
+ db_current++;
}
if(!db_current->name) {
- DPRINTF(E_WARN,L_DB,"Could not find db backend %s. Aborting.\n",type);
- return -1;
+ DPRINTF(E_WARN,L_DB,"Could not find db backend %s. Aborting.\n",type);
+ return -1;
}
DPRINTF(E_DBG,L_DB,"Backend database set\n");
@@ -409,15 +435,15 @@ extern int db_set_backend(char *type) {
*
* \param parameters This is backend-specific (mysql, sqlite, etc)
*/
-int db_open(char *parameters) {
+int db_open(char **pe, char *parameters) {
int result;
DPRINTF(E_DBG,L_DB,"Opening database\n");
if(pthread_once(&db_initlock,db_init_once))
- return -1;
+ return -1;
- result=db_current->dbs_open(parameters);
+ result=db_current->dbs_open(pe, parameters);
DPRINTF(E_DBG,L_DB,"Results: %d\n",result);
return result;
@@ -463,13 +489,13 @@ int db_scanning(void) {
/**
* add (or update) a file
*/
-int db_add(MP3FILE *pmp3) {
+int db_add(char **pe, MP3FILE *pmp3) {
int retval;
db_writelock();
db_utf8_validate(pmp3);
db_trim_strings(pmp3);
- retval=db_current->dbs_add(pmp3);
+ retval=db_current->dbs_add(pe,pmp3);
db_revision_no++;
db_unlock();
@@ -483,15 +509,15 @@ int db_add(MP3FILE *pmp3) {
* \param type type of playlist to add: 0 - static, 1 - smart, 2 - m3u
* \param clause where clause (if type 1)
* \param playlistid returns the id of the playlist created
- * \returns 0 on success, error code otherwise
+ * \returns 0 on success, error code otherwise
*/
-int db_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid) {
+int db_add_playlist(char **pe, char *name, int type, char *clause, char *path, int index, int *playlistid) {
int retval;
db_writelock();
- retval=db_current->dbs_add_playlist(name,type,clause,path,index,playlistid);
+ retval=db_current->dbs_add_playlist(pe,name,type,clause,path,index,playlistid);
if(retval == DB_E_SUCCESS)
- db_revision_no++;
+ db_revision_no++;
db_unlock();
return retval;
@@ -504,13 +530,13 @@ int db_add_playlist(char *name, int type, char *clause, char *path, int index, i
* \param songid song to add to playlist
* \returns 0 on success, DB_E_ code otherwise
*/
-int db_add_playlist_item(int playlistid, int songid) {
+int db_add_playlist_item(char **pe, int playlistid, int songid) {
int retval;
db_writelock();
- retval=db_current->dbs_add_playlist_item(playlistid,songid);
+ retval=db_current->dbs_add_playlist_item(pe,playlistid,songid);
if(retval == DB_E_SUCCESS)
- db_revision_no++;
+ db_revision_no++;
db_unlock();
return retval;
@@ -520,15 +546,15 @@ int db_add_playlist_item(int playlistid, int songid) {
* delete a playlist
*
* \param playlistid id of the playlist to delete
- * \returns 0 on success, error code otherwise
+ * \returns 0 on success, error code otherwise
*/
-int db_delete_playlist(int playlistid) {
+int db_delete_playlist(char **pe, int playlistid) {
int retval;
db_writelock();
- retval=db_current->dbs_delete_playlist(playlistid);
+ retval=db_current->dbs_delete_playlist(pe,playlistid);
if(retval == DB_E_SUCCESS)
- db_revision_no++;
+ db_revision_no++;
db_unlock();
return retval;
@@ -539,15 +565,15 @@ int db_delete_playlist(int playlistid) {
*
* \param playlistid id of the playlist to delete
* \param songid id of the song to delete
- * \returns 0 on success, error code otherwise
+ * \returns 0 on success, error code otherwise
*/
-int db_delete_playlist_item(int playlistid, int songid) {
+int db_delete_playlist_item(char **pe, int playlistid, int songid) {
int retval;
db_writelock();
- retval=db_current->dbs_delete_playlist_item(playlistid,songid);
+ retval=db_current->dbs_delete_playlist_item(pe,playlistid,songid);
if(retval == DB_E_SUCCESS)
- db_revision_no++;
+ db_revision_no++;
db_unlock();
return retval;
@@ -560,12 +586,12 @@ int db_delete_playlist_item(int playlistid, int songid) {
* @param name new name of playlist
* @param clause new where clause
*/
-int db_edit_playlist(int id, char *name, char *clause) {
+int db_edit_playlist(char **pe, int id, char *name, char *clause) {
int retval;
-
+
db_writelock();
-
- retval = db_current->dbs_edit_playlist(id, name, clause);
+
+ retval = db_current->dbs_edit_playlist(pe, id, name, clause);
db_unlock();
return retval;
}
@@ -577,15 +603,15 @@ int db_edit_playlist(int id, char *name, char *clause) {
* \param pinfo pointer to DBQUERYINFO struction
* \returns 0 on success, -1 on failure
*/
-int db_enum_start(DBQUERYINFO *pinfo) {
+int db_enum_start(char **pe, DBQUERYINFO *pinfo) {
int retval;
db_writelock();
- retval=db_current->dbs_enum_start(pinfo);
+ retval=db_current->dbs_enum_start(pe, pinfo);
if(retval) {
- db_unlock();
- return retval;
+ db_unlock();
+ return retval;
}
return 0;
@@ -596,8 +622,8 @@ int db_enum_start(DBQUERYINFO *pinfo) {
* db__enum_reset, so it should be positioned at the head
* of the list of returned items.
*/
-int db_enum_size(DBQUERYINFO *pinfo, int *count) {
- return db_current->dbs_enum_size(pinfo,count);
+int db_enum_size(char **pe, DBQUERYINFO *pinfo, int *size, int *count) {
+ return db_current->dbs_enum_size(pe,pinfo,size,count);
}
@@ -607,27 +633,29 @@ int db_enum_size(DBQUERYINFO *pinfo, int *count) {
* the dmap item.
*
* \param plen length of the dmap item returned
- * \returns dmap item
+ * \returns dmap item
*/
-int db_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap) {
- return db_current->dbs_enum_fetch(pinfo,pdmap);
+int db_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size,
+ unsigned char **pdmap)
+{
+ return db_current->dbs_enum_fetch(pe,pinfo,size,pdmap);
}
/**
* reset the enum, without coming out the the db_writelock
*/
-int db_enum_reset(DBQUERYINFO *pinfo) {
- return db_current->dbs_enum_reset(pinfo);
+int db_enum_reset(char **pe, DBQUERYINFO *pinfo) {
+ return db_current->dbs_enum_reset(pe,pinfo);
}
/**
* finish the enumeration
*/
-int db_enum_end(void) {
+int db_enum_end(char **pe) {
int retval;
- retval=db_current->dbs_enum_end();
+ retval=db_current->dbs_enum_end(pe);
db_unlock();
return retval;
}
@@ -638,32 +666,32 @@ int db_enum_end(void) {
* mostly only by the web interface, and when streaming a song
*
* \param id id of the item to get details for
- */
-MP3FILE *db_fetch_item(int id) {
+ */
+MP3FILE *db_fetch_item(char **pe, int id) {
MP3FILE *retval;
-
+
db_readlock();
- retval=db_current->dbs_fetch_item(id);
+ retval=db_current->dbs_fetch_item(pe, id);
db_unlock();
return retval;
}
-MP3FILE *db_fetch_path(char *path,int index) {
+MP3FILE *db_fetch_path(char **pe, char *path,int index) {
MP3FILE *retval;
-
+
db_readlock();
- retval=db_current->dbs_fetch_path(path, index);
+ retval=db_current->dbs_fetch_path(pe,path, index);
db_unlock();
return retval;
}
-M3UFILE *db_fetch_playlist(char *path, int index) {
+M3UFILE *db_fetch_playlist(char **pe, char *path, int index) {
M3UFILE *retval;
db_readlock();
- retval=db_current->dbs_fetch_playlist(path,index);
+ retval=db_current->dbs_fetch_playlist(pe,path,index);
db_unlock();
return retval;
@@ -676,7 +704,7 @@ int db_start_scan(void) {
retval=db_current->dbs_start_scan();
db_is_scanning=1;
db_unlock();
-
+
return retval;
}
@@ -686,7 +714,7 @@ int db_end_song_scan(void) {
db_writelock();
retval=db_current->dbs_end_song_scan();
db_unlock();
-
+
return retval;
}
@@ -697,10 +725,10 @@ int db_end_scan(void) {
retval=db_current->dbs_end_scan();
db_is_scanning=0;
db_unlock();
-
+
return retval;
}
-
+
void db_dispose_item(MP3FILE *pmp3) {
return db_current->dbs_dispose_item(pmp3);
}
@@ -709,26 +737,26 @@ void db_dispose_playlist(M3UFILE *pm3u) {
return db_current->dbs_dispose_playlist(pm3u);
}
-int db_get_count(CountType_t type) {
- int retval;
+int db_get_count(char **pe, int *count, CountType_t type) {
+ int retval;
db_readlock();
- retval=db_current->dbs_get_count(type);
+ retval=db_current->dbs_get_count(pe,count,type);
db_unlock();
return retval;
}
-
-/*
+
+/*
* FIXME: clearly a stub
*/
-int db_get_song_count() {
- return db_get_count(countSongs);
+int db_get_song_count(char **pe, int *count) {
+ return db_get_count(pe, count, countSongs);
}
-int db_get_playlist_count() {
- return db_get_count(countPlaylists);
+int db_get_playlist_count(char **pe, int *count) {
+ return db_get_count(pe, count, countPlaylists);
}
@@ -799,7 +827,7 @@ int db_dmap_add_int(unsigned char *where, char *tag, int value) {
where[9] = (value >> 16) & 0xFF;
where[10] = (value >> 8) & 0xFF;
where[11] = value & 0xFF;
-
+
return 12;
}
@@ -835,8 +863,8 @@ int db_dmap_add_string(unsigned char *where, char *tag, char *value) {
* \param value what to put there
* \param size how much data to cram in there
*/
-int db_dmap_add_literal(unsigned char *where, char *tag,
- char *value, int size) {
+int db_dmap_add_literal(unsigned char *where, char *tag,
+ char *value, int size) {
/* tag */
memcpy(where,tag,4);
@@ -885,7 +913,7 @@ void db_utf8_validate(MP3FILE *pmp3) {
int is_invalid=0;
/* we won't bother with path and fname... those were culled with the
- * scan. Even if they are invalid (_could_ they be?), then we
+ * scan. Even if they are invalid (_could_ they be?), then we
* won't be able to open the file if we change them. Likewise,
* we won't do type or description, as these can't be bad, or they
* wouldn't have been scanned */
@@ -902,7 +930,7 @@ void db_utf8_validate(MP3FILE *pmp3) {
is_invalid |= db_utf8_validate_string(pmp3->url);
if(is_invalid) {
- DPRINTF(E_LOG,L_SCAN,"Invalid UTF-8 in %s\n",pmp3->path);
+ DPRINTF(E_LOG,L_SCAN,"Invalid UTF-8 in %s\n",pmp3->path);
}
}
@@ -919,38 +947,38 @@ int db_utf8_validate_string(char *string) {
int retval=0;
if(!string)
- return 0;
+ return 0;
while(*current) {
- if(!((*current) & 0x80)) {
- current++;
- } else {
- run=0;
+ if(!((*current) & 0x80)) {
+ current++;
+ } else {
+ run=0;
- /* it's a lead utf-8 character */
- if((*current & 0xE0) == 0xC0) run=1;
- if((*current & 0xF0) == 0xE0) run=2;
- if((*current & 0xF8) == 0xF0) run=3;
+ /* it's a lead utf-8 character */
+ if((*current & 0xE0) == 0xC0) run=1;
+ if((*current & 0xF0) == 0xE0) run=2;
+ if((*current & 0xF8) == 0xF0) run=3;
- if(!run) {
- /* high bit set, but invalid */
- *current++='?';
- retval=1;
- } else {
- r_current=0;
- while((r_current != run) && (*(current + r_current + 1)) &&
- ((*(current + r_current + 1) & 0xC0) == 0x80)) {
- r_current++;
- }
+ if(!run) {
+ /* high bit set, but invalid */
+ *current++='?';
+ retval=1;
+ } else {
+ r_current=0;
+ while((r_current != run) && (*(current + r_current + 1)) &&
+ ((*(current + r_current + 1) & 0xC0) == 0x80)) {
+ r_current++;
+ }
- if(r_current != run) {
- *current++ = '?';
- retval=1;
- } else {
- current += (1 + run);
- }
- }
- }
+ if(r_current != run) {
+ *current++ = '?';
+ retval=1;
+ } else {
+ current += (1 + run);
+ }
+ }
+ }
}
return retval;
@@ -986,9 +1014,9 @@ void db_trim_strings(MP3FILE *pmp3) {
*/
void db_trim_string(char *string) {
if(!string)
- return;
+ return;
while(strlen(string) && (string[strlen(string) - 1] == ' '))
- string[strlen(string) - 1] = '\0';
+ string[strlen(string) - 1] = '\0';
}
diff --git a/src/db-generic.h b/src/db-generic.h
index 5e673c44..83d10135 100644
--- a/src/db-generic.h
+++ b/src/db-generic.h
@@ -32,7 +32,7 @@ typedef enum {
metaPersistentId,
metaContainerItemId,
metaParentContainerId,
-
+
firstTypeSpecificMetaId,
// song meta data
@@ -123,7 +123,7 @@ typedef struct tag_dbqueryinfo {
} DBQUERYINFO;
typedef struct {
- const char* tag;
+ const char* tag;
MetaFieldName_t bit;
} METAMAP;
@@ -137,34 +137,36 @@ extern DAAP_ITEMS taglist[];
extern int db_set_backend(char *type);
-extern int db_open(char *parameters);
+extern int db_open(char **pe, char *parameters);
extern int db_init(int reload);
extern int db_deinit(void);
extern int db_revision(void);
-extern int db_add(MP3FILE *pmp3);
+extern int db_add(char **pe, MP3FILE *pmp3);
-extern int db_enum_start(DBQUERYINFO *pinfo);
-extern int db_enum_size(DBQUERYINFO *pinfo, int *count);
-extern int db_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap);
-extern int db_enum_reset(DBQUERYINFO *pinfo);
-extern int db_enum_end(void);
+extern int db_enum_start(char **pe, DBQUERYINFO *pinfo);
+extern int db_enum_size(char **pe, DBQUERYINFO *pinfo, int *count, int *total_size);
+extern int db_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size, unsigned char **pdmap);
+extern int db_enum_reset(char **pe, DBQUERYINFO *pinfo);
+extern int db_enum_end(char **pe);
extern int db_start_scan(void);
extern int db_end_song_scan(void);
extern int db_end_scan(void);
extern int db_exists(char *path);
extern int db_scanning(void);
-extern int db_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid);
-extern int db_add_playlist_item(int playlistid, int songid);
-extern int db_edit_playlist(int id, char *name, char *clause);
-extern int db_delete_playlist(int playlistid);
-extern int db_delete_playlist_item(int playlistid, int songid);
+extern int db_add_playlist(char **pe, char *name, int type, char *clause, char *path, int index, int *playlistid);
+extern int db_add_playlist_item(char **pe, int playlistid, int songid);
+extern int db_edit_playlist(char **pe, int id, char *name, char *clause);
+extern int db_delete_playlist(char **pe, int playlistid);
+extern int db_delete_playlist_item(char **pe, int playlistid, int songid);
-extern MP3FILE *db_fetch_item(int id);
-extern MP3FILE *db_fetch_path(char *path, int index);
-extern M3UFILE *db_fetch_playlist(char *path, int index);
+extern void db_get_error(char **pe, int err, ...);
+
+extern MP3FILE *db_fetch_item(char **pe, int id);
+extern MP3FILE *db_fetch_path(char **pe, char *path, int index);
+extern M3UFILE *db_fetch_playlist(char **pe, char *path, int index);
/* metatag parsing */
@@ -182,8 +184,9 @@ extern int db_dmap_add_container(unsigned char *where, char *tag, int size);
/* Holdover functions from old db interface...
* should these be removed? Refactored?
*/
-extern int db_get_song_count(void);
-extern int db_get_playlist_count(void);
+
+extern int db_get_song_count(char **pe, int *count);
+extern int db_get_playlist_count(char **pe, int *count);
extern void db_dispose_item(MP3FILE *pmp3);
extern void db_dispose_playlist(M3UFILE *pm3u);
diff --git a/src/db-sql-sqlite2.c b/src/db-sql-sqlite2.c
new file mode 100644
index 00000000..2b1bcad4
--- /dev/null
+++ b/src/db-sql-sqlite2.c
@@ -0,0 +1,456 @@
+/*
+ * $Id$
+ * sqlite2-specific db implementation
+ *
+ * Copyright (C) 2005 Ron Pedde (ron@pedde.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * This file handles sqlite2 databases. SQLite2 databases
+ * should have a dsn of:
+ *
+ * sqlite2:/path/to/folder
+ *
+ * The actual db will be appended to the passed path.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define _XOPEN_SOURCE 500
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "err.h"
+#include "db-generic.h"
+#include "db-sql.h"
+#include "db-sql-sqlite2.h"
+
+#ifndef TRUE
+# define TRUE 1
+# define FALSE 0
+#endif
+
+
+/* Globals */
+static sqlite *db_sqlite2_songs; /**< Database that holds the mp3 info */
+static pthread_mutex_t db_sqlite2_mutex = PTHREAD_MUTEX_INITIALIZER; /**< sqlite not reentrant */
+static sqlite_vm *db_sqlite2_pvm;
+static int db_sqlite2_reload=0;
+static char *db_sqlite2_enum_query;
+static int db_sqlite2_in_enum=0;
+
+static char db_sqlite2_path[PATH_MAX + 1];
+
+#define DB_SQLITE2_VERSION 8
+
+
+/* Forwards */
+void db_sqlite2_lock(void);
+void db_sqlite2_unlock(void);
+extern char *db_initial;
+
+/**
+ * lock the db_mutex
+ */
+void db_sqlite2_lock(void) {
+ int err;
+
+ if((err=pthread_mutex_lock(&db_sqlite2_mutex))) {
+ DPRINTF(E_FATAL,L_DB,"cannot lock sqlite lock: %s\n",strerror(err));
+ }
+}
+
+/**
+ * unlock the db_mutex
+ */
+void db_sqlite2_unlock(void) {
+ int err;
+
+ if((err=pthread_mutex_unlock(&db_sqlite2_mutex))) {
+ DPRINTF(E_FATAL,L_DB,"cannot unlock sqlite2 lock: %s\n",strerror(err));
+ }
+}
+
+/**
+ *
+ */
+char *db_sqlite2_vmquery(char *fmt,va_list ap) {
+ return sqlite_vmprintf(fmt,ap);
+}
+
+/**
+ *
+ */
+void db_sqlite2_vmfree(char *query) {
+ sqlite_freemem(query);
+}
+
+
+/**
+ * open a sqlite2 database
+ *
+ * @param dsn the full dns to the database
+ * (sqlite2:/path/to/database)
+ *
+ * @returns DB_E_SUCCESS on success
+ */
+int db_sqlite2_open(char **pe, char *dsn) {
+ char *perr;
+ int ver;
+ int err;
+
+ snprintf(db_sqlite2_path,sizeof(db_sqlite2_path),"%s/songs.db",dsn);
+
+ db_sqlite2_lock();
+ db_sqlite2_songs=sqlite_open(db_sqlite2_path,0666,&perr);
+ if(!db_sqlite2_songs) {
+ db_get_error(pe,DB_E_SQL_ERROR,perr);
+ DPRINTF(E_LOG,L_DB,"db_sqlite2_open: %s (%s)\n",perr,
+ db_sqlite2_path);
+ sqlite_freemem(perr);
+ db_sqlite2_unlock();
+ return DB_E_SQL_ERROR;
+ }
+
+ sqlite_busy_timeout(db_sqlite2_songs,30000); /* 30 seconds */
+
+ err = db_sql_fetch_int(pe,&ver,"select value from config where term='version'");
+ if(err != DB_E_SUCCESS) {
+ free(*pe);
+ /* create the table */
+ DPRINTF(E_FATAL,L_DB,"Can't create table yet!\n");
+ }
+
+ if(ver != DB_SQLITE2_VERSION) {
+ DPRINTF(E_FATAL,L_DB,"Can't upgrade database!\n");
+ }
+
+ db_sqlite2_unlock();
+ return DB_E_SUCCESS;
+}
+
+/**
+ * close the database
+ */
+int db_sqlite2_close(void) {
+ db_sqlite2_lock();
+ sqlite_close(db_sqlite2_songs);
+ db_sqlite2_unlock();
+ return DB_E_SUCCESS;
+}
+
+/**
+ * execute a throwaway query against the database, disregarding
+ * the outcome
+ *
+ * @param pe db error structure
+ * @param loglevel error level to return if the query fails
+ * @param fmt sprintf-style arguements
+ *
+ * @returns DB_E_SUCCESS on success
+ */
+int db_sqlite2_exec(char **pe, int loglevel, char *fmt, ...) {
+ va_list ap;
+ char *query;
+ int err;
+ char *perr;
+
+ va_start(ap,fmt);
+ query=sqlite_vmprintf(fmt,ap);
+ va_end(ap);
+
+ DPRINTF(E_DBG,L_DB,"Executing: %s\n",query);
+
+ db_sqlite2_lock();
+ err=sqlite_exec(db_sqlite2_songs,query,NULL,NULL,&perr);
+ if(err != SQLITE_OK) {
+ db_get_error(pe,DB_E_SQL_ERROR,perr);
+
+ DPRINTF(loglevel == E_FATAL ? E_LOG : loglevel,L_DB,"Query: %s\n",
+ query);
+ DPRINTF(loglevel,L_DB,"Error: %s\n",perr);
+ sqlite_freemem(perr);
+ } else {
+ DPRINTF(E_DBG,L_DB,"Rows: %d\n",sqlite_changes(db_sqlite2_songs));
+ }
+ sqlite_freemem(query);
+
+ db_sqlite2_unlock();
+
+ if(err != SQLITE_OK)
+ return DB_E_SQL_ERROR;
+ return DB_E_SUCCESS;
+}
+
+/**
+ * start enumerating rows in a select
+ */
+int db_sqlite2_enum_begin(char **pe, char *fmt, ...) {
+ va_list ap;
+ int err;
+ char *perr;
+ const char *ptail;
+
+ if(!db_sqlite2_in_enum) {
+ va_start(ap, fmt);
+ db_sqlite2_lock();
+ db_sqlite2_enum_query = sqlite_vmprintf(fmt,ap);
+ va_end(ap);
+ }
+
+ DPRINTF(E_DBG,L_DB,"Executing :%s\n",db_sqlite2_enum_query);
+ db_sqlite2_in_enum=1;
+
+ err=sqlite_compile(db_sqlite2_songs,db_sqlite2_enum_query,&ptail,&db_sqlite2_pvm,&perr);
+
+ if(err != SQLITE_OK) {
+ db_get_error(pe,DB_E_SQL_ERROR,perr);
+ sqlite_freemem(perr);
+ db_sqlite2_in_enum=0;
+ db_sqlite2_unlock();
+ sqlite_freemem(db_sqlite2_enum_query);
+ return DB_E_SQL_ERROR;
+ }
+
+ /* otherwise, we leave the db locked while we walk through the enums */
+ return DB_E_SUCCESS;
+}
+
+/**
+ * fetch the next row
+ *
+ * @param pe error string, if result isn't DB_E_SUCCESS
+ * @param pr pointer to a row struct
+ *
+ * @returns DB_E_SUCCESS with *pr=NULL when end of table,
+ * DB_E_SUCCESS with a valid row when more data,
+ * DB_E_* on error
+ */
+int db_sqlite2_enum_fetch(char **pe, SQL_ROW *pr) {
+ int err;
+ char *perr=NULL;;
+ const char **colarray;
+ int cols;
+ int counter=10;
+
+ while(counter--) {
+ err=sqlite_step(db_sqlite2_pvm,&cols,(const char ***)pr,&colarray);
+ if(err != SQLITE_BUSY)
+ break;
+ usleep(100);
+ }
+
+ if(err == SQLITE_DONE) {
+ *pr = NULL;
+ return DB_E_SUCCESS;
+ }
+
+ if(err == SQLITE_ROW) {
+ return DB_E_SUCCESS;
+ }
+
+ db_get_error(pe,DB_E_SQL_ERROR,perr);
+ return DB_E_SQL_ERROR;
+}
+
+/**
+ * end the db enumeration
+ */
+int db_sqlite2_enum_end(char **pe) {
+ int err;
+ char *perr;
+
+ db_sqlite2_in_enum=0;
+ sqlite_freemem(db_sqlite2_enum_query);
+
+ err = sqlite_finalize(db_sqlite2_pvm,&perr);
+ if(err != SQLITE_OK) {
+ db_get_error(pe,DB_E_SQL_ERROR,perr);
+ sqlite_freemem(perr);
+ db_sqlite2_unlock();
+ return DB_E_SQL_ERROR;
+ }
+
+ db_sqlite2_unlock();
+ return DB_E_SUCCESS;
+}
+
+/**
+ * restart the enumeration
+ */
+int db_sqlite2_enum_restart(char **pe) {
+ return db_sqlite2_enum_begin(pe,NULL);
+}
+
+
+int db_sqlite2_event(int event_type) {
+ switch(event_type) {
+
+ case DB_SQL_EVENT_STARTUP: /* this is a startup with existing songs */
+ db_sqlite2_exec(NULL,E_FATAL,"vacuum");
+ db_sqlite2_reload=0;
+ break;
+
+ case DB_SQL_EVENT_FULLRELOAD: /* either a fresh load or force load */
+ db_sqlite2_exec(NULL,E_DBG,"delete index idx_path");
+ db_sqlite2_exec(NULL,E_DBG,"delete index idx_songid");
+ db_sqlite2_exec(NULL,E_DBG,"delete index idx_playlistid");
+
+ db_sqlite2_exec(NULL,E_DBG,"drop table songs");
+ db_sqlite2_exec(NULL,E_DBG,"drop table playlists");
+ db_sqlite2_exec(NULL,E_DBG,"drop table playlistitems");
+ db_sqlite2_exec(NULL,E_DBG,"drop table config");
+
+ db_sqlite2_exec(NULL,E_DBG,"vacuum");
+
+ db_sqlite2_exec(NULL,E_DBG,db_initial);
+ db_sqlite2_reload=1;
+ break;
+
+ case DB_SQL_EVENT_SONGSCANSTART:
+ if(db_sqlite2_reload) {
+ db_sqlite2_exec(NULL,E_FATAL,"pragma synchronous = off");
+ db_sqlite2_exec(NULL,E_FATAL,"begin transaction");
+ } else {
+ db_sqlite2_exec(NULL,E_DBG,"drop table updated");
+ db_sqlite2_exec(NULL,E_FATAL,"create temp table updated (id int)");
+ db_sqlite2_exec(NULL,E_DBG,"drop table plupdated");
+ db_sqlite2_exec(NULL,E_FATAL,"create temp table plupdated(id int)");
+ }
+ break;
+
+ case DB_SQL_EVENT_SONGSCANEND:
+ if(db_sqlite2_reload) {
+ db_sqlite2_exec(NULL,E_FATAL,"commit transaction");
+ db_sqlite2_exec(NULL,E_FATAL,"create index idx_path on songs(path)");
+ db_sqlite2_exec(NULL,E_DBG,"delete from config where term='rescan'");
+ } else {
+ db_sqlite2_exec(NULL,E_FATAL,"delete from songs where id not in (select id from updated)");
+ db_sqlite2_exec(NULL,E_FATAL,"update songs set force_update=0");
+ db_sqlite2_exec(NULL,E_FATAL,"drop table updated");
+ }
+ break;
+
+ case DB_SQL_EVENT_PLSCANSTART:
+ db_sqlite2_exec(NULL,E_FATAL,"begin transaction");
+ break;
+
+ case DB_SQL_EVENT_PLSCANEND:
+ db_sqlite2_exec(NULL,E_FATAL,"end transaction");
+
+ if(db_sqlite2_reload) {
+ db_sqlite2_exec(NULL,E_FATAL,"pragma synchronous=normal");
+ db_sqlite2_exec(NULL,E_FATAL,"create index idx_songid on playlistitems(songid)");
+ db_sqlite2_exec(NULL,E_FATAL,"create index idx_playlistid on playlistitems(playlistid)");
+
+ } else {
+ db_sqlite2_exec(NULL,E_FATAL,"delete from playlists where "
+ "((type=%d) OR (type=%d)) and "
+ "id not in (select id from plupdated)",
+ PL_STATICFILE,PL_STATICXML);
+ db_sqlite2_exec(NULL,E_FATAL,"delete from playlistitems where "
+ "playlistid not in (select distinct "
+ "id from playlists)");
+ db_sqlite2_exec(NULL,E_FATAL,"drop table plupdated");
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return DB_E_SUCCESS;
+}
+
+char *db_initial =
+ "create table songs (\n"
+ " id INTEGER PRIMARY KEY NOT NULL,\n"
+ " path VARCHAR(4096) UNIQUE NOT NULL,\n"
+ " fname VARCHAR(255) NOT NULL,\n"
+ " title VARCHAR(1024) DEFAULT NULL,\n"
+ " artist VARCHAR(1024) DEFAULT NULL,\n"
+ " album VARCHAR(1024) DEFAULT NULL,\n"
+ " genre VARCHAR(255) DEFAULT NULL,\n"
+ " comment VARCHAR(4096) DEFAULT NULL,\n"
+ " type VARCHAR(255) DEFAULT NULL,\n"
+ " composer VARCHAR(1024) DEFAULT NULL,\n"
+ " orchestra VARCHAR(1024) DEFAULT NULL,\n"
+ " conductor VARCHAR(1024) DEFAULT NULL,\n"
+ " grouping VARCHAR(1024) DEFAULT NULL,\n"
+ " url VARCHAR(1024) DEFAULT NULL,\n"
+ " bitrate INTEGER DEFAULT 0,\n"
+ " samplerate INTEGER DEFAULT 0,\n"
+ " song_length INTEGER DEFAULT 0,\n"
+ " file_size INTEGER DEFAULT 0,\n"
+ " year INTEGER DEFAULT 0,\n"
+ " track INTEGER DEFAULT 0,\n"
+ " total_tracks INTEGER DEFAULT 0,\n"
+ " disc INTEGER DEFAULT 0,\n"
+ " total_discs INTEGER DEFAULT 0,\n"
+ " bpm INTEGER DEFAULT 0,\n"
+ " compilation INTEGER DEFAULT 0,\n"
+ " rating INTEGER DEFAULT 0,\n"
+ " play_count INTEGER DEFAULT 0,\n"
+ " data_kind INTEGER DEFAULT 0,\n"
+ " item_kind INTEGER DEFAULT 0,\n"
+ " description INTEGER DEFAULT 0,\n"
+ " time_added INTEGER DEFAULT 0,\n"
+ " time_modified INTEGER DEFAULT 0,\n"
+ " time_played INTEGER DEFAULT 0,\n"
+ " db_timestamp INTEGER DEFAULT 0,\n"
+ " disabled INTEGER DEFAULT 0,\n"
+ " sample_count INTEGER DEFAULT 0,\n"
+ " force_update INTEGER DEFAULT 0,\n"
+ " codectype VARCHAR(5) DEFAULT NULL,\n"
+ " idx INTEGER NOT NULL\n"
+ ");\n"
+ "create table config (\n"
+ " term VARCHAR(255) NOT NULL,\n"
+ " subterm VARCHAR(255) DEFAULT NULL,\n"
+ " value VARCHAR(1024) NOT NULL\n"
+ ");\n"
+ "create table playlistitems (\n"
+ " id INTEGER PRIMARY KEY NOT NULL,\n"
+ " playlistid INTEGER NOT NULL,\n"
+ " songid INTEGER NOT NULL\n"
+ ");\n"
+ "create table playlists (\n"
+ " id INTEGER PRIMARY KEY NOT NULL,\n"
+ " title VARCHAR(255) NOT NULL,\n"
+ " type INTEGER NOT NULL,\n"
+ " items INTEGER NOT NULL,\n"
+ " query VARCHAR(1024),\n"
+ " db_timestamp INTEGER NOT NULL,\n"
+ " path VARCHAR(4096),\n"
+ " idx INTEGER NOT NULL\n"
+ ");\n"
+ "insert into config values ('version','','1');\n"
+ "insert into playlists values (1,'Library',1,0,'1',0,'',0);\n"
+ "create index idx_path on songs(path);\n"
+ "create index idx_songid on playlistitems(songid);\n"
+ "create index idx_playlistid on playlistitems(playlistid);\n";
+
+
diff --git a/src/db-sql-sqlite2.h b/src/db-sql-sqlite2.h
new file mode 100644
index 00000000..36600009
--- /dev/null
+++ b/src/db-sql-sqlite2.h
@@ -0,0 +1,42 @@
+/*
+ * $Id$
+ * sqlite2-specific db implementation
+ *
+ * Copyright (C) 2005 Ron Pedde (ron@pedde.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DB_SQL_SQLITE2_
+#define _DB_SQL_SQLITE2_
+
+extern int db_sqlite2_open(char **pe, char *dsn);
+extern int db_sqlite2_close(void);
+
+/* simple utility functions */
+extern int db_sqlite2_exec(char **pe, int loglevel, char *fmt, ...);
+extern char *db_sqlite2_vmquery(char *fmt,va_list ap);
+extern void db_sqlite2_vmfree(char *query);
+
+/* walk through a table */
+extern int db_sqlite2_enum_begin(char **pe, char *fmt, ...);
+extern int db_sqlite2_enum_fetch(char **pe, SQL_ROW *pr);
+extern int db_sqlite2_enum_end(char **pe);
+extern int db_sqlite2_enum_restart(char **pe);
+
+int db_sqlite2_event(int event_type);
+
+#endif /* _DB_SQL_SQLITE2_ */
+
diff --git a/src/db-sql.c b/src/db-sql.c
new file mode 100644
index 00000000..97e46604
--- /dev/null
+++ b/src/db-sql.c
@@ -0,0 +1,1636 @@
+/*
+ * $Id$
+ * sql-specific db implementation
+ *
+ * Copyright (C) 2005 Ron Pedde (ron@pedde.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define _XOPEN_SOURCE 500
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "err.h"
+#include "mp3-scanner.h"
+#include "db-generic.h"
+#include "db-sql.h"
+#include "db-sql-sqlite2.h"
+#include "restart.h"
+#include "ssc.h"
+#include "smart-parser.h"
+
+/* Globals */
+static int db_sql_reload=0;
+static int db_sql_in_playlist_scan=0;
+static int db_sql_in_scan=0;
+static int db_sql_need_dispose=0;
+
+/* Forwards */
+int db_sql_get_size(DBQUERYINFO *pinfo, char **valarray);
+int db_sql_build_dmap(DBQUERYINFO *pinfo, char **valarray, unsigned char *presult, int len);
+void db_sql_build_mp3file(char **valarray, MP3FILE *pmp3);
+int db_sql_update(char **pe, MP3FILE *pmp3);
+int db_sql_update_playlists(char **pe);
+char *db_sql_parse_smart(char *phrase);
+
+
+#define STR(a) (a) ? (a) : ""
+#define ISSTR(a) ((a) && strlen((a)))
+#define MAYBEFREE(a) { if((a)) free((a)); };
+
+
+/**
+ * fetch a single row, using the underlying database enum
+ * functions
+ */
+int db_sql_fetch_row(char **pe, SQL_ROW *row, char *fmt, ...) {
+ int err;
+ char *query;
+ va_list ap;
+
+ db_sql_need_dispose = 0;
+
+ va_start(ap,fmt);
+ query=db_sqlite2_vmquery(fmt,ap);
+ va_end(ap);
+
+ err=db_sqlite2_enum_begin(pe,query);
+ db_sqlite2_vmfree(query);
+
+
+ if(err != DB_E_SUCCESS) {
+ return err;
+ }
+
+ err=db_sqlite2_enum_fetch(pe, row);
+ if(err != DB_E_SUCCESS) {
+ db_sqlite2_enum_end(NULL);
+ return err;
+ }
+
+ if(!row) {
+ db_sqlite2_enum_end(NULL);
+ db_get_error(pe,DB_E_NOROWS);
+ return DB_E_NOROWS;
+ }
+
+ db_sql_need_dispose = 1;
+ return DB_E_SUCCESS;
+}
+
+int db_sql_fetch_int(char **pe, int *result, char *fmt, ...) {
+ int err;
+ char *query;
+ va_list ap;
+ SQL_ROW row;
+
+ va_start(ap,fmt);
+ query=db_sqlite2_vmquery(fmt,ap);
+ va_end(ap);
+
+ err = db_sql_fetch_row(pe, &row, query);
+ if(err != DB_E_SUCCESS)
+ return err;
+
+ *result = atoi(row[0]);
+ db_sql_dispose_row();
+ return DB_E_SUCCESS;
+}
+
+int db_sql_fetch_char(char **pe, char **result, char *fmt, ...) {
+ int err;
+ char *query;
+ va_list ap;
+ SQL_ROW row;
+
+ va_start(ap,fmt);
+ query=db_sqlite2_vmquery(fmt,ap);
+ va_end(ap);
+
+ err = db_sql_fetch_row(pe, &row, query);
+ if(err != DB_E_SUCCESS)
+ return err;
+
+ *result = strdup(row[0]);
+ db_sql_dispose_row();
+ return DB_E_SUCCESS;
+}
+
+int db_sql_dispose_row(void) {
+ int err = DB_E_SUCCESS;
+
+ /* don't really need the row */
+ if(db_sql_need_dispose) {
+ err=db_sqlite2_enum_end(NULL);
+ db_sql_need_dispose=0;
+ }
+
+ return err;
+}
+
+/**
+ * get the sql where clause for a smart playlist spec. This
+ * where clause must be freed by the caller
+ *
+ * @param phrase playlist spec to be converted
+ * @returns sql where clause if successful, NULL otherwise
+ */
+char *db_sql_parse_smart(char *phrase) {
+ PARSETREE pt;
+ char *result = NULL;
+
+ if(strcmp(phrase,"1") == 0)
+ return strdup("1");
+
+ pt=sp_init();
+ if(!pt)
+ return NULL;
+
+ if(!sp_parse(pt,phrase)) {
+ DPRINTF(E_LOG,L_DB,"Error parsing smart playlist: %s",sp_get_error(pt));
+ sp_dispose(pt);
+ return strdup("0");
+ } else {
+ result = sp_sql_clause(pt);
+ }
+
+ sp_dispose(pt);
+ return result;
+}
+
+/**
+ * open sqlite database
+ *
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_open(char **pe, char *parameters) {
+ return db_sqlite2_open(pe, parameters);
+}
+
+/**
+ * initialize the sqlite database, reloading if requested
+ *
+ * @param reload whether or not to do a full reload on the db
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_init(int reload) {
+ int items;
+ int rescan = 0;
+ int err;
+
+
+ err=db_sql_get_count(NULL,&items, countSongs);
+ if(err != DB_E_SUCCESS)
+ items = 0;
+
+ /* check if a request has been written into the db (by a db upgrade?) */
+ if(db_sql_fetch_int(NULL,&rescan,"select value from config where "
+ "term='rescan'") == DB_E_SUCCESS)
+ {
+ if(rescan)
+ reload=1;
+ }
+
+
+
+ if(reload || (!items)) {
+ DPRINTF(E_LOG,L_DB,"Full reload...\n");
+ db_sqlite2_event(DB_SQL_EVENT_FULLRELOAD);
+ db_sql_reload=1;
+ } else {
+ db_sqlite2_event(DB_SQL_EVENT_STARTUP);
+ db_sql_reload=0;
+ }
+
+ return DB_E_SUCCESS;
+}
+
+/**
+ * close the database
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_deinit(void) {
+ return db_sqlite2_close();
+}
+
+/**
+ * start a background scan
+ *
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_start_scan(void) {
+ DPRINTF(E_DBG,L_DB,"Starting db scan\n");
+ db_sqlite2_event(DB_SQL_EVENT_SONGSCANSTART);
+
+ db_sql_in_scan=1;
+ db_sql_in_playlist_scan=0;
+ return DB_E_SUCCESS;
+}
+
+/**
+ * end song scan -- start playlist scan
+ *
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_end_song_scan(void) {
+ DPRINTF(E_DBG,L_DB,"Ending song scan\n");
+
+ db_sqlite2_event(DB_SQL_EVENT_SONGSCANEND);
+ db_sqlite2_event(DB_SQL_EVENT_PLSCANSTART);
+
+ db_sql_in_scan=0;
+ db_sql_in_playlist_scan=1;
+
+ return DB_E_SUCCESS;
+}
+
+/**
+ * stop a db scan
+ *
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_end_scan(void) {
+ db_sqlite2_event(DB_SQL_EVENT_PLSCANEND);
+
+ db_sql_update_playlists(NULL);
+ db_sql_reload=0;
+ db_sql_in_playlist_scan=0;
+
+ return DB_E_SUCCESS;
+}
+
+/**
+ * delete a playlist
+ *
+ * @param playlistid playlist to delete
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_delete_playlist(char **pe, int playlistid) {
+ int type;
+ int result;
+
+ result=db_sql_fetch_int(pe,&type,"select type from playlists where id=%d",
+ playlistid);
+
+ if(result != DB_E_SUCCESS) {
+ if(result == DB_E_NOROWS) { /* Override the generic error */
+ free(*pe);
+ db_get_error(pe,DB_E_INVALID_PLAYLIST);
+ return DB_E_INVALID_PLAYLIST;
+ }
+
+ return result;
+ }
+
+ /* got a good playlist, now do what we need to do */
+ db_sqlite2_exec(pe,E_FATAL,"delete from playlists where id=%d",playlistid);
+ db_sqlite2_exec(pe,E_FATAL,"delete from playlistitems where playlistid=%d",playlistid);
+
+ return DB_E_SUCCESS;
+}
+
+/**
+ * delete a song from a playlist
+ *
+ * @param playlistid playlist to delete item from
+ * @param songid song to delete from playlist
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_delete_playlist_item(char **pe, int playlistid, int songid) {
+ int result;
+ int playlist_type;
+ int count;
+
+ /* first, check the playlist */
+ result=db_sql_fetch_int(pe,&playlist_type,
+ "select type from playlists where id=%d",
+ playlistid);
+
+ if(result != DB_E_SUCCESS) {
+ if(result == DB_E_NOROWS) { /* Override generic error */
+ free(*pe);
+ db_get_error(pe,DB_E_INVALID_PLAYLIST);
+ return DB_E_INVALID_PLAYLIST;
+ }
+ return result;
+ }
+
+ if(playlist_type == PL_SMART) { /* can't delete from a smart playlist */
+ db_get_error(pe,DB_E_INVALIDTYPE);
+ return DB_E_INVALIDTYPE;
+ }
+
+ /* make sure the songid is valid */
+ result=db_sql_fetch_int(pe,&count,"select count(*) from playlistitems "
+ "where playlistid=%d and songid=%d",
+ playlistid,songid);
+
+ if(result != DB_E_SUCCESS) {
+ if(result == DB_E_NOROWS) { /* Override generic error */
+ free(*pe);
+ db_get_error(pe,DB_E_INVALID_SONGID);
+ return DB_E_INVALID_SONGID;
+ }
+ return result;
+ }
+
+ /* looks valid, so lets add the item */
+ result=db_sqlite2_exec(pe,E_DBG,"delete from playlistitems where "
+ "playlistid=%d and songid=%d",playlistid,songid);
+
+ return result;
+}
+
+/**
+ * edit a playlist. The only things worth changing are the name
+ * and the "where" clause.
+ *
+ * @param id id of the playlist to alter
+ * @param name new name of the playlist
+ * @param where new where clause
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_edit_playlist(char **pe, int id, char *name, char *clause) {
+ int result;
+ int playlist_type;
+
+ /* first, check the playlist */
+ result=db_sql_fetch_int(pe,&playlist_type,
+ "select type from playlists where id=%d",id);
+
+ if(result != DB_E_SUCCESS) {
+ if(result == DB_E_NOROWS) { /* Override generic error */
+ free(*pe);
+ db_get_error(pe,DB_E_INVALID_PLAYLIST);
+ return DB_E_INVALID_PLAYLIST;
+ }
+ return result;
+ }
+
+ /* TODO: check for duplicate names here */
+
+ if(playlist_type != PL_SMART) { /* Ignore the clause */
+ return db_sqlite2_exec(pe,E_LOG,"update playlists set title='%q' "
+ "where id=%d",name,id);
+ }
+
+ return db_sqlite2_exec(pe,E_LOG,"update playlists set title='%q',"
+ "query='%q' where id=%d",name, clause, id);
+}
+
+/**
+ * add a playlist
+ *
+ * @param name name of the playlist
+ * @param type playlist type: 0 - static, 1 - smart, 2 - m3u
+ * @param clause: "where" clause for smart playlist
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_add_playlist(char **pe, char *name, int type, char *clause, char *path, int index, int *playlistid) {
+ int cnt=0;
+ int result=DB_E_SUCCESS;
+ char *criteria;
+
+ result=db_sql_fetch_int(pe,&cnt,"select count(*) from playlists where "
+ "upper(title)=upper('%q')",name);
+
+ if(result == DB_E_NOROWS) { /* good playlist name */
+ free(*pe);
+ } else {
+ if(result != DB_E_SUCCESS) {
+ return result;
+ } else {
+ db_get_error(pe,DB_E_DUPLICATE_PLAYLIST);
+ return DB_E_DUPLICATE_PLAYLIST;
+ }
+ }
+
+ if((type == PL_SMART) && (!clause)) {
+ db_get_error(pe,DB_E_NOCLAUSE);
+ return DB_E_NOCLAUSE;
+ }
+
+ /* Let's throw it in */
+ switch(type) {
+ case PL_STATICWEB: /* static, maintained in web interface */
+ case PL_STATICFILE: /* static, from file */
+ case PL_STATICXML: /* from iTunes XML file */
+ result = db_sqlite2_exec(pe,E_LOG,"insert into playlists "
+ "(title,type,items,query,db_timestamp,path,idx) "
+ "values ('%q',%d,0,NULL,%d,'%q',%d)",
+ name,type,time(NULL),path,index);
+ break;
+ case PL_SMART: /* smart */
+ criteria = db_sql_parse_smart(clause);
+ if(!criteria) {
+ db_get_error(pe,DB_E_PARSE);
+ return DB_E_PARSE;
+ }
+ free(criteria);
+
+ result = db_sqlite2_exec(pe,E_LOG,"insert into playlists "
+ "(title,type,items,query,db_timestamp,idx) "
+ "values ('%q',%d,%d,'%q',%d,0)",
+ name,PL_SMART,cnt,clause,time(NULL));
+ break;
+ }
+
+ if(result)
+ return result;
+
+ result = db_sql_fetch_int(pe,playlistid,
+ "select id from playlists where title='%q'",
+ name);
+
+ if(((type==PL_STATICFILE)||(type==PL_STATICXML))
+ && (db_sql_in_playlist_scan) && (!db_sql_reload)) {
+ db_sqlite2_exec(NULL,E_FATAL,"insert into plupdated values (%d)",*playlistid);
+ }
+
+ return result;
+}
+
+/**
+ * add a song to a static playlist
+ *
+ * @param playlistid playlist to add song to
+ * @param songid song to add
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_add_playlist_item(char **pe, int playlistid, int songid) {
+ int result;
+ int playlist_type;
+ int count;
+
+ /* first, check the playlist */
+ result=db_sql_fetch_int(pe,&playlist_type,
+ "select type from playlists where id=%d",
+ playlistid);
+
+ if(result != DB_E_SUCCESS) {
+ if(result == DB_E_NOROWS) { /* Override generic error */
+ free(*pe);
+ db_get_error(pe,DB_E_INVALID_PLAYLIST);
+ return DB_E_INVALID_PLAYLIST;
+ }
+ return result;
+ }
+
+ if(playlist_type == 1) { /* can't add to smart playlists, or static */
+ db_get_error(pe,DB_E_INVALIDTYPE);
+ return DB_E_INVALIDTYPE;
+ }
+
+ /* make sure the songid is valid */
+ result=db_sql_fetch_int(pe,&count,"select count(*) from songs where "
+ "id=%d",songid);
+
+ if(result != DB_E_SUCCESS) {
+ if(result == DB_E_NOROWS) { /* Override generic error */
+ free(*pe);
+ db_get_error(pe,DB_E_INVALID_SONGID);
+ return DB_E_INVALID_SONGID;
+ }
+ return result;
+ }
+
+ /* looks valid, so lets add the item */
+ result=db_sqlite2_exec(pe,E_DBG,"insert into playlistitems "
+ "(playlistid, songid) values (%d,%d)",
+ playlistid,songid);
+ return result;
+}
+
+
+/**
+ * add a database item
+ *
+ * @param pmp3 mp3 file to add
+ */
+int db_sql_add(char **pe, MP3FILE *pmp3) {
+ int err;
+ int count;
+
+ DPRINTF(E_SPAM,L_DB,"Entering db_sql_add\n");
+
+ if(!pmp3->time_added)
+ pmp3->time_added = (int)time(NULL);
+
+ if(!pmp3->time_modified)
+ pmp3->time_modified = (int)time(NULL);
+
+ pmp3->db_timestamp = (int)time(NULL);
+
+
+ if(!db_sql_reload) { /* if we are in a reload, then no need to check */
+ err=db_sql_fetch_int(NULL,&count,"select count(*) from songs where "
+ "path='%q'",pmp3->path);
+
+ if((err == DB_E_SUCCESS) && (count == 1)) { /* we should update */
+ return db_sql_update(pe,pmp3);
+ }
+ }
+
+ pmp3->play_count=0;
+ pmp3->time_played=0;
+
+ err=db_sqlite2_exec(pe,E_DBG,"INSERT INTO songs VALUES "
+ "(NULL," // id
+ "'%q'," // path
+ "'%q'," // fname
+ "'%q'," // title
+ "'%q'," // artist
+ "'%q'," // album
+ "'%q'," // genre
+ "'%q'," // comment
+ "'%q'," // type
+ "'%q'," // composer
+ "'%q'," // orchestra
+ "'%q'," // conductor
+ "'%q'," // grouping
+ "'%q'," // url
+ "%d," // bitrate
+ "%d," // samplerate
+ "%d," // song_length
+ "%d," // file_size
+ "%d," // year
+ "%d," // track
+ "%d," // total_tracks
+ "%d," // disc
+ "%d," // total_discs
+ "%d," // bpm
+ "%d," // compilation
+ "%d," // rating
+ "0," // play_count
+ "%d," // data_kind
+ "%d," // item_kind
+ "'%q'," // description
+ "%d," // time_added
+ "%d," // time_modified
+ "%d," // time_played
+ "%d," // db_timestamp
+ "%d," // disabled
+ "%d," // sample_count
+ "0," // force_update
+ "'%q'," // codectype
+ "%d)", // index
+ STR(pmp3->path),
+ STR(pmp3->fname),
+ STR(pmp3->title),
+ STR(pmp3->artist),
+ STR(pmp3->album),
+ STR(pmp3->genre),
+ STR(pmp3->comment),
+ STR(pmp3->type),
+ STR(pmp3->composer),
+ STR(pmp3->orchestra),
+ STR(pmp3->conductor),
+ STR(pmp3->grouping),
+ STR(pmp3->url),
+ pmp3->bitrate,
+ pmp3->samplerate,
+ pmp3->song_length,
+ pmp3->file_size,
+ pmp3->year,
+ pmp3->track,
+ pmp3->total_tracks,
+ pmp3->disc,
+ pmp3->total_discs,
+ pmp3->bpm,
+ pmp3->compilation,
+ pmp3->rating,
+ pmp3->data_kind,
+ pmp3->item_kind,
+ STR(pmp3->description),
+ pmp3->time_added,
+ pmp3->time_modified,
+ pmp3->time_played,
+ pmp3->db_timestamp,
+ pmp3->disabled,
+ pmp3->sample_count,
+ STR(pmp3->codectype),
+ pmp3->index);
+
+ if(err != DB_E_SUCCESS)
+ DPRINTF(E_FATAL,L_DB,"Error inserting file %s in database\n",pmp3->fname);
+
+ if((db_sql_in_scan)&&(!db_sql_reload)) {
+ /* FIXME: this is sqlite-specific */
+ db_sqlite2_exec(NULL,E_FATAL,"insert into updated values (last_insert_rowid())");
+ }
+
+ if((!db_sql_in_scan) && (!db_sql_in_playlist_scan))
+ db_sql_update_playlists(NULL);
+
+ DPRINTF(E_SPAM,L_DB,"Exiting db_sql_add\n");
+ return DB_E_SUCCESS;
+}
+
+/**
+ * update a database item
+ *
+ * @param pmp3 mp3 file to update
+ */
+int db_sql_update(char **pe, MP3FILE *pmp3) {
+ int err;
+
+ if(!pmp3->time_modified)
+ pmp3->time_modified = (int)time(NULL);
+
+ pmp3->db_timestamp = (int)time(NULL);
+
+ err=db_sqlite2_exec(pe,E_LOG,"UPDATE songs SET "
+ "title='%q'," // title
+ "artist='%q'," // artist
+ "album='%q'," // album
+ "genre='%q'," // genre
+ "comment='%q'," // comment
+ "type='%q'," // type
+ "composer='%q'," // composer
+ "orchestra='%q'," // orchestra
+ "conductor='%q'," // conductor
+ "grouping='%q'," // grouping
+ "url='%q'," // url
+ "bitrate=%d," // bitrate
+ "samplerate=%d," // samplerate
+ "song_length=%d," // song_length
+ "file_size=%d," // file_size
+ "year=%d," // year
+ "track=%d," // track
+ "total_tracks=%d," // total_tracks
+ "disc=%d," // disc
+ "total_discs=%d," // total_discs
+ "time_modified=%d," // time_modified
+ "db_timestamp=%d," // db_timestamp
+ "bpm=%d," // bpm
+ "disabled=%d," // disabled
+ "compilation=%d," // compilation
+ "rating=%d," // rating
+ "sample_count=%d," // sample_count
+ "codectype='%q'" // codec
+ " WHERE path='%q'",
+ STR(pmp3->title),
+ STR(pmp3->artist),
+ STR(pmp3->album),
+ STR(pmp3->genre),
+ STR(pmp3->comment),
+ STR(pmp3->type),
+ STR(pmp3->composer),
+ STR(pmp3->orchestra),
+ STR(pmp3->conductor),
+ STR(pmp3->grouping),
+ STR(pmp3->url),
+ pmp3->bitrate,
+ pmp3->samplerate,
+ pmp3->song_length,
+ pmp3->file_size,
+ pmp3->year,
+ pmp3->track,
+ pmp3->total_tracks,
+ pmp3->disc,
+ pmp3->total_discs,
+ pmp3->time_modified,
+ pmp3->db_timestamp,
+ pmp3->bpm,
+ pmp3->disabled,
+ pmp3->compilation,
+ pmp3->rating,
+ pmp3->sample_count,
+ STR(pmp3->codectype),
+ pmp3->path);
+
+
+ if(err != DB_E_SUCCESS)
+ DPRINTF(E_FATAL,L_DB,"Error updating file: %s\n",pmp3->fname);
+
+ if((db_sql_in_scan) && (!db_sql_reload)) {
+ db_sqlite2_exec(NULL,E_FATAL,"INSERT INTO updated (id) select id from songs where path='%q'",
+ pmp3->path);
+ }
+
+ if((!db_sql_in_scan) && (!db_sql_in_playlist_scan))
+ db_sql_update_playlists(NULL);
+
+ return 0;
+}
+
+
+/**
+ * Update the playlist item counts
+ */
+int db_sql_update_playlists(char **pe) {
+ typedef struct tag_plinfo {
+ char *plid;
+ char *type;
+ char *clause;
+ } PLINFO;
+
+ PLINFO *pinfo;
+ int playlists;
+ int err;
+ int index;
+ SQL_ROW row;
+ char *where_clause;
+
+ /* FIXME: There is a race here for externally added playlists */
+
+ err = db_sql_fetch_int(pe,&playlists,"select count(*) from playlists");
+
+ if(err != DB_E_SUCCESS) {
+ return err;
+ }
+
+ pinfo = (PLINFO*)malloc(sizeof(PLINFO) * playlists);
+ if(!pinfo) {
+ DPRINTF(E_FATAL,L_DB,"Malloc error\n");
+ }
+
+ /* now, let's walk through the table */
+ err = db_sqlite2_enum_begin(pe,"select * from playlistitems");
+ if(err != DB_E_SUCCESS)
+ return err;
+
+ /* otherwise, walk the table */
+ index=0;
+ while((db_sqlite2_enum_fetch(pe, &row) == DB_E_SUCCESS) && (row) &&
+ (index < playlists))
+ {
+ /* process row */
+ pinfo[index].plid=strdup(STR(row[0]));
+ pinfo[index].type=strdup(STR(row[1]));
+ pinfo[index].clause=strdup(STR(row[2]));
+ index++;
+ }
+ db_sqlite2_enum_end(pe);
+ if(index != playlists) {
+ DPRINTF(E_FATAL,L_DB,"Playlist count mismatch -- transaction problem?\n");
+ }
+
+ /* Now, update the playlists */
+ for(index=0;index < playlists; index++) {
+ if(atoi(pinfo[index].type) == 1) {
+ /* smart */
+ where_clause = db_sql_parse_smart(pinfo[index].clause);
+ db_sqlite2_exec(NULL,E_FATAL,"update playlists set items=("
+ "select count(*) from songs where %s) "
+ "where id=%s",where_clause,pinfo[index].plid);
+ } else {
+ db_sqlite2_exec(NULL,E_FATAL,"update playlists set items=("
+ "select count(*) from playlistitems where "
+ "playlistid=%s) where id=%s",
+ pinfo[index].plid, pinfo[index].plid);
+ }
+
+ if(pinfo[index].plid) free(pinfo[index].plid);
+ if(pinfo[index].type) free(pinfo[index].type);
+ if(pinfo[index].clause) free(pinfo[index].clause);
+ }
+
+ free(pinfo);
+ return DB_E_SUCCESS;
+}
+
+
+/**
+ * start enum based on the DBQUERYINFO struct passed
+ *
+ * @param pinfo DBQUERYINFO struct detailing what to enum
+ */
+int db_sql_enum_start(char **pe, DBQUERYINFO *pinfo) {
+ char scratch[4096];
+ char query[4096];
+ char query_select[255];
+ char query_count[255];
+ char query_rest[4096];
+ char *where_clause;
+
+ int is_smart;
+ int have_clause=0;
+ int err;
+ int browse=0;
+ int results=0;
+ SQL_ROW temprow;
+
+ query[0] = '\0';
+ query_select[0] = '\0';
+ query_count[0] = '\0';
+ query_rest[0] = '\0';
+
+ switch(pinfo->query_type) {
+ case queryTypeItems:
+ strcpy(query_select,"SELECT * FROM songs ");
+ strcpy(query_count,"SELECT COUNT(*) FROM songs ");
+ break;
+
+ case queryTypePlaylists:
+ strcpy(query_select,"SELECT * FROM playlists ");
+ strcpy(query_count,"SELECT COUNT (*) FROM playlists ");
+ break;
+
+ case queryTypePlaylistItems: /* Figure out if it's smart or dull */
+ err = db_sqlite2_enum_begin(pe, "select type,query from playlists "
+ "where id=%d",pinfo->playlist_id);
+
+ if(err != DB_E_SUCCESS)
+ return err;
+
+ err = db_sqlite2_enum_fetch(pe,&temprow);
+
+ if(err != DB_E_SUCCESS) {
+ db_sqlite2_enum_end(NULL);
+ return err;
+ }
+
+ if(!temprow) { /* bad playlist */
+ db_get_error(pe,DB_E_INVALID_PLAYLIST);
+ db_sqlite2_enum_end(NULL);
+ return DB_E_INVALID_PLAYLIST;
+ }
+
+ is_smart=(atoi(temprow[0]) == 1);
+ have_clause=1;
+ if(is_smart) {
+ where_clause=db_sql_parse_smart(temprow[1]);
+ if(!where_clause) {
+ db_sqlite2_enum_end(NULL);
+ db_get_error(pe,DB_E_PARSE);
+ return DB_E_PARSE;
+ }
+ sprintf(query_select,"SELECT * FROM songs ");
+ sprintf(query_count,"SELECT COUNT(id) FROM songs ");
+ sprintf(query_rest,"WHERE (%s)",where_clause);
+ free(where_clause);
+ } else {
+ sprintf(query_count,"SELECT COUNT(id) FROM songs ");
+
+ /* We need to fix playlist queries to stop
+ * pulling the whole song db... the performance
+ * of these playlist queries sucks.
+ */
+#if 1
+ sprintf(query_select,"select * from songs ");
+ sprintf(query_rest,"where songs.id in (select songid from "
+ "playlistitems where playlistid=%d)",
+ pinfo->playlist_id);
+#else
+ sprintf(query_select,"select * from songs,playlistitems ");
+ sprintf(query_rest,"where (songs.id=playlistitems.songid and "
+ "playlistitems.playlistid=%d) order by "
+ "playlistitems.id",pinfo->playlist_id);
+#endif
+ }
+
+ db_sqlite2_enum_end(NULL);
+ break;
+
+ /* Note that sqlite doesn't support COUNT(DISTINCT x) */
+ case queryTypeBrowseAlbums:
+ strcpy(query_select,"select distinct album from songs ");
+ strcpy(query_count,"select count(album) from (select "
+ "distinct album from songs ");
+ browse=1;
+ break;
+
+ case queryTypeBrowseArtists:
+ strcpy(query_select,"select distinct artist from songs ");
+ strcpy(query_count,"select count(artist) from (select "
+ "distinct artist from songs ");
+ browse=1;
+ break;
+
+ case queryTypeBrowseGenres:
+ strcpy(query_select,"select distinct genre from songs ");
+ strcpy(query_count,"select count(genre) from (select "
+ "distinct genre from songs ");
+ browse=1;
+ break;
+
+ case queryTypeBrowseComposers:
+ strcpy(query_select,"select distinct composer from songs ");
+ strcpy(query_count,"select count(composer) from (select "
+ "distinct composer from songs ");
+ browse=1;
+ break;
+ default:
+ DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type\n");
+ return -1;
+ }
+
+ /* Apply the query/filter */
+ if(pinfo->whereclause) {
+ if(have_clause)
+ strcat(query_rest," AND ");
+ else
+ strcpy(query_rest," WHERE ");
+
+ strcat(query_rest,"(");
+ strcat(query_rest,pinfo->whereclause);
+ strcat(query_rest,")");
+ }
+
+
+ if(pinfo->index_type == indexTypeLast) {
+ /* We don't really care how many items unless we are
+ * doing a "last n items" query */
+ strcpy(scratch,query_count);
+ strcat(scratch,query_rest);
+ if(browse)
+ strcat(scratch,")");
+
+
+ err = db_sql_fetch_int(pe,&results,scratch);
+ if(err != DB_E_SUCCESS)
+ return err;
+
+ DPRINTF(E_DBG,L_DB,"Number of results: %d\n",results);
+ }
+
+ strcpy(query,query_select);
+ strcat(query,query_rest);
+
+ /* FIXME: sqlite specific */
+ /* Apply any index */
+ switch(pinfo->index_type) {
+ case indexTypeFirst:
+ sprintf(scratch," LIMIT %d",pinfo->index_high);
+ break;
+ case indexTypeLast:
+ if(pinfo->index_low >= results) {
+ sprintf(scratch," LIMIT %d",pinfo->index_low); /* unnecessary */
+ } else {
+ sprintf(scratch," LIMIT %d OFFSET %d",pinfo->index_low, results-pinfo->index_low);
+ }
+ break;
+ case indexTypeSub:
+ sprintf(scratch," LIMIT %d OFFSET %d",pinfo->index_high - pinfo->index_low,
+ pinfo->index_low);
+ break;
+ case indexTypeNone:
+ break;
+ default:
+ DPRINTF(E_LOG,L_DB,"Bad indexType: %d\n",(int)pinfo->index_type);
+ scratch[0]='\0';
+ break;
+ }
+
+ if(pinfo->index_type != indexTypeNone)
+ strcat(query,scratch);
+
+ /* start fetching... */
+ err=db_sqlite2_enum_begin(pe,query);
+ return err;
+}
+
+/**
+ * find the size of the response by walking through the query and
+ * sizing it
+ *
+ * @returns DB_E_SUCCESS on success, error code otherwise
+ */
+int db_sql_enum_size(char **pe, DBQUERYINFO *pinfo, int *count, int *total_size) {
+ int err;
+ int record_size;
+ SQL_ROW row;
+
+ DPRINTF(E_DBG,L_DB,"Enumerating size\n");
+
+ *count=0;
+ *total_size = 0;
+
+ while(((err=db_sqlite2_enum_fetch(pe,&row)) == DB_E_SUCCESS) && (row)) {
+ if((record_size = db_sql_get_size(pinfo,row))) {
+ *total_size += record_size;
+ *count = *count + 1;
+ }
+ }
+
+ if(err != DB_E_SUCCESS) {
+ db_sqlite2_enum_end(NULL);
+ return err;
+ }
+
+ err=db_sqlite2_enum_restart(pe);
+
+ DPRINTF(E_DBG,L_DB,"Got size: %d\n",*total_size);
+ return err;
+}
+
+
+/**
+ * fetch the next record from the enum
+ */
+int db_sql_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size, unsigned char **pdmap) {
+ int err;
+ int result_size=0;
+ unsigned char *presult;
+ SQL_ROW row;
+
+ err=db_sqlite2_enum_fetch(pe, &row);
+ if(err != DB_E_SUCCESS) {
+ db_sqlite2_enum_end(NULL);
+ return err;
+ }
+
+ if(row) {
+ result_size = db_sql_get_size(pinfo,row);
+ if(result_size) {
+ presult = (unsigned char*)malloc(result_size);
+ if(!presult) {
+ DPRINTF(E_FATAL,L_DB,"Malloc error\n");
+ }
+
+ db_sql_build_dmap(pinfo,row,presult,result_size);
+ *pdmap=presult;
+ *size = result_size;
+ }
+ } else {
+ *size = 0;
+ }
+
+ return DB_E_SUCCESS;
+}
+
+/**
+ * start the enum again
+ */
+int db_sql_enum_reset(char **pe, DBQUERYINFO *pinfo) {
+ return db_sqlite2_enum_restart(pe);
+}
+
+
+/**
+ * stop the enum
+ */
+int db_sql_enum_end(char **pe) {
+ return db_sqlite2_enum_end(pe);
+}
+
+/**
+ * get the size of the generated dmap, given a specific meta
+ */
+int db_sql_get_size(DBQUERYINFO *pinfo, SQL_ROW valarray) {
+ int size;
+ int transcode;
+
+ switch(pinfo->query_type) {
+ case queryTypeBrowseArtists: /* simple 'mlit' entry */
+ case queryTypeBrowseAlbums:
+ case queryTypeBrowseGenres:
+ case queryTypeBrowseComposers:
+ return valarray[0] ? (8 + strlen(valarray[0])) : 0;
+ case queryTypePlaylists:
+ size = 8; /* mlit */
+ size += 12; /* mimc - you get it whether you want it or not */
+ if(db_wantsmeta(pinfo->meta, metaItemId))
+ size += 12; /* miid */
+ if(db_wantsmeta(pinfo->meta, metaItunesSmartPlaylist)) {
+ if(valarray[plType] && (atoi(valarray[plType])==1))
+ size += 9; /* aeSP */
+ }
+ if(db_wantsmeta(pinfo->meta, metaItemName))
+ size += (8 + strlen(valarray[plTitle])); /* minm */
+ if(valarray[plType] && (atoi(valarray[plType])==1) &&
+ db_wantsmeta(pinfo->meta, metaMPlaylistSpec))
+ size += (8 + strlen(valarray[plQuery])); /* MSPS */
+ if(db_wantsmeta(pinfo->meta, metaMPlaylistType))
+ size += 9; /* MPTY */
+ return size;
+ break;
+ case queryTypeItems:
+ case queryTypePlaylistItems: /* essentially the same query */
+ /* see if this is going to be transcoded */
+ transcode = server_side_convert(valarray[37]);
+
+ /* Items that get changed by transcode:
+ *
+ * type: item 8: changes to 'wav'
+ * description: item 29: changes to 'wav audio file'
+ * bitrate: item 15: guestimated, based on item 15, samplerate
+ *
+ * probably file size should change as well, but currently doesn't
+ */
+
+ size = 8; /* mlit */
+ if(db_wantsmeta(pinfo->meta, metaItemKind))
+ /* mikd */
+ size += 9;
+ if(db_wantsmeta(pinfo->meta, metaSongDataKind))
+ /* asdk */
+ size += 9;
+ if(ISSTR(valarray[13]) && db_wantsmeta(pinfo->meta, metaSongDataURL))
+ /* asul */
+ size += (8 + strlen(valarray[13]));
+ if(ISSTR(valarray[5]) && db_wantsmeta(pinfo->meta, metaSongAlbum))
+ /* asal */
+ size += (8 + strlen(valarray[5]));
+ if(ISSTR(valarray[4]) && db_wantsmeta(pinfo->meta, metaSongArtist))
+ /* asar */
+ size += (8 + strlen(valarray[4]));
+ if(valarray[23] && atoi(valarray[23]) && db_wantsmeta(pinfo->meta, metaSongBPM))
+ /* asbt */
+ size += 10;
+ if(db_wantsmeta(pinfo->meta, metaSongBitRate)) {
+ /* asbr */
+ if(transcode) {
+ if(valarray[15] && atoi(valarray[15]))
+ size += 10;
+ } else {
+ if(valarray[14] && atoi(valarray[14]))
+ size += 10;
+ }
+ }
+ if(ISSTR(valarray[7]) && db_wantsmeta(pinfo->meta, metaSongComment))
+ /* ascm */
+ size += (8 + strlen(valarray[7]));
+ if(valarray[24] && atoi(valarray[24]) && db_wantsmeta(pinfo->meta,metaSongCompilation))
+ /* asco */
+ size += 9;
+ if(ISSTR(valarray[9]) && db_wantsmeta(pinfo->meta, metaSongComposer))
+ /* ascp */
+ size += (8 + strlen(valarray[9]));
+ if(ISSTR(valarray[12]) && db_wantsmeta(pinfo->meta, metaSongGrouping))
+ /* agrp */
+ size += (8 + strlen(valarray[12]));
+ if(valarray[30] && atoi(valarray[30]) && db_wantsmeta(pinfo->meta, metaSongDateAdded))
+ /* asda */
+ size += 12;
+ if(valarray[31] && atoi(valarray[31]) && db_wantsmeta(pinfo->meta,metaSongDateModified))
+ /* asdm */
+ size += 12;
+ if(valarray[22] && atoi(valarray[22]) && db_wantsmeta(pinfo->meta, metaSongDiscCount))
+ /* asdc */
+ size += 10;
+ if(valarray[21] && atoi(valarray[21]) && db_wantsmeta(pinfo->meta, metaSongDiscNumber))
+ size += 10;
+ /* asdn */
+ if(ISSTR(valarray[6]) && db_wantsmeta(pinfo->meta, metaSongGenre))
+ /* asgn */
+ size += (8 + strlen(valarray[6]));
+ if(db_wantsmeta(pinfo->meta,metaItemId))
+ /* miid */
+ size += 12;
+ if(ISSTR(valarray[8]) && db_wantsmeta(pinfo->meta,metaSongFormat)) {
+ /* asfm */
+ if(transcode) {
+ size += 11; /* 'wav' */
+ } else {
+ size += (8 + strlen(valarray[8]));
+ }
+ }
+ if(ISSTR(valarray[29]) && db_wantsmeta(pinfo->meta,metaSongDescription)) {
+ /* asdt */
+ if(transcode) {
+ size += 22; /* 'wav audio file' */
+ } else {
+ size += (8 + strlen(valarray[29]));
+ }
+ }
+ if(ISSTR(valarray[3]) && db_wantsmeta(pinfo->meta,metaItemName))
+ /* minm */
+ size += (8 + strlen(valarray[3]));
+ if(valarray[34] && atoi(valarray[34]) && db_wantsmeta(pinfo->meta,metaSongDisabled))
+ /* asdb */
+ size += 9;
+ if(valarray[15] && atoi(valarray[15]) && db_wantsmeta(pinfo->meta,metaSongSampleRate))
+ /* assr */
+ size += 12;
+ if(valarray[17] && atoi(valarray[17]) && db_wantsmeta(pinfo->meta,metaSongSize))
+ /* assz */
+ size += 12;
+
+ /* In the old daap code, we always returned 0 for asst and assp
+ * (song start time, song stop time). I don't know if this
+ * is required, so I'm going to disabled it
+ */
+
+ if(valarray[16] && atoi(valarray[16]) && db_wantsmeta(pinfo->meta, metaSongTime))
+ /* astm */
+ size += 12;
+ if(valarray[20] && atoi(valarray[20]) && db_wantsmeta(pinfo->meta, metaSongTrackCount))
+ /* astc */
+ size += 10;
+ if(valarray[19] && atoi(valarray[19]) && db_wantsmeta(pinfo->meta, metaSongTrackNumber))
+ /* astn */
+ size += 10;
+ if(valarray[25] && atoi(valarray[25]) && db_wantsmeta(pinfo->meta, metaSongUserRating))
+ /* asur */
+ size += 9;
+ if(valarray[18] && atoi(valarray[18]) && db_wantsmeta(pinfo->meta, metaSongYear))
+ /* asyr */
+ size += 10;
+ if(db_wantsmeta(pinfo->meta, metaContainerItemId))
+ /* mcti */
+ size += 12;
+ if(ISSTR(valarray[37]) && db_wantsmeta(pinfo->meta, metaSongCodecType))
+ /* ascd */
+ size += 12;
+ return size;
+ break;
+
+ default:
+ DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type: %d\n",(int)pinfo->query_type);
+ return 0;
+ }
+ return 0;
+}
+
+int db_sql_build_dmap(DBQUERYINFO *pinfo, char **valarray, unsigned char *presult, int len) {
+ unsigned char *current = presult;
+ int transcode;
+ int samplerate=0;
+
+ switch(pinfo->query_type) {
+ case queryTypeBrowseArtists: /* simple 'mlit' entry */
+ case queryTypeBrowseAlbums:
+ case queryTypeBrowseGenres:
+ case queryTypeBrowseComposers:
+ return db_dmap_add_string(current,"mlit",valarray[0]);
+ case queryTypePlaylists:
+ /* do I want to include the mlit? */
+ current += db_dmap_add_container(current,"mlit",len - 8);
+ if(db_wantsmeta(pinfo->meta,metaItemId))
+ current += db_dmap_add_int(current,"miid",atoi(valarray[plID]));
+ current += db_dmap_add_int(current,"mimc",atoi(valarray[plItems]));
+ if(db_wantsmeta(pinfo->meta,metaItunesSmartPlaylist)) {
+ if(valarray[plType] && (atoi(valarray[plType]) == 1))
+ current += db_dmap_add_char(current,"aeSP",1);
+ }
+ if(db_wantsmeta(pinfo->meta,metaItemName))
+ current += db_dmap_add_string(current,"minm",valarray[plTitle]);
+ if((valarray[plType]) && (atoi(valarray[plType])==1) &&
+ db_wantsmeta(pinfo->meta, metaMPlaylistSpec))
+ current += db_dmap_add_string(current,"MSPS",valarray[plQuery]);
+ if(db_wantsmeta(pinfo->meta, metaMPlaylistType))
+ current += db_dmap_add_char(current,"MPTY",atoi(valarray[plType]));
+ break;
+ case queryTypeItems:
+ case queryTypePlaylistItems: /* essentially the same query */
+ /* see if this is going to be transcoded */
+ transcode = server_side_convert(valarray[37]);
+
+ /* Items that get changed by transcode:
+ *
+ * type: item 8: changes to 'wav'
+ * description: item 29: changes to 'wav audio file'
+ * bitrate: item 15: guestimated, but doesn't change file size
+ *
+ * probably file size should change as well, but currently doesn't
+ */
+
+ current += db_dmap_add_container(current,"mlit",len-8);
+ if(db_wantsmeta(pinfo->meta, metaItemKind))
+ current += db_dmap_add_char(current,"mikd",(char)atoi(valarray[28]));
+ if(db_wantsmeta(pinfo->meta, metaSongDataKind))
+ current += db_dmap_add_char(current,"asdk",(char)atoi(valarray[27]));
+ if(ISSTR(valarray[13]) && db_wantsmeta(pinfo->meta, metaSongDataURL))
+ current += db_dmap_add_string(current,"asul",valarray[13]);
+ if(ISSTR(valarray[5]) && db_wantsmeta(pinfo->meta, metaSongAlbum))
+ current += db_dmap_add_string(current,"asal",valarray[5]);
+ if(ISSTR(valarray[4]) && db_wantsmeta(pinfo->meta, metaSongArtist))
+ current += db_dmap_add_string(current,"asar",valarray[4]);
+ if(valarray[23] && atoi(valarray[23]) && db_wantsmeta(pinfo->meta, metaSongBPM))
+ current += db_dmap_add_short(current,"asbt",(short)atoi(valarray[23]));
+ if(valarray[14] && atoi(valarray[14]) && db_wantsmeta(pinfo->meta, metaSongBitRate)) {
+ if(transcode) {
+ if(valarray[15]) samplerate=atoi(valarray[15]);
+ if(samplerate) {
+ current += db_dmap_add_short(current,"asbr",
+ (short)(samplerate * 4 * 8)/1000);
+ }
+ } else {
+ current += db_dmap_add_short(current,"asbr",(short)atoi(valarray[14]));
+ }
+ }
+ if(ISSTR(valarray[7]) && db_wantsmeta(pinfo->meta, metaSongComment))
+ current += db_dmap_add_string(current,"ascm",valarray[7]);
+ if(valarray[24] && atoi(valarray[24]) && db_wantsmeta(pinfo->meta,metaSongCompilation))
+ current += db_dmap_add_char(current,"asco",(char)atoi(valarray[24]));
+ if(ISSTR(valarray[9]) && db_wantsmeta(pinfo->meta, metaSongComposer))
+ current += db_dmap_add_string(current,"ascp",valarray[9]);
+ if(ISSTR(valarray[12]) && db_wantsmeta(pinfo->meta, metaSongGrouping))
+ current += db_dmap_add_string(current,"agrp",valarray[12]);
+ if(valarray[30] && atoi(valarray[30]) && db_wantsmeta(pinfo->meta, metaSongDateAdded))
+ current += db_dmap_add_int(current,"asda",(int)atoi(valarray[30]));
+ if(valarray[31] && atoi(valarray[31]) && db_wantsmeta(pinfo->meta,metaSongDateModified))
+ current += db_dmap_add_int(current,"asdm",(int)atoi(valarray[31]));
+ if(valarray[22] && atoi(valarray[22]) && db_wantsmeta(pinfo->meta, metaSongDiscCount))
+ current += db_dmap_add_short(current,"asdc",(short)atoi(valarray[22]));
+ if(valarray[21] && atoi(valarray[21]) && db_wantsmeta(pinfo->meta, metaSongDiscNumber))
+ current += db_dmap_add_short(current,"asdn",(short)atoi(valarray[21]));
+ if(ISSTR(valarray[6]) && db_wantsmeta(pinfo->meta, metaSongGenre))
+ current += db_dmap_add_string(current,"asgn",valarray[6]);
+ if(db_wantsmeta(pinfo->meta,metaItemId))
+ current += db_dmap_add_int(current,"miid",(int)atoi(valarray[0]));
+ if(ISSTR(valarray[8]) && db_wantsmeta(pinfo->meta,metaSongFormat)) {
+ if(transcode) {
+ current += db_dmap_add_string(current,"asfm","wav");
+ } else {
+ current += db_dmap_add_string(current,"asfm",valarray[8]);
+ }
+ }
+ if(ISSTR(valarray[29]) && db_wantsmeta(pinfo->meta,metaSongDescription)) {
+ if(transcode) {
+ current += db_dmap_add_string(current,"asdt","wav audio file");
+ } else {
+ current += db_dmap_add_string(current,"asdt",valarray[29]);
+ }
+ }
+ if(ISSTR(valarray[3]) && db_wantsmeta(pinfo->meta,metaItemName))
+ current += db_dmap_add_string(current,"minm",valarray[3]);
+ if(valarray[34] && atoi(valarray[34]) && db_wantsmeta(pinfo->meta,metaSongDisabled))
+ current += db_dmap_add_char(current,"asdb",(char)atoi(valarray[34]));
+ if(valarray[15] && atoi(valarray[15]) && db_wantsmeta(pinfo->meta,metaSongSampleRate))
+ current += db_dmap_add_int(current,"assr",atoi(valarray[15]));
+ if(valarray[17] && atoi(valarray[17]) && db_wantsmeta(pinfo->meta,metaSongSize))
+ current += db_dmap_add_int(current,"assz",atoi(valarray[17]));
+ if(valarray[16] && atoi(valarray[16]) && db_wantsmeta(pinfo->meta, metaSongTime))
+ current += db_dmap_add_int(current,"astm",atoi(valarray[16]));
+ if(valarray[20] && atoi(valarray[20]) && db_wantsmeta(pinfo->meta, metaSongTrackCount))
+ current += db_dmap_add_short(current,"astc",(short)atoi(valarray[20]));
+ if(valarray[19] && atoi(valarray[19]) && db_wantsmeta(pinfo->meta, metaSongTrackNumber))
+ current += db_dmap_add_short(current,"astn",(short)atoi(valarray[19]));
+ if(valarray[25] && atoi(valarray[25]) && db_wantsmeta(pinfo->meta, metaSongUserRating))
+ current += db_dmap_add_char(current,"asur",(char)atoi(valarray[25]));
+ if(valarray[18] && atoi(valarray[18]) && db_wantsmeta(pinfo->meta, metaSongYear))
+ current += db_dmap_add_short(current,"asyr",(short)atoi(valarray[18]));
+ if(ISSTR(valarray[37]) && db_wantsmeta(pinfo->meta, metaSongCodecType))
+ current += db_dmap_add_literal(current,"ascd",valarray[37],4);
+ if(db_wantsmeta(pinfo->meta, metaContainerItemId))
+ current += db_dmap_add_int(current,"mcti",atoi(valarray[0]));
+ return 0;
+ break;
+
+ default:
+ DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type: %d\n",(int)pinfo->query_type);
+ return 0;
+ }
+ return 0;
+}
+
+int db_sql_atoi(const char *what) {
+ return what ? atoi(what) : 0;
+}
+char *db_sql_strdup(const char *what) {
+ return what ? (strlen(what) ? strdup(what) : NULL) : NULL;
+}
+
+void db_sql_build_m3ufile(SQL_ROW valarray, M3UFILE *pm3u) {
+ memset(pm3u,0x00,sizeof(M3UFILE));
+
+ pm3u->id=db_sql_atoi(valarray[0]);
+ pm3u->title=db_sql_strdup(valarray[1]);
+ pm3u->type=db_sql_atoi(valarray[2]);
+ pm3u->items=db_sql_atoi(valarray[3]);
+ pm3u->query=db_sql_strdup(valarray[4]);
+ pm3u->db_timestamp=db_sql_atoi(valarray[5]);
+ pm3u->path=db_sql_strdup(valarray[6]);
+ pm3u->index=db_sql_atoi(valarray[7]);
+ return;
+}
+
+void db_sql_build_mp3file(SQL_ROW valarray, MP3FILE *pmp3) {
+ memset(pmp3,0x00,sizeof(MP3FILE));
+ pmp3->id=db_sql_atoi(valarray[0]);
+ pmp3->path=db_sql_strdup(valarray[1]);
+ pmp3->fname=db_sql_strdup(valarray[2]);
+ pmp3->title=db_sql_strdup(valarray[3]);
+ pmp3->artist=db_sql_strdup(valarray[4]);
+ pmp3->album=db_sql_strdup(valarray[5]);
+ pmp3->genre=db_sql_strdup(valarray[6]);
+ pmp3->comment=db_sql_strdup(valarray[7]);
+ pmp3->type=db_sql_strdup(valarray[8]);
+ pmp3->composer=db_sql_strdup(valarray[9]);
+ pmp3->orchestra=db_sql_strdup(valarray[10]);
+ pmp3->conductor=db_sql_strdup(valarray[11]);
+ pmp3->grouping=db_sql_strdup(valarray[12]);
+ pmp3->url=db_sql_strdup(valarray[13]);
+ pmp3->bitrate=db_sql_atoi(valarray[14]);
+ pmp3->samplerate=db_sql_atoi(valarray[15]);
+ pmp3->song_length=db_sql_atoi(valarray[16]);
+ pmp3->file_size=db_sql_atoi(valarray[17]);
+ pmp3->year=db_sql_atoi(valarray[18]);
+ pmp3->track=db_sql_atoi(valarray[19]);
+ pmp3->total_tracks=db_sql_atoi(valarray[20]);
+ pmp3->disc=db_sql_atoi(valarray[21]);
+ pmp3->total_discs=db_sql_atoi(valarray[22]);
+ pmp3->bpm=db_sql_atoi(valarray[23]);
+ pmp3->compilation=db_sql_atoi(valarray[24]);
+ pmp3->rating=db_sql_atoi(valarray[25]);
+ pmp3->play_count=db_sql_atoi(valarray[26]);
+ pmp3->data_kind=db_sql_atoi(valarray[27]);
+ pmp3->item_kind=db_sql_atoi(valarray[28]);
+ pmp3->description=db_sql_strdup(valarray[29]);
+ pmp3->time_added=db_sql_atoi(valarray[30]);
+ pmp3->time_modified=db_sql_atoi(valarray[31]);
+ pmp3->time_played=db_sql_atoi(valarray[32]);
+ pmp3->db_timestamp=db_sql_atoi(valarray[33]);
+ pmp3->disabled=db_sql_atoi(valarray[34]);
+ pmp3->sample_count=db_sql_atoi(valarray[35]);
+ pmp3->force_update=db_sql_atoi(valarray[36]);
+ pmp3->codectype=db_sql_strdup(valarray[37]);
+ pmp3->index=db_sql_atoi(valarray[38]);
+}
+
+/**
+ * fetch a playlist by path and index
+ *
+ * @param path path to fetch
+ */
+M3UFILE *db_sql_fetch_playlist(char **pe, char *path, int index) {
+ int result;
+ M3UFILE *pm3u=NULL;
+ SQL_ROW row;
+
+ result = db_sqlite2_enum_begin(pe,"select * from playlists where "
+ "path='%q' and idx=%d",path,index);
+
+ if(result != DB_E_SUCCESS)
+ return NULL;
+
+ result = db_sqlite2_enum_fetch(pe, &row);
+ if(result != DB_E_SUCCESS) {
+ db_sqlite2_enum_end(NULL);
+ return NULL;
+ }
+
+ if(!row) {
+ db_sqlite2_enum_end(NULL);
+ db_get_error(pe,DB_E_INVALID_PLAYLIST);
+ return NULL;
+ }
+
+ pm3u=(M3UFILE*)malloc(sizeof(M3UFILE));
+ if(!pm3u)
+ DPRINTF(E_FATAL,L_MISC,"malloc error: db_sql_fetch_playlist\n");
+
+ db_sql_build_m3ufile(row,pm3u);
+
+ if((db_sql_in_playlist_scan) && (!db_sql_reload)) {
+ db_sqlite2_exec(NULL,E_FATAL,"insert into plupdated values (%d)",
+ pm3u->id);
+ }
+
+ db_sqlite2_enum_end(NULL);
+ return pm3u;
+}
+
+
+/* FIXME: bad error handling -- not like the rest */
+
+/**
+ * fetch a MP3FILE for a specific id
+ *
+ * @param id id to fetch
+ */
+MP3FILE *db_sql_fetch_item(char **pe, int id) {
+ SQL_ROW row;
+ MP3FILE *pmp3=NULL;
+ int err;
+
+ err=db_sql_fetch_row(pe,&row,"select * from songs where id=%d",id);
+ if(err != DB_E_SUCCESS) {
+ if(err == DB_E_NOROWS) { /* Override generic error */
+ free(*pe);
+ db_get_error(pe,DB_E_INVALID_SONGID);
+ return NULL;
+ }
+ return NULL;
+ }
+
+ pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
+ if(!pmp3)
+ DPRINTF(E_FATAL,L_MISC,"Malloc error in db_sql_fetch_item\n");
+
+ db_sql_build_mp3file(row,pmp3);
+
+ db_sql_dispose_row();
+
+ if ((db_sql_in_scan) && (!db_sql_reload)) {
+ db_sqlite2_exec(pe,E_FATAL,"INSERT INTO updated VALUES (%d)",id);
+ }
+
+ return pmp3;
+}
+
+/**
+ * retrieve a MP3FILE struct for the song with a given path
+ *
+ * @param path path of the file to retreive
+ */
+MP3FILE *db_sql_fetch_path(char **pe, char *path, int index) {
+ SQL_ROW row;
+ MP3FILE *pmp3=NULL;
+ int err;
+
+ err=db_sql_fetch_row(pe,&row,"select * from songs where path='%q'",path);
+ if(err != DB_E_SUCCESS) {
+ if(err == DB_E_NOROWS) { /* Override generic error */
+ free(*pe);
+ db_get_error(pe,DB_E_INVALID_SONGID);
+ return NULL;
+ }
+ return NULL;
+ }
+
+ pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
+ if(!pmp3)
+ DPRINTF(E_FATAL,L_MISC,"Malloc error in db_sql_fetch_path\n");
+
+ db_sql_build_mp3file(row,pmp3);
+
+ db_sql_dispose_row();
+
+ if ((db_sql_in_scan) && (!db_sql_reload)) {
+ db_sqlite2_exec(pe,E_FATAL,"INSERT INTO updated VALUES (%d)",pmp3->id);
+ }
+
+ return pmp3;
+}
+
+/**
+ * dispose of a MP3FILE struct that was obtained
+ * from db_sql_fetch_item
+ *
+ * @param pmp3 item obtained from db_sql_fetch_item
+ */
+void db_sql_dispose_item(MP3FILE *pmp3) {
+ if(!pmp3)
+ return;
+
+ MAYBEFREE(pmp3->path);
+ MAYBEFREE(pmp3->fname);
+ MAYBEFREE(pmp3->title);
+ MAYBEFREE(pmp3->artist);
+ MAYBEFREE(pmp3->album);
+ MAYBEFREE(pmp3->genre);
+ MAYBEFREE(pmp3->comment);
+ MAYBEFREE(pmp3->type);
+ MAYBEFREE(pmp3->composer);
+ MAYBEFREE(pmp3->orchestra);
+ MAYBEFREE(pmp3->conductor);
+ MAYBEFREE(pmp3->grouping);
+ MAYBEFREE(pmp3->description);
+ MAYBEFREE(pmp3->url);
+ MAYBEFREE(pmp3->codectype);
+ free(pmp3);
+}
+
+void db_sql_dispose_playlist(M3UFILE *pm3u) {
+ if(!pm3u)
+ return;
+
+ MAYBEFREE(pm3u->title);
+ MAYBEFREE(pm3u->query);
+ MAYBEFREE(pm3u->path);
+ free(pm3u);
+}
+
+/**
+ * count either the number of playlists, or the number of
+ * songs
+ *
+ * @param type either countPlaylists or countSongs (type to count)
+ */
+int db_sql_get_count(char **pe, int *count, CountType_t type) {
+ char *table;
+ int err;
+
+ switch(type) {
+ case countPlaylists:
+ table="playlists";
+ break;
+
+ case countSongs:
+ default:
+ table="songs";
+ break;
+ }
+
+ err=db_sql_fetch_int(pe,count,"SELECT COUNT(*) FROM '%q'", table);
+ return err;
+}
+
diff --git a/src/db-sql.h b/src/db-sql.h
new file mode 100644
index 00000000..53d9f7c6
--- /dev/null
+++ b/src/db-sql.h
@@ -0,0 +1,114 @@
+/*
+ * $Id$
+ * sql-specific db implementation
+ *
+ * Copyright (C) 2005 Ron Pedde (ron@pedde.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DB_SQL_H_
+#define _DB_SQL_H_
+
+typedef char** SQL_ROW;
+
+extern int db_sql_open(char **pe, char *parameters);
+extern int db_sql_init(int reload);
+extern int db_sql_deinit(void);
+extern int db_sql_add(char **pe, MP3FILE *pmp3);
+extern int db_sql_enum_start(char **pe, DBQUERYINFO *pinfo);
+extern int db_sql_enum_size(char **pe, DBQUERYINFO *pinfo, int *count, int *total_size);
+extern int db_sql_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size, unsigned char **pdmap);
+extern int db_sql_enum_reset(char **pe, DBQUERYINFO *pinfo);
+extern int db_sql_enum_end(char **pe);
+extern int db_sql_start_scan(void);
+extern int db_sql_end_song_scan(void);
+extern int db_sql_end_scan(void);
+extern int db_sql_get_count(char **pe, int *count, CountType_t type);
+extern MP3FILE *db_sql_fetch_item(char **pe, int id);
+extern MP3FILE *db_sql_fetch_path(char **pe, char *path,int index);
+extern M3UFILE *db_sql_fetch_playlist(char **pe, char *path, int index);
+extern void db_sql_dispose_item(MP3FILE *pmp3);
+extern void db_sql_dispose_playlist(M3UFILE *pm3u);
+extern int db_sql_add_playlist(char **pe, char *name, int type, char *clause, char *path, int index, int *playlistid);
+extern int db_sql_add_playlist_item(char **pe, int playlistid, int songid);
+extern int db_sql_edit_playlist(char **pe, int id, char *name, char *clause);
+extern int db_sql_delete_playlist(char **pe, int playlistid);
+extern int db_sql_delete_playlist_item(char **pe, int playlistid, int songid);
+
+extern int db_sql_fetch_row(char **pe, SQL_ROW *row, char *fmt, ...);
+extern int db_sql_fetch_int(char **pe, int *result, char *fmt, ...);
+extern int db_sql_fetch_char(char **pe, char **result, char *fmt, ...);
+extern int db_sql_dispose_row(void);
+
+typedef enum {
+ songID,
+ songPath,
+ songFname,
+ songTitle,
+ songArtist,
+ songAlbum,
+ songGenre,
+ songComment,
+ songType,
+ songComposer,
+ songOrchestra,
+ songGrouping,
+ songURL,
+ songBitrate,
+ songSampleRate,
+ songLength,
+ songFilesize,
+ songYear,
+ songTrack,
+ songTotalTracks,
+ songDisc,
+ songTotalDiscs,
+ songBPM,
+ songCompilation,
+ songRating,
+ songPlayCount,
+ songDataKind,
+ songItemKind,
+ songDescription,
+ songTimeAdded,
+ songTimeModified,
+ songTimePlayed,
+ songDBTimestamp,
+ songDisabled,
+ songSampleCount,
+ songForceUpdate,
+ songCodecType
+} SongField_t;
+
+typedef enum {
+ plID,
+ plTitle,
+ plType,
+ plItems,
+ plQuery,
+ plDBTimestamp,
+ plPath
+} PlaylistField_t;
+
+#define DB_SQL_EVENT_STARTUP 0
+#define DB_SQL_EVENT_SONGSCANSTART 1
+#define DB_SQL_EVENT_SONGSCANEND 2
+#define DB_SQL_EVENT_PLSCANSTART 3
+#define DB_SQL_EVENT_PLSCANEND 4
+#define DB_SQL_EVENT_FULLRELOAD 5
+
+
+#endif /* _DB_SQL_H_ */
diff --git a/src/dbs-sqlite.c b/src/dbs-sqlite.c
deleted file mode 100644
index 83f60a9b..00000000
--- a/src/dbs-sqlite.c
+++ /dev/null
@@ -1,1935 +0,0 @@
-/*
- * $Id$
- * sqlite-specific db implementation
- *
- * Copyright (C) 2005 Ron Pedde (ron@pedde.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#define _XOPEN_SOURCE 500
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "err.h"
-#include "mp3-scanner.h"
-#include "db-generic.h"
-#include "dbs-sqlite.h"
-#include "restart.h"
-#include "ssc.h"
-#include "smart-parser.h"
-
-/* Globals */
-static sqlite *db_sqlite_songs; /**< Database that holds the mp3 info */
-static pthread_mutex_t db_sqlite_mutex = PTHREAD_MUTEX_INITIALIZER; /**< sqlite not reentrant */
-static sqlite_vm *db_sqlite_pvm;
-static int db_sqlite_in_scan=0;
-static int db_sqlite_reload=0;
-static int db_sqlite_in_playlist_scan=0;
-
-static char db_path[PATH_MAX + 1];
-
-/* Forwards */
-int db_sqlite_get_size(DBQUERYINFO *pinfo, char **valarray);
-int db_sqlite_build_dmap(DBQUERYINFO *pinfo, char **valarray, unsigned char *presult, int len);
-void db_sqlite_build_mp3file(char **valarray, MP3FILE *pmp3);
-int db_sqlite_exec(int fatal, char *fmt, ...);
-int db_sqlite_get_table(int fatal, char ***resarray, int *rows, int *cols, char *fmt, ...);
-int db_sqlite_free_table(char **resarray);
-int db_sqlite_get_int(int loglevel, int *result, char *fmt, ...);
-int db_sqlite_update(MP3FILE *pmp3);
-int db_sqlite_update_version(int from_version);
-int db_sqlite_get_version(void);
-int db_sqlite_update_playlists(void);
-char *db_sqlite_parse_smart(char *phrase);
-
-#define STR(a) (a) ? (a) : ""
-#define ISSTR(a) ((a) && strlen((a)))
-#define MAYBEFREE(a) { if((a)) free((a)); };
-
-/**
- * get the sql where clause for a smart playlist spec. This
- * where clause must be freed by the caller
- *
- * @param phrase playlist spec to be converted
- * @returns sql where clause if successful, NULL otherwise
- */
-char *db_sqlite_parse_smart(char *phrase) {
- PARSETREE pt;
- char *result = NULL;
-
- if(strcmp(phrase,"1") == 0)
- return strdup("1");
-
- pt=sp_init();
- if(!pt)
- return NULL;
-
- if(!sp_parse(pt,phrase)) {
- DPRINTF(E_LOG,L_DB,"Error parsing smart playlist: %s",sp_get_error(pt));
- } else {
- result = sp_sql_clause(pt);
- }
-
- sp_dispose(pt);
- return result;
-}
-
-/**
- * lock the db_mutex
- */
-void db_sqlite_lock(void) {
- int err;
-
- if((err=pthread_mutex_lock(&db_sqlite_mutex))) {
- DPRINTF(E_FATAL,L_DB,"cannot lock sqlite lock: %s\n",strerror(err));
- }
-}
-
-/**
- * unlock the db_mutex
- */
-int db_sqlite_unlock(void) {
- return pthread_mutex_unlock(&db_sqlite_mutex);
-}
-
-/**
- * exec a simple statement
- *
- * \param what level to log a failure of this sql statement to exec
- */
-int db_sqlite_exec(int loglevel, char *fmt, ...) {
- va_list ap;
- char *query;
- int err;
- char *perr;
-
- va_start(ap,fmt);
- query=sqlite_vmprintf(fmt,ap);
- va_end(ap);
-
- DPRINTF(E_DBG,L_DB,"Executing: %s\n",query);
-
- db_sqlite_lock();
- err=sqlite_exec(db_sqlite_songs,query,NULL,NULL,&perr);
- if(err != SQLITE_OK) {
- DPRINTF(loglevel == E_FATAL ? E_LOG : loglevel,L_DB,"Query: %s\n",
- query);
- DPRINTF(loglevel,L_DB,"Error: %s\n",perr);
- sqlite_freemem(perr);
- } else {
- DPRINTF(E_DBG,L_DB,"Rows: %d\n",sqlite_changes(db_sqlite_songs));
- }
- sqlite_freemem(query);
-
- db_sqlite_unlock();
- return err;
-}
-
-/**
- * get a sqlite table
- *
- */
-int db_sqlite_get_table(int loglevel, char ***resarray, int *rows, int *cols, char *fmt, ...) {
- va_list ap;
- char *query;
- int err;
- char *perr;
-
- va_start(ap,fmt);
- query=sqlite_vmprintf(fmt,ap);
- va_end(ap);
-
- DPRINTF(E_DBG,L_DB,"Executing: %s\n",query);
-
- db_sqlite_lock();
- err=sqlite_get_table(db_sqlite_songs,query,resarray,rows,cols,&perr);
- if(err == SQLITE_OK)
- sqlite_freemem(query);
- db_sqlite_unlock();
-
- if(err != SQLITE_OK) {
- DPRINTF(loglevel == E_FATAL ? E_LOG : loglevel,L_DB,"Query: %s\n",query);
- DPRINTF(loglevel,L_DB,"Error: %s\n",perr);
- db_sqlite_lock();
- sqlite_freemem(query);
- sqlite_freemem(perr);
- db_sqlite_unlock();
- return err;
- }
-
- return 0;
-}
-
-int db_sqlite_free_table(char **resarray) {
- db_sqlite_lock();
- sqlite_free_table(resarray);
- db_sqlite_unlock();
- return 0;
-}
-
-/**
- * db_sqlite_get_int
- */
-int db_sqlite_get_int(int loglevel, int *result, char *fmt, ...) {
- int rows, cols;
- char **resarray;
- va_list ap;
- char *query;
- int err;
- char *perr;
-
- va_start(ap,fmt);
- query=sqlite_vmprintf(fmt,ap);
- va_end(ap);
-
- DPRINTF(E_DBG,L_DB,"Executing: %s\n",query);
-
- db_sqlite_lock();
- err=sqlite_get_table(db_sqlite_songs,query,&resarray,&rows,&cols,&perr);
- if(err == SQLITE_OK)
- sqlite_freemem(query);
- db_sqlite_unlock();
-
- if(err != SQLITE_OK) {
- DPRINTF(loglevel == E_FATAL ? E_LOG : loglevel,L_DB,"Query: %s\n",query);
- DPRINTF(loglevel,L_DB,"Error: %s\n",perr);
- db_sqlite_lock();
- sqlite_freemem(query);
- sqlite_freemem(perr);
- db_sqlite_unlock();
- return DB_E_SQL_ERROR;
- }
-
- if(rows==0) {
- sqlite_free_table(resarray);
- return DB_E_NOROWS;
- }
-
- *result=atoi(resarray[cols]);
-
- sqlite_free_table(resarray);
- return DB_E_SUCCESS;
-}
-
-
-/**
- * open sqlite database
- */
-int db_sqlite_open(char *parameters) {
- char *perr;
-
- snprintf(db_path,sizeof(db_path),"%s/songs.db",parameters);
-
- db_sqlite_lock();
- db_sqlite_songs=sqlite_open(db_path,0666,&perr);
- if(!db_sqlite_songs) {
- DPRINTF(E_FATAL,L_DB,"db_sqlite_open: %s (%s)\n",perr,db_path);
- sqlite_freemem(perr);
- }
-
- sqlite_busy_timeout(db_sqlite_songs,30000); /* 30 seconds */
-
- db_sqlite_unlock();
- return 0;
-}
-
-/**
- * initialize the sqlite database, reloading if requested
- *
- * \param reload whether or not to do a full reload on the db
- */
-int db_sqlite_init(int reload) {
- int items;
- int rescan=0;
-
- /* make sure we have an index... might not if aborted during scan */
- db_sqlite_exec(E_DBG,"CREATE INDEX idx_path ON songs(path)");
- db_sqlite_update_version(db_sqlite_get_version());
- db_sqlite_get_int(E_DBG,&rescan,"SELECT value FROM config WHERE term='rescan'");
-
- if(rescan)
- reload=1;
-
- items=db_sqlite_get_count(countSongs);
-
- if((reload) || (!items)) {
- DPRINTF(E_LOG,L_DB,"Full reload...\n");
- /* this may or may not fail, depending if the index is already in place */
- db_sqlite_reload=1;
- db_sqlite_exec(E_DBG,"DROP INDEX idx_path");
- db_sqlite_exec(E_FATAL,"DELETE FROM songs");
- } else {
- db_sqlite_exec(E_FATAL,"VACUUM");
- }
- return 0;
-}
-
-/**
- * close the database
- */
-int db_sqlite_deinit(void) {
- db_sqlite_lock();
- sqlite_close(db_sqlite_songs);
- db_sqlite_unlock();
-
- return 0;
-}
-
-
-/**
- * start a background scan
- */
-int db_sqlite_start_scan(void) {
- DPRINTF(E_DBG,L_DB,"Starting db scan\n");
-
- if(db_sqlite_reload) {
- db_sqlite_exec(E_FATAL,"PRAGMA synchronous = OFF");
- db_sqlite_exec(E_FATAL,"BEGIN TRANSACTION");
- } else {
- /* if not a full reload, we'll be doing update checks */
- db_sqlite_exec(E_DBG,"drop table updated");
- db_sqlite_exec(E_FATAL,"create temp table updated (id int)");
- db_sqlite_exec(E_DBG,"drop table plupdated");
- db_sqlite_exec(E_FATAL,"create temp table plupdated(id int)");
- }
-
- db_sqlite_in_scan=1;
- db_sqlite_in_playlist_scan=0;
- return 0;
-}
-
-/**
- * end song scan -- start playlist scan
- */
-int db_sqlite_end_song_scan(void) {
- if(db_sqlite_reload) {
- db_sqlite_exec(E_FATAL,"commit transaction");
- db_sqlite_exec(E_FATAL,"create index idx_path on songs(path)");
- db_sqlite_exec(E_DBG,"delete from config where term='rescan'");
- } else {
- db_sqlite_exec(E_FATAL,"delete from songs where id not in (select id from updated)");
- db_sqlite_exec(E_FATAL,"update songs set force_update=0");
- db_sqlite_exec(E_FATAL,"drop table updated");
- }
-
- db_sqlite_exec(E_FATAL,"begin transaction");
-
- db_sqlite_in_scan=0;
- db_sqlite_in_playlist_scan=1;
-
- return 0;
-}
-
-/**
- * stop a db scan
- */
-int db_sqlite_end_scan(void) {
- db_sqlite_exec(E_FATAL,"end transaction");
-
- if(db_sqlite_reload) {
- db_sqlite_exec(E_FATAL,"pragma synchronous=normal");
- } else {
- db_sqlite_exec(E_FATAL,"delete from playlists where ((type=%d) OR (type=%d)) and "
- "id not in (select id from plupdated)",PL_STATICFILE,PL_STATICXML);
- db_sqlite_exec(E_FATAL,"delete from playlistitems where playlistid not in (select distinct "
- "id from playlists)");
- db_sqlite_exec(E_FATAL,"drop table plupdated");
- }
-
- db_sqlite_update_playlists();
- db_sqlite_reload=0;
- db_sqlite_in_playlist_scan=0;
-
- return 0;
-}
-
-/**
- * delete a playlist
- *
- * \param playlistid playlist to delete
- */
-int db_sqlite_delete_playlist(int playlistid) {
- int type;
- int result;
-
- result=db_sqlite_get_int(E_DBG,&type,"select type from playlists where id=%d",playlistid);
- if(result != DB_E_SUCCESS) {
- if(result == DB_E_NOROWS)
- return DB_E_INVALID_PLAYLIST;
- return result;
- }
-
- /* got a good playlist, now do what we need to do */
- db_sqlite_exec(E_FATAL,"delete from playlists where id=%d",playlistid);
- db_sqlite_exec(E_FATAL,"delete from playlistitems where playlistid=%d",playlistid);
-
- return DB_E_SUCCESS;
-}
-
-/**
- * delete a song from a playlist
- *
- * \param playlistid playlist to delete item from
- * \param songid song to delete from playlist
- */
-int db_sqlite_delete_playlist_item(int playlistid, int songid) {
- int result;
- int playlist_type;
- int count;
-
- /* first, check the playlist */
- result=db_sqlite_get_int(E_DBG,&playlist_type,
- "select type from playlists where id=%d",playlistid);
-
- if(result != DB_E_SUCCESS) {
- if(result == DB_E_NOROWS)
- return DB_E_INVALID_PLAYLIST;
- return result;
- }
-
- if(playlist_type == PL_SMART) /* can't delete from a smart playlist */
- return DB_E_INVALIDTYPE;
-
- /* make sure the songid is valid */
- result=db_sqlite_get_int(E_DBG,&count,"select count(*) from playlistitems where playlistid=%d "
- "and songid=%d",playlistid,songid);
- if(result != DB_E_SUCCESS) {
- if(result == DB_E_NOROWS)
- return DB_E_INVALID_SONGID;
- return result;
- }
-
- /* looks valid, so lets add the item */
- result=db_sqlite_exec(E_DBG,"delete from playlistitems where playlistid=%d and songid=%d",
- playlistid,songid);
- return result;
-}
-
-/**
- * edit a playlist. The only things worth changing are the name
- * and the "where" clause.
- *
- * @param id id of the playlist to alter
- * @param name new name of the playlist
- * @param where new where clause
- */
-int db_sqlite_edit_playlist(int id, char *name, char *clause) {
- int result;
-
- result = db_sqlite_exec(E_LOG,"update playlists set title='%q',query='%q' where id=%d",
- name, clause, id);
-
- return result;
-}
-
-
-
-/**
- * add a playlist
- *
- * \param name name of the playlist
- * \param type playlist type: 0 - static, 1 - smart, 2 - m3u
- * \param clause: "where" clause for smart playlist
- */
-int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid) {
- int cnt=0;
- int result=DB_E_SUCCESS;
- char *criteria;
-
- db_sqlite_get_int(E_DBG,&cnt,"select count(*) from playlists where "
- "upper(title)=upper('%q')",name);
-
- if(cnt) return DB_E_DUPLICATE_PLAYLIST;
- if((type == PL_SMART) && (!clause)) return DB_E_NOCLAUSE;
-
- /* Let's throw it in */
- switch(type) {
- case PL_STATICWEB: /* static, maintained in web interface */
- case PL_STATICFILE: /* static, from file */
- case PL_STATICXML: /* from iTunes XML file */
- result = db_sqlite_exec(E_LOG,"insert into playlists "
- "(title,type,items,query,db_timestamp,path,idx) "
- "values ('%q',%d,0,NULL,%d,'%q',%d)",name,type,time(NULL),path,index);
- break;
- case PL_SMART: /* smart */
- criteria = db_sqlite_parse_smart(clause);
- if(!criteria)
- return DB_E_PARSE;
- result = db_sqlite_exec(E_LOG,"insert into playlists "
- "(title,type,items,query,db_timestamp,idx) "
- "values ('%q',%d,%d,'%q',%d,0)",name,PL_SMART,cnt,clause,time(NULL));
- free(criteria);
- break;
- }
-
- if(result)
- return result;
-
- result = db_sqlite_get_int(E_LOG,playlistid,
- "select id from playlists where title='%q'", name);
-
- if(((type==PL_STATICFILE)||(type==PL_STATICXML))
- && (db_sqlite_in_playlist_scan) && (!db_sqlite_reload)) {
- db_sqlite_exec(E_FATAL,"insert into plupdated values (%d)",*playlistid);
- }
-
- return result;
-}
-
-/**
- * add a song to a static playlist
- *
- * \param playlistid playlist to add song to
- * \param songid song to add
- * \returns 0 on success, otherwise a DB_E error code
- */
-int db_sqlite_add_playlist_item(int playlistid, int songid) {
- int result;
- int playlist_type;
- int count;
-
- /* first, check the playlist */
- result=db_sqlite_get_int(E_DBG,&playlist_type,
- "select type from playlists where id=%d",playlistid);
-
- if(result != DB_E_SUCCESS) {
- if(result == DB_E_NOROWS)
- return DB_E_INVALID_PLAYLIST;
- return result;
- }
-
- if(playlist_type == 1) /* can't add to smart playlists, or static */
- return DB_E_INVALIDTYPE;
-
- /* make sure the songid is valid */
- result=db_sqlite_get_int(E_DBG,&count,"select count(*) from songs where id=%d",songid);
- if(result != DB_E_SUCCESS) {
- if(result == DB_E_NOROWS)
- return DB_E_INVALID_SONGID;
- return result;
- }
-
- /* looks valid, so lets add the item */
- result=db_sqlite_exec(E_DBG,"insert into playlistitems (playlistid, songid) values (%d,%d)",playlistid,songid);
- return result;
-}
-
-
-/**
- * add a database item
- *
- * \param pmp3 mp3 file to add
- */
-int db_sqlite_add(MP3FILE *pmp3) {
- int err;
-
- DPRINTF(E_SPAM,L_DB,"Entering db_sqlite_add\n");
-
- if(!pmp3->time_added)
- pmp3->time_added = (int)time(NULL);
-
- if(!pmp3->time_modified)
- pmp3->time_modified = (int)time(NULL);
-
- pmp3->db_timestamp = (int)time(NULL);
- pmp3->play_count=0;
- pmp3->time_played=0;
-
- err=db_sqlite_exec(E_DBG,"INSERT INTO songs VALUES "
- "(NULL," // id
- "'%q'," // path
- "'%q'," // fname
- "'%q'," // title
- "'%q'," // artist
- "'%q'," // album
- "'%q'," // genre
- "'%q'," // comment
- "'%q'," // type
- "'%q'," // composer
- "'%q'," // orchestra
- "'%q'," // conductor
- "'%q'," // grouping
- "'%q'," // url
- "%d," // bitrate
- "%d," // samplerate
- "%d," // song_length
- "%d," // file_size
- "%d," // year
- "%d," // track
- "%d," // total_tracks
- "%d," // disc
- "%d," // total_discs
- "%d," // bpm
- "%d," // compilation
- "%d," // rating
- "0," // play_count
- "%d," // data_kind
- "%d," // item_kind
- "'%q'," // description
- "%d," // time_added
- "%d," // time_modified
- "%d," // time_played
- "%d," // db_timestamp
- "%d," // disabled
- "%d," // sample_count
- "0," // force_update
- "'%q'," // codectype
- "%d)", // index
- STR(pmp3->path),
- STR(pmp3->fname),
- STR(pmp3->title),
- STR(pmp3->artist),
- STR(pmp3->album),
- STR(pmp3->genre),
- STR(pmp3->comment),
- STR(pmp3->type),
- STR(pmp3->composer),
- STR(pmp3->orchestra),
- STR(pmp3->conductor),
- STR(pmp3->grouping),
- STR(pmp3->url),
- pmp3->bitrate,
- pmp3->samplerate,
- pmp3->song_length,
- pmp3->file_size,
- pmp3->year,
- pmp3->track,
- pmp3->total_tracks,
- pmp3->disc,
- pmp3->total_discs,
- pmp3->bpm,
- pmp3->compilation,
- pmp3->rating,
- pmp3->data_kind,
- pmp3->item_kind,
- STR(pmp3->description),
- pmp3->time_added,
- pmp3->time_modified,
- pmp3->time_played,
- pmp3->db_timestamp,
- pmp3->disabled,
- pmp3->sample_count,
- STR(pmp3->codectype),
- pmp3->index);
-
- if(err == SQLITE_CONSTRAINT) {
- /* probably because the path already exists... */
- DPRINTF(E_DBG,L_DB,"Could not add mp3 file: %s... updating instead\n",pmp3->path);
- return db_sqlite_update(pmp3);
- }
-
- if(err != SQLITE_OK)
- DPRINTF(E_FATAL,L_DB,"Error inserting file %s in database\n",pmp3->fname);
-
- if((db_sqlite_in_scan)&&(!db_sqlite_reload)) {
- db_sqlite_exec(E_FATAL,"INSERT INTO updated VALUES (last_insert_rowid())");
- }
-
- if((!db_sqlite_in_scan) && (!db_sqlite_in_playlist_scan))
- db_sqlite_update_playlists();
-
- DPRINTF(E_SPAM,L_DB,"Exiting db_sqlite_add\n");
- return 0;
-}
-
-/**
- * update a database item
- *
- * \param pmp3 mp3 file to update
- */
-int db_sqlite_update(MP3FILE *pmp3) {
- int err;
-
- if(!pmp3->time_modified)
- pmp3->time_modified = (int)time(NULL);
-
- pmp3->db_timestamp = (int)time(NULL);
-
- err=db_sqlite_exec(E_FATAL,"UPDATE songs SET "
- "title='%q'," // title
- "artist='%q'," // artist
- "album='%q'," // album
- "genre='%q'," // genre
- "comment='%q'," // comment
- "type='%q'," // type
- "composer='%q'," // composer
- "orchestra='%q'," // orchestra
- "conductor='%q'," // conductor
- "grouping='%q'," // grouping
- "url='%q'," // url
- "bitrate=%d," // bitrate
- "samplerate=%d," // samplerate
- "song_length=%d," // song_length
- "file_size=%d," // file_size
- "year=%d," // year
- "track=%d," // track
- "total_tracks=%d," // total_tracks
- "disc=%d," // disc
- "total_discs=%d," // total_discs
- "time_modified=%d," // time_modified
- "db_timestamp=%d," // db_timestamp
- "bpm=%d," // bpm
- "disabled=%d," // disabled
- "compilation=%d," // compilation
- "rating=%d," // rating
- "sample_count=%d," // sample_count
- "codectype='%q'" // codec
- " WHERE path='%q'",
- STR(pmp3->title),
- STR(pmp3->artist),
- STR(pmp3->album),
- STR(pmp3->genre),
- STR(pmp3->comment),
- STR(pmp3->type),
- STR(pmp3->composer),
- STR(pmp3->orchestra),
- STR(pmp3->conductor),
- STR(pmp3->grouping),
- STR(pmp3->url),
- pmp3->bitrate,
- pmp3->samplerate,
- pmp3->song_length,
- pmp3->file_size,
- pmp3->year,
- pmp3->track,
- pmp3->total_tracks,
- pmp3->disc,
- pmp3->total_discs,
- pmp3->time_modified,
- pmp3->db_timestamp,
- pmp3->bpm,
- pmp3->disabled,
- pmp3->compilation,
- pmp3->rating,
- pmp3->sample_count,
- STR(pmp3->codectype),
- pmp3->path);
-
- if((db_sqlite_in_scan) && (!db_sqlite_reload)) {
- db_sqlite_exec(E_FATAL,"INSERT INTO updated (id) select id from songs where path='%q'",
- pmp3->path);
- }
-
- if((!db_sqlite_in_scan) && (!db_sqlite_in_playlist_scan))
- db_sqlite_update_playlists();
-
- return 0;
-}
-
-
-/**
- * Update the playlist item counts
- */
-int db_sqlite_update_playlists(void) {
- char **resarray;
- int rows, cols, index;
- char *where_clause;
-
- db_sqlite_get_table(E_FATAL,&resarray, &rows, &cols, "SELECT * FROM playlists");
-
- for(index=1;index <= rows; index ++) {
- DPRINTF(E_DBG,L_DB,"Updating playlist counts for %s\n",resarray[cols * index + 1]);
- if(atoi(resarray[cols * index + 2]) == 1) { // is a smart playlist
- where_clause=db_sqlite_parse_smart(resarray[cols * index + 4]);
- if(where_clause) {
- db_sqlite_exec(E_FATAL,"UPDATE playlists SET items=(SELECT COUNT(*) "
- "FROM songs WHERE %s) WHERE id=%s",where_clause,
- resarray[cols * index]);
- free(where_clause);
- }
- } else {
- db_sqlite_exec(E_FATAL,"UPDATE playlists SET items=(SELECT COUNT(*) "
- "FROM playlistitems WHERE playlistid=%s) WHERE id=%s",
- resarray[cols * index], resarray[cols * index]);
- }
- }
-
- db_sqlite_free_table(resarray);
-
- return 0;
-}
-
-
-/**
- * start enum based on the DBQUERYINFO struct passed
- *
- * \param pinfo DBQUERYINFO struct detailing what to enum
- */
-int db_sqlite_enum_start(DBQUERYINFO *pinfo) {
- char scratch[4096];
- char query[4096];
- char query_select[255];
- char query_count[255];
- char query_rest[4096];
- char *where_clause;
-
- int is_smart;
- int have_clause=0;
- int err;
- char *perr;
- char **resarray;
- int rows, cols;
- int browse=0;
- int results=0;
-
- const char *ptail;
-
- query[0] = '\0';
- query_select[0] = '\0';
- query_count[0] = '\0';
- query_rest[0] = '\0';
-
- switch(pinfo->query_type) {
- case queryTypeItems:
- strcpy(query_select,"SELECT * FROM songs ");
- strcpy(query_count,"SELECT COUNT(*) FROM songs ");
- break;
-
- case queryTypePlaylists:
- strcpy(query_select,"SELECT * FROM playlists ");
- strcpy(query_count,"SELECT COUNT (*) FROM playlists ");
- break;
-
- case queryTypePlaylistItems: /* Figure out if it's smart or dull */
- db_sqlite_lock();
- sprintf(scratch,"SELECT type,query FROM playlists WHERE id=%d",pinfo->playlist_id);
- DPRINTF(E_DBG,L_DB,"Executing %s\n",scratch);
- err=sqlite_get_table(db_sqlite_songs,scratch,&resarray,&rows,&cols,&perr);
- if(err != SQLITE_OK) {
- DPRINTF(E_LOG,L_DB|L_DAAP,"Error: %s\n",perr);
- sqlite_freemem(perr);
- db_sqlite_unlock();
- return -1;
- }
- if(!rows) {
- DPRINTF(E_LOG,L_DB|L_DAAP,"Could not find playlist %d\n",pinfo->playlist_id);
- return -1;
- }
-
- is_smart=(atoi(resarray[2]) == 1);
- have_clause=1;
- if(is_smart) {
- where_clause=db_sqlite_parse_smart(resarray[3]);
- if(!where_clause) {
- return -1;
- }
- sprintf(query_select,"SELECT * FROM songs ");
- sprintf(query_count,"SELECT COUNT(id) FROM songs ");
- sprintf(query_rest,"WHERE (%s)",where_clause);
- free(where_clause);
- } else {
- sprintf(query_count,"SELECT COUNT(id) FROM songs ");
-
- /* We need to fix playlist queries to stop
- * pulling the whole song db... the performance
- * of these playlist queries sucks.
- */
-#if 1
- sprintf(query_select,"select * from songs ");
- sprintf(query_rest,"where songs.id in (select songid from playlistitems where playlistid=%d)",pinfo->playlist_id);
-#else
- sprintf(query_select,"SELECT * FROM songs,playlistitems ");
- sprintf(query_rest,"WHERE (songs.id=playlistitems.songid and playlistitems.playlistid=%d) ORDER BY playlistitems.id",
- pinfo->playlist_id);
-#endif
- }
- sqlite_free_table(resarray);
- db_sqlite_unlock();
- break;
-
- /* Note that sqlite doesn't support COUNT(DISTINCT x) */
- case queryTypeBrowseAlbums:
- strcpy(query_select,"SELECT DISTINCT album FROM songs ");
- strcpy(query_count,"SELECT COUNT(album) FROM (SELECT DISTINCT album FROM songs ");
- browse=1;
- break;
-
- case queryTypeBrowseArtists:
- strcpy(query_select,"SELECT DISTINCT artist FROM songs ");
- strcpy(query_count,"SELECT COUNT(artist) FROM (SELECT DISTINCT artist FROM songs ");
- browse=1;
- break;
-
- case queryTypeBrowseGenres:
- strcpy(query_select,"SELECT DISTINCT genre FROM songs ");
- strcpy(query_count,"SELECT COUNT(genre) FROM (SELECT DISTINCT genre FROM songs ");
- browse=1;
- break;
-
- case queryTypeBrowseComposers:
- strcpy(query_select,"SELECT DISTINCT composer FROM songs ");
- strcpy(query_count,"SELECT COUNT(composer) FROM (SELECT DISTINCT composer FROM songs ");
- browse=1;
- break;
- default:
- DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type\n");
- return -1;
- }
-
- /* Apply the query/filter */
- if(pinfo->whereclause) {
- if(have_clause)
- strcat(query_rest," AND ");
- else
- strcpy(query_rest," WHERE ");
-
- strcat(query_rest,"(");
- strcat(query_rest,pinfo->whereclause);
- strcat(query_rest,")");
- }
-
-
- if(pinfo->index_type == indexTypeLast) {
- /* We don't really care how many items unless we are
- * doing a "last n items" query */
- strcpy(scratch,query_count);
- strcat(scratch,query_rest);
- if(browse)
- strcat(scratch,")");
-
- DPRINTF(E_DBG,L_DB,"result count query: %s\n",scratch);
-
- db_sqlite_lock();
- err=sqlite_get_table(db_sqlite_songs,scratch,&resarray,&rows,&cols,&perr);
- if(err != SQLITE_OK) {
- db_sqlite_unlock();
- DPRINTF(E_LOG,L_DB,"Error in results query: %s\n",perr);
- sqlite_freemem(perr);
- return -1;
- }
-
-
- results=atoi(resarray[1]);
- sqlite_free_table(resarray);
- db_sqlite_unlock();
-
- DPRINTF(E_DBG,L_DB,"Number of results: %d\n",results);
- }
-
- strcpy(query,query_select);
- strcat(query,query_rest);
-
- /* Apply any index */
- switch(pinfo->index_type) {
- case indexTypeFirst:
- sprintf(scratch," LIMIT %d",pinfo->index_high);
- break;
- case indexTypeLast:
- if(pinfo->index_low >= results) {
- sprintf(scratch," LIMIT %d",pinfo->index_low); /* unnecessary */
- } else {
- sprintf(scratch," LIMIT %d OFFSET %d",pinfo->index_low, results-pinfo->index_low);
- }
- break;
- case indexTypeSub:
- sprintf(scratch," LIMIT %d OFFSET %d",pinfo->index_high - pinfo->index_low,
- pinfo->index_low);
- break;
- case indexTypeNone:
- break;
- default:
- DPRINTF(E_LOG,L_DB,"Bad indexType: %d\n",(int)pinfo->index_type);
- scratch[0]='\0';
- break;
- }
-
- if(pinfo->index_type != indexTypeNone)
- strcat(query,scratch);
-
- /* start fetching... */
- db_sqlite_lock();
- err=sqlite_compile(db_sqlite_songs,query,&ptail,&db_sqlite_pvm,&perr);
- db_sqlite_unlock();
-
- DPRINTF(E_DBG,L_DB,"Enum query: %s\n",query);
-
- if(err != SQLITE_OK) {
- DPRINTF(E_LOG,L_DB,"Could not compile query: %s\n",query);
- sqlite_freemem(perr);
- return -1;
- }
-
- return 0;
-}
-
-int db_sqlite_enum_size(DBQUERYINFO *pinfo, int *count) {
- const char **valarray;
- const char **colarray;
- int err;
- char *perr;
- int cols;
- int total_size=0;
- int record_size;
-
- DPRINTF(E_DBG,L_DB,"Enumerating size\n");
-
- *count=0;
-
- db_sqlite_lock();
- while((err=sqlite_step(db_sqlite_pvm,&cols,&valarray,&colarray)) == SQLITE_ROW) {
- if((record_size = db_sqlite_get_size(pinfo,(char**)valarray))) {
- total_size += record_size;
- *count = *count + 1;
- }
- }
-
- if(err != SQLITE_DONE) {
- sqlite_finalize(db_sqlite_pvm,&perr);
- DPRINTF(E_FATAL,L_DB,"sqlite_step: %s\n",perr);
- sqlite_freemem(perr);
- db_sqlite_unlock();
- }
-
- db_sqlite_unlock();
- db_sqlite_enum_reset(pinfo);
-
- DPRINTF(E_DBG,L_DB,"Got size: %d\n",total_size);
- return total_size;
-}
-
-
-/**
- * fetch the next record from the enum
- */
-int db_sqlite_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap) {
- const char **valarray;
- const char **colarray;
- int err;
- char *perr;
- int cols;
- int result_size=-1;
- unsigned char *presult;
-
- db_sqlite_lock();
- err=sqlite_step(db_sqlite_pvm,&cols,&valarray,&colarray);
- db_sqlite_unlock();
-
- while((err == SQLITE_ROW) && (result_size)) {
- result_size=db_sqlite_get_size(pinfo,(char**)valarray);
- if(result_size) {
- presult=(unsigned char*)malloc(result_size);
- if(!presult)
- return 0;
- db_sqlite_build_dmap(pinfo,(char**)valarray,presult,result_size);
- *pdmap = presult;
- return result_size;
- }
- }
-
- if(err == SQLITE_DONE) {
- return -1;
- }
-
- db_sqlite_lock();
- sqlite_finalize(db_sqlite_pvm,&perr);
- DPRINTF(E_FATAL,L_DB,"sqlite_step: %s\n",perr);
- sqlite_freemem(perr);
- db_sqlite_unlock();
-
- return 0;
-}
-
-/**
- * start the enum again
- */
-int db_sqlite_enum_reset(DBQUERYINFO *pinfo) {
- db_sqlite_enum_end();
- return db_sqlite_enum_start(pinfo);
-}
-
-
-/**
- * stop the enum
- */
-int db_sqlite_enum_end(void) {
- char *perr=NULL;
-
- db_sqlite_lock();
- sqlite_finalize(db_sqlite_pvm,&perr);
- if(perr) {
- sqlite_freemem(perr);
- }
- db_sqlite_unlock();
-
- return 0;
-}
-
-int db_sqlite_get_size(DBQUERYINFO *pinfo, char **valarray) {
- int size;
- int transcode;
-
- switch(pinfo->query_type) {
- case queryTypeBrowseArtists: /* simple 'mlit' entry */
- case queryTypeBrowseAlbums:
- case queryTypeBrowseGenres:
- case queryTypeBrowseComposers:
- return valarray[0] ? (8 + strlen(valarray[0])) : 0;
- case queryTypePlaylists:
- size = 8; /* mlit */
- size += 12; /* mimc - you get it whether you want it or not */
- if(db_wantsmeta(pinfo->meta, metaItemId))
- size += 12; /* miid */
- if(db_wantsmeta(pinfo->meta, metaItunesSmartPlaylist)) {
- if(valarray[plType] && (atoi(valarray[plType])==1))
- size += 9; /* aeSP */
- }
- if(db_wantsmeta(pinfo->meta, metaItemName))
- size += (8 + strlen(valarray[plTitle])); /* minm */
- if(valarray[plType] && (atoi(valarray[plType])==1) &&
- db_wantsmeta(pinfo->meta, metaMPlaylistSpec))
- size += (8 + strlen(valarray[plQuery])); /* MSPS */
- if(db_wantsmeta(pinfo->meta, metaMPlaylistType))
- size += 9; /* MPTY */
- return size;
- break;
- case queryTypeItems:
- case queryTypePlaylistItems: /* essentially the same query */
- /* see if this is going to be transcoded */
- transcode = server_side_convert(valarray[37]);
-
- /* Items that get changed by transcode:
- *
- * type: item 8: changes to 'wav'
- * description: item 29: changes to 'wav audio file'
- * bitrate: item 15: guestimated, based on item 15, samplerate
- *
- * probably file size should change as well, but currently doesn't
- */
-
- size = 8; /* mlit */
- if(db_wantsmeta(pinfo->meta, metaItemKind))
- /* mikd */
- size += 9;
- if(db_wantsmeta(pinfo->meta, metaSongDataKind))
- /* asdk */
- size += 9;
- if(ISSTR(valarray[13]) && db_wantsmeta(pinfo->meta, metaSongDataURL))
- /* asul */
- size += (8 + strlen(valarray[13]));
- if(ISSTR(valarray[5]) && db_wantsmeta(pinfo->meta, metaSongAlbum))
- /* asal */
- size += (8 + strlen(valarray[5]));
- if(ISSTR(valarray[4]) && db_wantsmeta(pinfo->meta, metaSongArtist))
- /* asar */
- size += (8 + strlen(valarray[4]));
- if(valarray[23] && atoi(valarray[23]) && db_wantsmeta(pinfo->meta, metaSongBPM))
- /* asbt */
- size += 10;
- if(db_wantsmeta(pinfo->meta, metaSongBitRate)) {
- /* asbr */
- if(transcode) {
- if(valarray[15] && atoi(valarray[15]))
- size += 10;
- } else {
- if(valarray[14] && atoi(valarray[14]))
- size += 10;
- }
- }
- if(ISSTR(valarray[7]) && db_wantsmeta(pinfo->meta, metaSongComment))
- /* ascm */
- size += (8 + strlen(valarray[7]));
- if(valarray[24] && atoi(valarray[24]) && db_wantsmeta(pinfo->meta,metaSongCompilation))
- /* asco */
- size += 9;
- if(ISSTR(valarray[9]) && db_wantsmeta(pinfo->meta, metaSongComposer))
- /* ascp */
- size += (8 + strlen(valarray[9]));
- if(ISSTR(valarray[12]) && db_wantsmeta(pinfo->meta, metaSongGrouping))
- /* agrp */
- size += (8 + strlen(valarray[12]));
- if(valarray[30] && atoi(valarray[30]) && db_wantsmeta(pinfo->meta, metaSongDateAdded))
- /* asda */
- size += 12;
- if(valarray[31] && atoi(valarray[31]) && db_wantsmeta(pinfo->meta,metaSongDateModified))
- /* asdm */
- size += 12;
- if(valarray[22] && atoi(valarray[22]) && db_wantsmeta(pinfo->meta, metaSongDiscCount))
- /* asdc */
- size += 10;
- if(valarray[21] && atoi(valarray[21]) && db_wantsmeta(pinfo->meta, metaSongDiscNumber))
- size += 10;
- /* asdn */
- if(ISSTR(valarray[6]) && db_wantsmeta(pinfo->meta, metaSongGenre))
- /* asgn */
- size += (8 + strlen(valarray[6]));
- if(db_wantsmeta(pinfo->meta,metaItemId))
- /* miid */
- size += 12;
- if(ISSTR(valarray[8]) && db_wantsmeta(pinfo->meta,metaSongFormat)) {
- /* asfm */
- if(transcode) {
- size += 11; /* 'wav' */
- } else {
- size += (8 + strlen(valarray[8]));
- }
- }
- if(ISSTR(valarray[29]) && db_wantsmeta(pinfo->meta,metaSongDescription)) {
- /* asdt */
- if(transcode) {
- size += 22; /* 'wav audio file' */
- } else {
- size += (8 + strlen(valarray[29]));
- }
- }
- if(ISSTR(valarray[3]) && db_wantsmeta(pinfo->meta,metaItemName))
- /* minm */
- size += (8 + strlen(valarray[3]));
- if(valarray[34] && atoi(valarray[34]) && db_wantsmeta(pinfo->meta,metaSongDisabled))
- /* asdb */
- size += 9;
- if(valarray[15] && atoi(valarray[15]) && db_wantsmeta(pinfo->meta,metaSongSampleRate))
- /* assr */
- size += 12;
- if(valarray[17] && atoi(valarray[17]) && db_wantsmeta(pinfo->meta,metaSongSize))
- /* assz */
- size += 12;
-
- /* In the old daap code, we always returned 0 for asst and assp
- * (song start time, song stop time). I don't know if this
- * is required, so I'm going to disabled it
- */
-
- if(valarray[16] && atoi(valarray[16]) && db_wantsmeta(pinfo->meta, metaSongTime))
- /* astm */
- size += 12;
- if(valarray[20] && atoi(valarray[20]) && db_wantsmeta(pinfo->meta, metaSongTrackCount))
- /* astc */
- size += 10;
- if(valarray[19] && atoi(valarray[19]) && db_wantsmeta(pinfo->meta, metaSongTrackNumber))
- /* astn */
- size += 10;
- if(valarray[25] && atoi(valarray[25]) && db_wantsmeta(pinfo->meta, metaSongUserRating))
- /* asur */
- size += 9;
- if(valarray[18] && atoi(valarray[18]) && db_wantsmeta(pinfo->meta, metaSongYear))
- /* asyr */
- size += 10;
- if(db_wantsmeta(pinfo->meta, metaContainerItemId))
- /* mcti */
- size += 12;
- if(ISSTR(valarray[37]) && db_wantsmeta(pinfo->meta, metaSongCodecType))
- /* ascd */
- size += 12;
- return size;
- break;
-
- default:
- DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type: %d\n",(int)pinfo->query_type);
- return 0;
- }
- return 0;
-}
-
-int db_sqlite_build_dmap(DBQUERYINFO *pinfo, char **valarray, unsigned char *presult, int len) {
- unsigned char *current = presult;
- int transcode;
- int samplerate=0;
-
- switch(pinfo->query_type) {
- case queryTypeBrowseArtists: /* simple 'mlit' entry */
- case queryTypeBrowseAlbums:
- case queryTypeBrowseGenres:
- case queryTypeBrowseComposers:
- return db_dmap_add_string(current,"mlit",valarray[0]);
- case queryTypePlaylists:
- /* do I want to include the mlit? */
- current += db_dmap_add_container(current,"mlit",len - 8);
- if(db_wantsmeta(pinfo->meta,metaItemId))
- current += db_dmap_add_int(current,"miid",atoi(valarray[plID]));
- current += db_dmap_add_int(current,"mimc",atoi(valarray[plItems]));
- if(db_wantsmeta(pinfo->meta,metaItunesSmartPlaylist)) {
- if(valarray[plType] && (atoi(valarray[plType]) == 1))
- current += db_dmap_add_char(current,"aeSP",1);
- }
- if(db_wantsmeta(pinfo->meta,metaItemName))
- current += db_dmap_add_string(current,"minm",valarray[plTitle]);
- if((valarray[plType]) && (atoi(valarray[plType])==1) &&
- db_wantsmeta(pinfo->meta, metaMPlaylistSpec))
- current += db_dmap_add_string(current,"MSPS",valarray[plQuery]);
- if(db_wantsmeta(pinfo->meta, metaMPlaylistType))
- current += db_dmap_add_char(current,"MPTY",atoi(valarray[plType]));
- break;
- case queryTypeItems:
- case queryTypePlaylistItems: /* essentially the same query */
- /* see if this is going to be transcoded */
- transcode = server_side_convert(valarray[37]);
-
- /* Items that get changed by transcode:
- *
- * type: item 8: changes to 'wav'
- * description: item 29: changes to 'wav audio file'
- * bitrate: item 15: guestimated, but doesn't change file size
- *
- * probably file size should change as well, but currently doesn't
- */
-
- current += db_dmap_add_container(current,"mlit",len-8);
- if(db_wantsmeta(pinfo->meta, metaItemKind))
- current += db_dmap_add_char(current,"mikd",(char)atoi(valarray[28]));
- if(db_wantsmeta(pinfo->meta, metaSongDataKind))
- current += db_dmap_add_char(current,"asdk",(char)atoi(valarray[27]));
- if(ISSTR(valarray[13]) && db_wantsmeta(pinfo->meta, metaSongDataURL))
- current += db_dmap_add_string(current,"asul",valarray[13]);
- if(ISSTR(valarray[5]) && db_wantsmeta(pinfo->meta, metaSongAlbum))
- current += db_dmap_add_string(current,"asal",valarray[5]);
- if(ISSTR(valarray[4]) && db_wantsmeta(pinfo->meta, metaSongArtist))
- current += db_dmap_add_string(current,"asar",valarray[4]);
- if(valarray[23] && atoi(valarray[23]) && db_wantsmeta(pinfo->meta, metaSongBPM))
- current += db_dmap_add_short(current,"asbt",(short)atoi(valarray[23]));
- if(valarray[14] && atoi(valarray[14]) && db_wantsmeta(pinfo->meta, metaSongBitRate)) {
- if(transcode) {
- if(valarray[15]) samplerate=atoi(valarray[15]);
- if(samplerate) {
- current += db_dmap_add_short(current,"asbr",
- (short)(samplerate * 4 * 8)/1000);
- }
- } else {
- current += db_dmap_add_short(current,"asbr",(short)atoi(valarray[14]));
- }
- }
- if(ISSTR(valarray[7]) && db_wantsmeta(pinfo->meta, metaSongComment))
- current += db_dmap_add_string(current,"ascm",valarray[7]);
- if(valarray[24] && atoi(valarray[24]) && db_wantsmeta(pinfo->meta,metaSongCompilation))
- current += db_dmap_add_char(current,"asco",(char)atoi(valarray[24]));
- if(ISSTR(valarray[9]) && db_wantsmeta(pinfo->meta, metaSongComposer))
- current += db_dmap_add_string(current,"ascp",valarray[9]);
- if(ISSTR(valarray[12]) && db_wantsmeta(pinfo->meta, metaSongGrouping))
- current += db_dmap_add_string(current,"agrp",valarray[12]);
- if(valarray[30] && atoi(valarray[30]) && db_wantsmeta(pinfo->meta, metaSongDateAdded))
- current += db_dmap_add_int(current,"asda",(int)atoi(valarray[30]));
- if(valarray[31] && atoi(valarray[31]) && db_wantsmeta(pinfo->meta,metaSongDateModified))
- current += db_dmap_add_int(current,"asdm",(int)atoi(valarray[31]));
- if(valarray[22] && atoi(valarray[22]) && db_wantsmeta(pinfo->meta, metaSongDiscCount))
- current += db_dmap_add_short(current,"asdc",(short)atoi(valarray[22]));
- if(valarray[21] && atoi(valarray[21]) && db_wantsmeta(pinfo->meta, metaSongDiscNumber))
- current += db_dmap_add_short(current,"asdn",(short)atoi(valarray[21]));
- if(ISSTR(valarray[6]) && db_wantsmeta(pinfo->meta, metaSongGenre))
- current += db_dmap_add_string(current,"asgn",valarray[6]);
- if(db_wantsmeta(pinfo->meta,metaItemId))
- current += db_dmap_add_int(current,"miid",(int)atoi(valarray[0]));
- if(ISSTR(valarray[8]) && db_wantsmeta(pinfo->meta,metaSongFormat)) {
- if(transcode) {
- current += db_dmap_add_string(current,"asfm","wav");
- } else {
- current += db_dmap_add_string(current,"asfm",valarray[8]);
- }
- }
- if(ISSTR(valarray[29]) && db_wantsmeta(pinfo->meta,metaSongDescription)) {
- if(transcode) {
- current += db_dmap_add_string(current,"asdt","wav audio file");
- } else {
- current += db_dmap_add_string(current,"asdt",valarray[29]);
- }
- }
- if(ISSTR(valarray[3]) && db_wantsmeta(pinfo->meta,metaItemName))
- current += db_dmap_add_string(current,"minm",valarray[3]);
- if(valarray[34] && atoi(valarray[34]) && db_wantsmeta(pinfo->meta,metaSongDisabled))
- current += db_dmap_add_char(current,"asdb",(char)atoi(valarray[34]));
- if(valarray[15] && atoi(valarray[15]) && db_wantsmeta(pinfo->meta,metaSongSampleRate))
- current += db_dmap_add_int(current,"assr",atoi(valarray[15]));
- if(valarray[17] && atoi(valarray[17]) && db_wantsmeta(pinfo->meta,metaSongSize))
- current += db_dmap_add_int(current,"assz",atoi(valarray[17]));
- if(valarray[16] && atoi(valarray[16]) && db_wantsmeta(pinfo->meta, metaSongTime))
- current += db_dmap_add_int(current,"astm",atoi(valarray[16]));
- if(valarray[20] && atoi(valarray[20]) && db_wantsmeta(pinfo->meta, metaSongTrackCount))
- current += db_dmap_add_short(current,"astc",(short)atoi(valarray[20]));
- if(valarray[19] && atoi(valarray[19]) && db_wantsmeta(pinfo->meta, metaSongTrackNumber))
- current += db_dmap_add_short(current,"astn",(short)atoi(valarray[19]));
- if(valarray[25] && atoi(valarray[25]) && db_wantsmeta(pinfo->meta, metaSongUserRating))
- current += db_dmap_add_char(current,"asur",(char)atoi(valarray[25]));
- if(valarray[18] && atoi(valarray[18]) && db_wantsmeta(pinfo->meta, metaSongYear))
- current += db_dmap_add_short(current,"asyr",(short)atoi(valarray[18]));
- if(ISSTR(valarray[37]) && db_wantsmeta(pinfo->meta, metaSongCodecType))
- current += db_dmap_add_literal(current,"ascd",valarray[37],4);
- if(db_wantsmeta(pinfo->meta, metaContainerItemId))
- current += db_dmap_add_int(current,"mcti",atoi(valarray[0]));
- return 0;
- break;
-
- default:
- DPRINTF(E_LOG,L_DB|L_DAAP,"Unknown query type: %d\n",(int)pinfo->query_type);
- return 0;
- }
- return 0;
-}
-
-int db_sqlite_atoi(const char *what) {
- return what ? atoi(what) : 0;
-}
-char *db_sqlite_strdup(const char *what) {
- return what ? (strlen(what) ? strdup(what) : NULL) : NULL;
-}
-
-void db_sqlite_build_m3ufile(char **valarray, M3UFILE *pm3u) {
- memset(pm3u,0x00,sizeof(M3UFILE));
-
- pm3u->id=db_sqlite_atoi(valarray[0]);
- pm3u->title=db_sqlite_strdup(valarray[1]);
- pm3u->type=db_sqlite_atoi(valarray[2]);
- pm3u->items=db_sqlite_atoi(valarray[3]);
- pm3u->query=db_sqlite_strdup(valarray[4]);
- pm3u->db_timestamp=db_sqlite_atoi(valarray[5]);
- pm3u->path=db_sqlite_strdup(valarray[6]);
- pm3u->index=db_sqlite_atoi(valarray[7]);
- return;
-}
-
-void db_sqlite_build_mp3file(char **valarray, MP3FILE *pmp3) {
- memset(pmp3,0x00,sizeof(MP3FILE));
- pmp3->id=db_sqlite_atoi(valarray[0]);
- pmp3->path=db_sqlite_strdup(valarray[1]);
- pmp3->fname=db_sqlite_strdup(valarray[2]);
- pmp3->title=db_sqlite_strdup(valarray[3]);
- pmp3->artist=db_sqlite_strdup(valarray[4]);
- pmp3->album=db_sqlite_strdup(valarray[5]);
- pmp3->genre=db_sqlite_strdup(valarray[6]);
- pmp3->comment=db_sqlite_strdup(valarray[7]);
- pmp3->type=db_sqlite_strdup(valarray[8]);
- pmp3->composer=db_sqlite_strdup(valarray[9]);
- pmp3->orchestra=db_sqlite_strdup(valarray[10]);
- pmp3->conductor=db_sqlite_strdup(valarray[11]);
- pmp3->grouping=db_sqlite_strdup(valarray[12]);
- pmp3->url=db_sqlite_strdup(valarray[13]);
- pmp3->bitrate=db_sqlite_atoi(valarray[14]);
- pmp3->samplerate=db_sqlite_atoi(valarray[15]);
- pmp3->song_length=db_sqlite_atoi(valarray[16]);
- pmp3->file_size=db_sqlite_atoi(valarray[17]);
- pmp3->year=db_sqlite_atoi(valarray[18]);
- pmp3->track=db_sqlite_atoi(valarray[19]);
- pmp3->total_tracks=db_sqlite_atoi(valarray[20]);
- pmp3->disc=db_sqlite_atoi(valarray[21]);
- pmp3->total_discs=db_sqlite_atoi(valarray[22]);
- pmp3->bpm=db_sqlite_atoi(valarray[23]);
- pmp3->compilation=db_sqlite_atoi(valarray[24]);
- pmp3->rating=db_sqlite_atoi(valarray[25]);
- pmp3->play_count=db_sqlite_atoi(valarray[26]);
- pmp3->data_kind=db_sqlite_atoi(valarray[27]);
- pmp3->item_kind=db_sqlite_atoi(valarray[28]);
- pmp3->description=db_sqlite_strdup(valarray[29]);
- pmp3->time_added=db_sqlite_atoi(valarray[30]);
- pmp3->time_modified=db_sqlite_atoi(valarray[31]);
- pmp3->time_played=db_sqlite_atoi(valarray[32]);
- pmp3->db_timestamp=db_sqlite_atoi(valarray[33]);
- pmp3->disabled=db_sqlite_atoi(valarray[34]);
- pmp3->sample_count=db_sqlite_atoi(valarray[35]);
- pmp3->force_update=db_sqlite_atoi(valarray[36]);
- pmp3->codectype=db_sqlite_strdup(valarray[37]);
- pmp3->index=db_sqlite_atoi(valarray[38]);
-}
-
-/**
- * fetch a playlist by path
- *
- * \param path path to fetch
- */
-M3UFILE *db_sqlite_fetch_playlist(char *path, int index) {
- int rows,cols;
- char **resarray;
- int result;
- M3UFILE *pm3u=NULL;
-
- result = db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,
- "select * from playlists where path='%q' and idx=%d",
- path,index);
-
- if(result != DB_E_SUCCESS)
- return NULL;
-
- if(rows != 0) {
- pm3u=(M3UFILE*)malloc(sizeof(M3UFILE));
- if(!pm3u)
- DPRINTF(E_FATAL,L_MISC,"malloc error: db_sqlite_fetch_playlist\n");
-
- db_sqlite_build_m3ufile((char**)&resarray[cols],pm3u);
- }
-
- db_sqlite_free_table(resarray);
-
- if((rows) && (db_sqlite_in_playlist_scan) && (!db_sqlite_reload)) {
- db_sqlite_exec(E_FATAL,"insert into plupdated values (%d)",pm3u->id);
- }
-
- return pm3u;
-}
-
-/**
- * fetch a MP3FILE for a specific id
- *
- * \param id id to fetch
- */
-MP3FILE *db_sqlite_fetch_item(int id) {
- int rows,cols;
- char **resarray;
- MP3FILE *pmp3=NULL;
-
- db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,
- "SELECT * FROM songs WHERE id=%d",id);
-
- if(rows != 0) {
- pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
- if(!pmp3)
- DPRINTF(E_FATAL,L_MISC,"Malloc error in db_sqlite_fetch_item\n");
-
- db_sqlite_build_mp3file((char **)&resarray[cols],pmp3);
- }
-
- db_sqlite_free_table(resarray);
-
- if ((rows) && (db_sqlite_in_scan) && (!db_sqlite_reload)) {
- db_sqlite_exec(E_FATAL,"INSERT INTO updated VALUES (%d)",id);
- }
-
- return pmp3;
-}
-
-/**
- * retrieve a MP3FILE struct for the song with a give path
- *
- * \param path path of the file to retreive
- */
-MP3FILE *db_sqlite_fetch_path(char *path, int index) {
- int rows,cols;
- char **resarray;
- MP3FILE *pmp3=NULL;
-
- db_sqlite_get_table(E_DBG,&resarray,&rows,&cols,
- "SELECT * FROM songs WHERE path='%q' and idx=%d",
- path,index);
-
- if(rows != 0) {
- pmp3=(MP3FILE*)malloc(sizeof(MP3FILE));
- if(!pmp3)
- DPRINTF(E_FATAL,L_MISC,"Malloc error in db_sqlite_fetch_item\n");
-
- db_sqlite_build_mp3file((char **)&resarray[cols],pmp3);
- }
-
- db_sqlite_free_table(resarray);
-
- if ((rows) && (db_sqlite_in_scan) && (!db_sqlite_reload)) {
- db_sqlite_exec(E_FATAL,"INSERT INTO updated VALUES (%d)",pmp3->id);
- }
-
- return pmp3;
-}
-
-/**
- * dispose of a MP3FILE struct that was obtained
- * from db_sqlite_fetch_item
- *
- * \param pmp3 item obtained from db_sqlite_fetch_item
- */
-void db_sqlite_dispose_item(MP3FILE *pmp3) {
- if(!pmp3)
- return;
-
- MAYBEFREE(pmp3->path);
- MAYBEFREE(pmp3->fname);
- MAYBEFREE(pmp3->title);
- MAYBEFREE(pmp3->artist);
- MAYBEFREE(pmp3->album);
- MAYBEFREE(pmp3->genre);
- MAYBEFREE(pmp3->comment);
- MAYBEFREE(pmp3->type);
- MAYBEFREE(pmp3->composer);
- MAYBEFREE(pmp3->orchestra);
- MAYBEFREE(pmp3->conductor);
- MAYBEFREE(pmp3->grouping);
- MAYBEFREE(pmp3->description);
- MAYBEFREE(pmp3->url);
- MAYBEFREE(pmp3->codectype);
- free(pmp3);
-}
-
-void db_sqlite_dispose_playlist(M3UFILE *pm3u) {
- if(!pm3u)
- return;
-
- MAYBEFREE(pm3u->title);
- MAYBEFREE(pm3u->query);
- MAYBEFREE(pm3u->path);
- free(pm3u);
-}
-
-/**
- * count either the number of playlists, or the number of
- * songs
- *
- * \param type either countPlaylists or countSongs (type to count)
- */
-int db_sqlite_get_count(CountType_t type) {
- char *table;
- int rows, cols;
- char **resarray;
- int retval=0;
-
- switch(type) {
- case countPlaylists:
- table="playlists";
- break;
-
- case countSongs:
- default:
- table="songs";
- break;
- }
-
- db_sqlite_get_table(E_DBG,&resarray, &rows, &cols,
- "SELECT COUNT(*) FROM %q", table);
-
- if(rows != 0) {
- retval=atoi(resarray[cols]);
- }
-
- db_sqlite_free_table(resarray);
- return retval;
-}
-
-/**
- * get the database version of the currently opened database
- */
-int db_sqlite_get_version(void) {
- int rows, cols;
- char **resarray;
- int retval=0;
-
- db_sqlite_get_table(E_DBG,&resarray, &rows, &cols,
- "select value from config where term='version'");
-
- if(rows != 0) {
- retval=atoi(resarray[cols]);
- }
-
- db_sqlite_free_table(resarray);
- return retval;
-}
-
-
-char *db_sqlite_upgrade_scripts[] = {
- /* version 0 -> version 1 -- initial update */
- "CREATE TABLE songs (\n"
- " id INTEGER PRIMARY KEY NOT NULL,\n"
- " path VARCHAR(4096) UNIQUE NOT NULL,\n"
- " fname VARCHAR(255) NOT NULL,\n"
- " title VARCHAR(1024) DEFAULT NULL,\n"
- " artist VARCHAR(1024) DEFAULT NULL,\n"
- " album VARCHAR(1024) DEFAULT NULL,\n"
- " genre VARCHAR(255) DEFAULT NULL,\n"
- " comment VARCHAR(4096) DEFAULT NULL,\n"
- " type VARCHAR(255) DEFAULT NULL,\n"
- " composer VARCHAR(1024) DEFAULT NULL,\n"
- " orchestra VARCHAR(1024) DEFAULT NULL,\n"
- " conductor VARCHAR(1024) DEFAULT NULL,\n"
- " grouping VARCHAR(1024) DEFAULT NULL,\n"
- " url VARCHAR(1024) DEFAULT NULL,\n"
- " bitrate INTEGER DEFAULT 0,\n"
- " samplerate INTEGER DEFAULT 0,\n"
- " song_length INTEGER DEFAULT 0,\n"
- " file_size INTEGER DEFAULT 0,\n"
- " year INTEGER DEFAULT 0,\n"
- " track INTEGER DEFAULT 0,\n"
- " total_tracks INTEGER DEFAULT 0,\n"
- " disc INTEGER DEFAULT 0,\n"
- " total_discs INTEGER DEFAULT 0,\n"
- " bpm INTEGER DEFAULT 0,\n"
- " compilation INTEGER DEFAULT 0,\n"
- " rating INTEGER DEFAULT 0,\n"
- " play_count INTEGER DEFAULT 0,\n"
- " data_kind INTEGER DEFAULT 0,\n"
- " item_kind INTEGER DEFAULT 0,\n"
- " description INTEGER DEFAULT 0,\n"
- " time_added INTEGER DEFAULT 0,\n"
- " time_modified INTEGER DEFAULT 0,\n"
- " time_played INTEGER DEFAULT 0,\n"
- " db_timestamp INTEGER DEFAULT 0,\n"
- " disabled INTEGER DEFAULT 0,\n"
- " sample_count INTEGER DEFAULT 0,\n"
- " force_update INTEGER DEFAULT 0\n"
- ");\n"
- "CREATE INDEX idx_path ON songs(path);\n"
- "CREATE TABLE config (\n"
- " term VARCHAR(255) NOT NULL,\n"
- " subterm VARCHAR(255) DEFAULT NULL,\n"
- " value VARCHAR(1024) NOT NULL\n"
- ");\n"
- "CREATE TABLE playlists (\n"
- " id INTEGER PRIMARY KEY NOT NULL,\n"
- " title VARCHAR(255) NOT NULL,\n"
- " smart INTEGER NOT NULL,\n"
- " items INTEGER NOT NULL,\n"
- " query VARCHAR(1024)\n"
- ");\n"
- "CREATE TABLE playlistitems (\n"
- " id INTEGER NOT NULL,\n"
- " songid INTEGER NOT NULL\n"
- ");\n"
- "INSERT INTO config VALUES ('version','','1');\n"
- "INSERT INTO playlists VALUES (1,'Library',1,0,'1');\n",
-
- /* version 1 -> version 2 */
- /* force rescan for invalid utf-8 data */
- "REPLACE INTO config VALUES('rescan',NULL,1);\n"
- "UPDATE config SET value=2 WHERE term='version';\n",
-
- /* version 2 -> version 3 */
- /* add daap.songcodectype, normalize daap.songformat and daap.songdescription */
- "drop index idx_path;\n"
- "create temp table tempsongs as select * from songs;\n"
- "drop table songs;\n"
- "CREATE TABLE songs (\n"
- " id INTEGER PRIMARY KEY NOT NULL,\n"
- " path VARCHAR(4096) UNIQUE NOT NULL,\n"
- " fname VARCHAR(255) NOT NULL,\n"
- " title VARCHAR(1024) DEFAULT NULL,\n"
- " artist VARCHAR(1024) DEFAULT NULL,\n"
- " album VARCHAR(1024) DEFAULT NULL,\n"
- " genre VARCHAR(255) DEFAULT NULL,\n"
- " comment VARCHAR(4096) DEFAULT NULL,\n"
- " type VARCHAR(255) DEFAULT NULL,\n"
- " composer VARCHAR(1024) DEFAULT NULL,\n"
- " orchestra VARCHAR(1024) DEFAULT NULL,\n"
- " conductor VARCHAR(1024) DEFAULT NULL,\n"
- " grouping VARCHAR(1024) DEFAULT NULL,\n"
- " url VARCHAR(1024) DEFAULT NULL,\n"
- " bitrate INTEGER DEFAULT 0,\n"
- " samplerate INTEGER DEFAULT 0,\n"
- " song_length INTEGER DEFAULT 0,\n"
- " file_size INTEGER DEFAULT 0,\n"
- " year INTEGER DEFAULT 0,\n"
- " track INTEGER DEFAULT 0,\n"
- " total_tracks INTEGER DEFAULT 0,\n"
- " disc INTEGER DEFAULT 0,\n"
- " total_discs INTEGER DEFAULT 0,\n"
- " bpm INTEGER DEFAULT 0,\n"
- " compilation INTEGER DEFAULT 0,\n"
- " rating INTEGER DEFAULT 0,\n"
- " play_count INTEGER DEFAULT 0,\n"
- " data_kind INTEGER DEFAULT 0,\n"
- " item_kind INTEGER DEFAULT 0,\n"
- " description INTEGER DEFAULT 0,\n"
- " time_added INTEGER DEFAULT 0,\n"
- " time_modified INTEGER DEFAULT 0,\n"
- " time_played INTEGER DEFAULT 0,\n"
- " db_timestamp INTEGER DEFAULT 0,\n"
- " disabled INTEGER DEFAULT 0,\n"
- " sample_count INTEGER DEFAULT 0,\n"
- " force_update INTEGER DEFAULT 0,\n"
- " codectype VARCHAR(5) DEFAULT NULL\n"
- ");\n"
- "begin transaction;\n"
- "insert into songs select *,NULL from tempsongs;\n"
- "commit transaction;\n"
- "update songs set type=lower(type);\n"
- "update songs set type='m4a' where type='aac' or type='mp4';\n"
- "update songs set type='flac' where type='fla';\n"
- "update songs set type='mpc' where type='mpp';\n"
- "update songs set type='mpc' where type='mp+';\n"
- "update songs set description='AAC audio file' where type='m4a';\n"
- "update songs set description='MPEG audio file' where type='mp3';\n"
- "update songs set description='WAV audio file' where type='wav';\n"
- "update songs set description='Playlist URL' where type='pls';\n"
- "update songs set description='Ogg Vorbis audio file' where type='ogg';\n"
- "update songs set description='FLAC audio file' where type='flac';\n"
- "update songs set description='Musepack audio file' where type='mpc';\n"
- "update songs set codectype='mp4a' where type='m4a' or type='m4p';\n"
- "update songs set codectype='mpeg' where type='mp3';\n"
- "update songs set codectype='ogg' where type='ogg';\n"
- "update songs set codectype='flac' where type='flac';\n"
- "update songs set codectype='mpc' where type='mpc';\n"
- "update songs set force_update=1 where type='m4a';\n" /* look for alac */
- "create index idx_path on songs(path);\n"
- "drop table tempsongs;\n"
- "update config set value=3 where term='version';\n",
-
- /* version 3 -> version 4 */
- /* add db_timestamp and path to playlist table */
- "create temp table tempplaylists as select * from playlists;\n"
- "drop table playlists;\n"
- "CREATE TABLE playlists (\n"
- " id INTEGER PRIMARY KEY NOT NULL,\n"
- " title VARCHAR(255) NOT NULL,\n"
- " type INTEGER NOT NULL,\n"
- " items INTEGER NOT NULL,\n"
- " query VARCHAR(1024),\n"
- " db_timestamp INTEGER NOT NULL,\n"
- " path VARCHAR(4096)\n"
- ");\n"
- "insert into playlists select *,0,NULL from tempplaylists;\n"
- "drop table tempplaylists;\n"
- "update config set value=4 where term='version';\n",
-
- /* version 4 -> version 5 */
- /* add index to playlist table */
- "create temp table tempplaylists as select * from playlists;\n"
- "drop table playlists;\n"
- "CREATE TABLE playlists (\n"
- " id INTEGER PRIMARY KEY NOT NULL,\n"
- " title VARCHAR(255) NOT NULL,\n"
- " type INTEGER NOT NULL,\n"
- " items INTEGER NOT NULL,\n"
- " query VARCHAR(1024),\n"
- " db_timestamp INTEGER NOT NULL,\n"
- " path VARCHAR(4096),\n"
- " idx INTEGER NOT NULL\n"
- ");\n"
- "insert into playlists select *,0 from tempplaylists;\n"
- "drop table tempplaylists;\n"
- "update config set value=5 where term='version';\n",
-
- /* version 5 -> version 6 */
- "drop index idx_path;\n"
- "create temp table tempsongs as select * from songs;\n"
- "drop table songs;\n"
- "CREATE TABLE songs (\n"
- " id INTEGER PRIMARY KEY NOT NULL,\n"
- " path VARCHAR(4096) UNIQUE NOT NULL,\n"
- " fname VARCHAR(255) NOT NULL,\n"
- " title VARCHAR(1024) DEFAULT NULL,\n"
- " artist VARCHAR(1024) DEFAULT NULL,\n"
- " album VARCHAR(1024) DEFAULT NULL,\n"
- " genre VARCHAR(255) DEFAULT NULL,\n"
- " comment VARCHAR(4096) DEFAULT NULL,\n"
- " type VARCHAR(255) DEFAULT NULL,\n"
- " composer VARCHAR(1024) DEFAULT NULL,\n"
- " orchestra VARCHAR(1024) DEFAULT NULL,\n"
- " conductor VARCHAR(1024) DEFAULT NULL,\n"
- " grouping VARCHAR(1024) DEFAULT NULL,\n"
- " url VARCHAR(1024) DEFAULT NULL,\n"
- " bitrate INTEGER DEFAULT 0,\n"
- " samplerate INTEGER DEFAULT 0,\n"
- " song_length INTEGER DEFAULT 0,\n"
- " file_size INTEGER DEFAULT 0,\n"
- " year INTEGER DEFAULT 0,\n"
- " track INTEGER DEFAULT 0,\n"
- " total_tracks INTEGER DEFAULT 0,\n"
- " disc INTEGER DEFAULT 0,\n"
- " total_discs INTEGER DEFAULT 0,\n"
- " bpm INTEGER DEFAULT 0,\n"
- " compilation INTEGER DEFAULT 0,\n"
- " rating INTEGER DEFAULT 0,\n"
- " play_count INTEGER DEFAULT 0,\n"
- " data_kind INTEGER DEFAULT 0,\n"
- " item_kind INTEGER DEFAULT 0,\n"
- " description INTEGER DEFAULT 0,\n"
- " time_added INTEGER DEFAULT 0,\n"
- " time_modified INTEGER DEFAULT 0,\n"
- " time_played INTEGER DEFAULT 0,\n"
- " db_timestamp INTEGER DEFAULT 0,\n"
- " disabled INTEGER DEFAULT 0,\n"
- " sample_count INTEGER DEFAULT 0,\n"
- " force_update INTEGER DEFAULT 0,\n"
- " codectype VARCHAR(5) DEFAULT NULL,\n"
- " idx INTEGER NOT NULL\n"
- ");\n"
- "begin transaction;\n"
- "insert into songs select *,0 from tempsongs;\n"
- "commit transaction;\n"
- "create index idx_path on songs(path);\n"
- "drop table tempsongs;\n"
- "update config set value=6 where term='version';\n",
-
- /* version 6 -> version 7 */
- "create temp table tempitems as select * from playlistitems;\n"
- "drop table playlistitems;\n"
- "CREATE TABLE playlistitems (\n"
- " id INTEGER PRIMARY KEY NOT NULL,\n"
- " playlistid INTEGER NOT NULL,\n"
- " songid INTEGER NOT NULL\n"
- ");\n"
- "insert into playlistitems (playlistid, songid) select * from tempitems;\n"
- "drop table tempitems;\n"
- "update config set value=7 where term='version';\n",
-
- /* version 7 -> version 8 */
- "create index idx_songid on playlistitems(songid);\n"
- "create index idx_playlistid on playlistitems(playlistid);\n"
- "update config set value=8 where term='version';\n",
-
- NULL /* No more versions! */
-};
-
-/**
- * Upgrade database from an older version of the database to the newest
- *
- * \param from_version the current version of the database
- */
-int db_sqlite_update_version(int from_version) {
- char db_new_path[PATH_MAX + 1];
- int from_fd, to_fd;
- int copied=0;
- int result;
-
- if(from_version > (sizeof(db_sqlite_upgrade_scripts)/sizeof(char*))) {
- DPRINTF(E_FATAL,L_DB,"Database version too new (time machine, maybe?)\n");
- }
-
- while(db_sqlite_upgrade_scripts[from_version]) {
- DPRINTF(E_LOG,L_DB,"Upgrading database from version %d to version %d\n",from_version,
- from_version+1);
-
- if(!copied) {
- /* copy original version */
- sprintf(db_new_path,"%s.version-%02d",db_path,from_version);
- from_fd=r_open2(db_path,O_RDONLY);
- to_fd=r_open3(db_new_path,O_RDWR | O_CREAT,0666);
-
- if((from_fd == -1) || (to_fd == -1)) {
- DPRINTF(E_FATAL,L_DB,"Could not make backup copy of database "
- "(%s). Check write permissions for runas user.\n",
- db_new_path);
- }
-
- while((result=readwrite(from_fd, to_fd) > 0));
-
- if(result == -1) {
- DPRINTF(E_FATAL,L_DB,"Could not make db backup (%s)\n",
- strerror(errno));
- }
-
- r_close(from_fd);
- r_close(to_fd);
-
- copied=1;
- }
-
- if(db_sqlite_exec(E_LOG,db_sqlite_upgrade_scripts[from_version]) != DB_E_SUCCESS) {
- DPRINTF(E_FATAL,L_DB,"Error upgrading database. A backup copy of "
- "you original database is located at %s. Please save it "
- " somewhere and report to the forums at www.mt-daapd.org. "
- " Thanks.\n",
- db_new_path);
- }
- from_version++;
- }
-
- /* removed our backup file */
- if(copied)
- unlink(db_new_path);
-
- return 0;
-}
-
diff --git a/src/dbs-sqlite.h b/src/dbs-sqlite.h
deleted file mode 100644
index 3db1aefe..00000000
--- a/src/dbs-sqlite.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * $Id$
- * sqlite-specific db implementation
- *
- * Copyright (C) 2005 Ron Pedde (ron@pedde.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef _DBS_SQLITE_H_
-#define _DBS_SQLITE_H_
-
-extern int db_sqlite_open(char *parameters);
-extern int db_sqlite_init(int reload);
-extern int db_sqlite_deinit(void);
-extern int db_sqlite_add(MP3FILE *pmp3);
-extern int db_sqlite_enum_start(DBQUERYINFO *pinfo);
-extern int db_sqlite_enum_size(DBQUERYINFO *pinfo, int *count);
-extern int db_sqlite_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap);
-extern int db_sqlite_enum_reset(DBQUERYINFO *pinfo);
-extern int db_sqlite_enum_end(void);
-extern int db_sqlite_start_scan(void);
-extern int db_sqlite_end_song_scan(void);
-extern int db_sqlite_end_scan(void);
-extern int db_sqlite_get_count(CountType_t type);
-extern MP3FILE *db_sqlite_fetch_item(int id);
-extern MP3FILE *db_sqlite_fetch_path(char *path,int index);
-extern M3UFILE *db_sqlite_fetch_playlist(char *path, int index);
-extern void db_sqlite_dispose_item(MP3FILE *pmp3);
-extern void db_sqlite_dispose_playlist(M3UFILE *pm3u);
-extern int db_sqlite_add_playlist(char *name, int type, char *clause, char *path, int index, int *playlistid);
-extern int db_sqlite_add_playlist_item(int playlistid, int songid);
-extern int db_sqlite_edit_playlist(int id, char *name, char *clause);
-extern int db_sqlite_delete_playlist(int playlistid);
-extern int db_sqlite_delete_playlist_item(int playlistid, int songid);
-
-typedef enum {
- songID,
- songPath,
- songFname,
- songTitle,
- songArtist,
- songAlbum,
- songGenre,
- songComment,
- songType,
- songComposer,
- songOrchestra,
- songGrouping,
- songURL,
- songBitrate,
- songSampleRate,
- songLength,
- songFilesize,
- songYear,
- songTrack,
- songTotalTracks,
- songDisc,
- songTotalDiscs,
- songBPM,
- songCompilation,
- songRating,
- songPlayCount,
- songDataKind,
- songItemKind,
- songDescription,
- songTimeAdded,
- songTimeModified,
- songTimePlayed,
- songDBTimestamp,
- songDisabled,
- songSampleCount,
- songForceUpdate,
- songCodecType
-} SongField_t;
-
-typedef enum {
- plID,
- plTitle,
- plType,
- plItems,
- plQuery,
- plDBTimestamp,
- plPath
-} PlaylistField_t;
-
-
-#endif /* _DBS_SQLITE_H_ */
diff --git a/src/dispatch.c b/src/dispatch.c
index 4cd0c6b0..303ead69 100644
--- a/src/dispatch.c
+++ b/src/dispatch.c
@@ -70,7 +70,7 @@ static char *dispatch_xml_encode(char *original, int len);
static int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char *block, int len);
-/**
+/**
* Hold the inf for the output serializer
*/
typedef struct tag_xml_stack {
@@ -90,11 +90,11 @@ typedef struct tag_output_info {
/**
* Handles authentication for the daap server. This isn't the
- * authenticator for the web admin page, but rather the iTunes
+ * authenticator for the web admin page, but rather the iTunes
* authentication when trying to connect to the server. Note that most
* of this is actually handled in the web server registration, which
* decides when to apply the authentication or not. If you mess with
- * when and where the webserver applies auth or not, you'll likely
+ * when and where the webserver applies auth or not, you'll likely
* break something. It seems that some requests must be authed, and others
* not. If you apply authentication somewhere that iTunes doesn't expect
* it, it happily disconnects.
@@ -104,7 +104,7 @@ typedef struct tag_output_info {
* \returns 1 if auth successful, 0 otherwise
*/
int daap_auth(char *username, char *password) {
- if((password == NULL) &&
+ if((password == NULL) &&
((config.readpassword == NULL) || (strlen(config.readpassword)==0)))
return 1;
@@ -151,24 +151,24 @@ void daap_handler(WS_CONNINFO *pwsc) {
/* nm... backing this out. Really do need a "quirks" mode
pwsc->close=0;
if(ws_testrequestheader(pwsc,"Connection","Close")) {
- pwsc->close = 1;
+ pwsc->close = 1;
}
*/
-
+
if(ws_getvar(pwsc,"session-id"))
pqi->session_id = atoi(ws_getvar(pwsc,"session-id"));
-
+
/* tokenize the uri for easier decoding */
string=(pwsc->uri)+1;
while((token=strtok_r(string,"/",&save))) {
string=NULL;
pqi->uri_sections[pqi->uri_count++] = token;
}
-
+
/* Start dispatching */
if(!strcasecmp(pqi->uri_sections[0],"server-info"))
return dispatch_server_info(pwsc,pqi);
-
+
if(!strcasecmp(pqi->uri_sections[0],"content-codes"))
return dispatch_content_codes(pwsc,pqi);
@@ -194,13 +194,13 @@ void daap_handler(WS_CONNINFO *pwsc) {
}
pqi->db_id=atoi(pqi->uri_sections[1]);
if(pqi->uri_count == 3) {
- if(!strcasecmp(pqi->uri_sections[2],"items"))
+ if(!strcasecmp(pqi->uri_sections[2],"items"))
/* /databases/id/items */
return dispatch_items(pwsc,pqi);
if(!strcasecmp(pqi->uri_sections[2],"containers"))
- /* /databases/id/containers */
+ /* /databases/id/containers */
return dispatch_playlists(pwsc,pqi);
-
+
pwsc->close=1;
free(pqi);
ws_returnerror(pwsc,404,"Page not found");
@@ -252,7 +252,7 @@ void daap_handler(WS_CONNINFO *pwsc) {
}
}
}
-
+
pwsc->close=1;
free(pqi);
ws_returnerror(pwsc,404,"Page not found");
@@ -319,7 +319,7 @@ int dispatch_output_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char *bl
OUTPUT_INFO *poi=(pqi->output_info);
int result;
- if(poi->xml_output)
+ if(poi->xml_output)
return dispatch_output_xml_write(pwsc, pqi, block, len);
result=r_write(pwsc->fd,block,len);
@@ -375,7 +375,7 @@ int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char
/* lookup and serialize */
DPRINTF(E_SPAM,L_DAAP,"%*s %s: %d\n",poi->stack_height,"",block_tag,block_len);
pitem=dispatch_xml_lookup_tag(block_tag);
- if(poi->readable)
+ if(poi->readable)
r_fdprintf(pwsc->fd,"%*s",poi->stack_height,"");
r_fdprintf(pwsc->fd,"<%s>",pitem->description);
switch(pitem->type) {
@@ -497,11 +497,11 @@ int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char
if(poi->stack[stack_ptr].bytes_left < 0) {
DPRINTF(E_FATAL,L_DAAP,"negative container\n");
}
-
+
if(!poi->stack[stack_ptr].bytes_left) {
poi->stack_height--;
pitem=dispatch_xml_lookup_tag(poi->stack[stack_ptr].tag);
- if(poi->readable)
+ if(poi->readable)
r_fdprintf(pwsc->fd,"%*s",poi->stack_height,"");
r_fdprintf(pwsc->fd,"%s>",pitem->description);
if(poi->readable)
@@ -518,8 +518,8 @@ int dispatch_output_xml_write(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, unsigned char
/**
* finish streaming output to the client, freeing any allocated
* memory, and cleaning up
- *
- * \param pwsc current conninfo struct
+ *
+ * \param pwsc current conninfo struct
* \param pqi current dbquery struct
*/
int dispatch_output_end(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
@@ -629,11 +629,12 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
item=atoi(pqi->uri_sections[3]);
- if(ws_getrequestheader(pwsc,"range")) {
+ if(ws_getrequestheader(pwsc,"range")) {
offset=(off_t)atol(ws_getrequestheader(pwsc,"range") + 6);
}
- pmp3=db_fetch_item(item);
+ /* FIXME: error handling */
+ pmp3=db_fetch_item(NULL,item);
if(!pmp3) {
DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item);
ws_returnerror(pwsc,404,"File Not Found");
@@ -646,7 +647,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
file_ptr = server_side_convert_open(pmp3->path,
offset,
pmp3->song_length,
- pmp3->codectype);
+ pmp3->codectype);
if (file_ptr) {
file_fd = fileno(file_ptr);
} else {
@@ -699,7 +700,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_LOG,L_WS,
"Session %d: Streaming file '%s' to %s (offset %ld)\n",
pqi->session_id,pmp3->fname, pwsc->hostname,(long)offset);
-
+
if(!offset)
config.stats.songs_served++; /* FIXME: remove stat races */
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
@@ -736,33 +737,33 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
fstat(img_fd, &sb);
img_size = sb.st_size;
r_close(img_fd);
-
+
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
/*PENDING*/
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
real_len += img_size + 24;
-
+
if (offset > img_size + 24) {
offset -= img_size + 24;
}
}
}
-
+
file_len = real_len - offset;
-
+
DPRINTF(E_DBG,L_WS,"Thread %d: Length of file (remaining) is %ld\n",
pwsc->threadno,(long)file_len);
-
+
// DWB: fix content-type to correctly reflect data
// content type (dmap tagged) should only be used on
// dmap protocol requests, not the actually song data
- if(pmp3->type)
+ if(pmp3->type)
ws_addresponseheader(pwsc,"Content-Type","audio/%s",pmp3->type);
-
+
ws_addresponseheader(pwsc,"Content-Length","%ld",(long)file_len);
ws_addresponseheader(pwsc,"Connection","Close");
-
-
+
+
if(!offset)
ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n");
else {
@@ -771,16 +772,16 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
(long)real_len+1);
ws_writefd(pwsc,"HTTP/1.1 206 Partial Content\r\n");
}
-
+
ws_emitheaders(pwsc);
-
+
config_set_status(pwsc,pqi->session_id,"Streaming file '%s'",pmp3->fname);
DPRINTF(E_LOG,L_WS,"Session %d: Streaming file '%s' to %s (offset %d)\n",
pqi->session_id,pmp3->fname, pwsc->hostname,(long)offset);
-
+
if(!offset)
config.stats.songs_served++; /* FIXME: remove stat races */
-
+
if((config.artfilename) &&
(!offset) &&
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
@@ -789,7 +790,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
pmp3->fname, img_fd);
da_attach_image(img_fd, pwsc->fd, file_fd, offset);
} else if (strncasecmp(pmp3->type, "m4a", 4) == 0) {
- DPRINTF(E_INF,L_WS|L_ART,"Dynamic add artwork to %s (fd %d)\n",
+ DPRINTF(E_INF,L_WS|L_ART,"Dynamic add artwork to %s (fd %d)\n",
pmp3->fname, img_fd);
da_aac_attach_image(img_fd, pwsc->fd, file_fd, offset);
}
@@ -797,7 +798,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_INF,L_WS,"Seeking to offset %ld\n",(long)offset);
lseek(file_fd,offset,SEEK_SET);
}
-
+
if((bytes_copied=copyfile(file_fd,pwsc->fd)) == -1) {
DPRINTF(E_INF,L_WS,"Error copying file to remote... %s\n",
strerror(errno));
@@ -805,7 +806,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_INF,L_WS,"Finished streaming file to remote: %d bytes\n",
bytes_copied);
}
-
+
config_set_status(pwsc,pqi->session_id,NULL);
r_close(file_fd);
db_dispose_item(pmp3);
@@ -836,10 +837,11 @@ void dispatch_addplaylistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
while((token=strsep((char**)¤t,","))) {
if(token) {
- db_add_playlist_item(pqi->playlist_id,atoi(token));
+ /* FIXME: error handling */
+ db_add_playlist_item(NULL,pqi->playlist_id,atoi(token));
}
}
-
+
free(tempstring);
/* success(ish)... spool out a dmap block */
@@ -869,7 +871,8 @@ void dispatch_deleteplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
return;
}
- db_delete_playlist(atoi(ws_getvar(pwsc,"dmap.itemid")));
+ /* FIXME: error handling */
+ db_delete_playlist(NULL,atoi(ws_getvar(pwsc,"dmap.itemid")));
/* success(ish)... spool out a dmap block */
current = playlist_response;
@@ -905,10 +908,11 @@ void dispatch_deleteplaylistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
while((token=strsep((char**)¤t,","))) {
if(token) {
- db_delete_playlist_item(pqi->playlist_id,atoi(token));
+ /* FIXME: Error handling */
+ db_delete_playlist_item(NULL,pqi->playlist_id,atoi(token));
}
}
-
+
free(tempstring);
/* success(ish)... spool out a dmap block */
@@ -946,7 +950,8 @@ void dispatch_addplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
name=ws_getvar(pwsc,"dmap.itemname");
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
- retval=db_add_playlist(name,type,query,NULL,0,&playlistid);
+ /* FIXME: Error handling */
+ retval=db_add_playlist(NULL,name,type,query,NULL,0,&playlistid);
if(retval != DB_E_SUCCESS) {
DPRINTF(E_LOG,L_DAAP,"error adding playlist. aborting\n");
ws_returnerror(pwsc,500,"error adding playlist");
@@ -973,37 +978,38 @@ void dispatch_addplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
void dispatch_editplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
unsigned char edit_response[20];
unsigned char *current = edit_response;
-
+
char *name, *query;
int id;
-
+
int retval;
-
+
if((!ws_getvar(pwsc,"dmap.itemname")) ||
(!ws_getvar(pwsc,"dmap.itemid"))) {
DPRINTF(E_LOG,L_DAAP,"Missing name on playlist edit");
ws_returnerror(pwsc,500,"missing playlist name");
return;
}
-
+
name=ws_getvar(pwsc,"dmap.itemname");
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
id=atoi(ws_getvar(pwsc,"dmap.itemid"));
-
- retval=db_edit_playlist(id,name,query);
+
+ /* FIXME: Error handling */
+ retval=db_edit_playlist(NULL,id,name,query);
if(retval != DB_E_SUCCESS) {
DPRINTF(E_LOG,L_DAAP,"error editing playlist.");
ws_returnerror(pwsc,500,"Error editing playlist");
return;
}
-
- current += db_dmap_add_container(current,"MEPR",12);
+
+ current += db_dmap_add_container(current,"MEPR",12);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
-
+
dispatch_output_start(pwsc,pqi,20);
dispatch_output_write(pwsc,pqi,edit_response,20);
dispatch_output_end(pwsc,pqi);
-
+
pwsc->close=1;
return;
}
@@ -1031,13 +1037,16 @@ void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
pqi->query_type = queryTypePlaylistItems;
pqi->index_type=indexTypeNone;
- if(db_enum_start(pqi)) {
+
+ /* FIXME: Error handling */
+ if(db_enum_start(NULL,pqi)) {
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!");
return;
}
-
- list_length=db_enum_size(pqi,&song_count);
+
+ /* FIXME: Error handling */
+ db_enum_size(NULL,pqi,&song_count,&list_length);
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d songs, dmap size: %d\n",song_count,list_length);
@@ -1046,12 +1055,14 @@ void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
current += db_dmap_add_char(current,"muty",0); /* 9 */
current += db_dmap_add_int(current,"mtco",song_count); /* 12 */
current += db_dmap_add_int(current,"mrco",song_count); /* 12 */
- current += db_dmap_add_container(current,"mlcl",list_length);
+ current += db_dmap_add_container(current,"mlcl",list_length);
dispatch_output_start(pwsc,pqi,61+list_length);
dispatch_output_write(pwsc,pqi,items_response,61);
- while((list_length=db_enum_fetch(pqi,&block)) > 0) {
+ while((db_enum_fetch(NULL,pqi,&list_length,&block) == DB_E_SUCCESS) &&
+ (list_length))
+ {
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length);
free(block);
@@ -1059,7 +1070,7 @@ void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
- db_enum_end();
+ db_enum_end(NULL);
dispatch_output_end(pwsc,pqi);
return;
@@ -1095,15 +1106,16 @@ void dispatch_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
pqi->index_type = indexTypeNone;
- if(db_enum_start(pqi)) {
+ if(db_enum_start(NULL,pqi)) {
DPRINTF(E_LOG,L_DAAP|L_BROW,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!\n");
return;
}
-
+
DPRINTF(E_DBG,L_DAAP|L_BROW,"Getting enum size.\n");
- list_length=db_enum_size(pqi,&item_count);
+ /* FIXME: Error handling */
+ db_enum_size(NULL,pqi,&item_count,&list_length);
DPRINTF(E_DBG,L_DAAP|L_BROW,"Item enum: got %d items, dmap size: %d\n",
item_count,list_length);
@@ -1117,15 +1129,17 @@ void dispatch_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
dispatch_output_start(pwsc,pqi,52+list_length);
dispatch_output_write(pwsc,pqi,browse_response,52);
- while((list_length=db_enum_fetch(pqi,&block)) > 0) {
+ while((db_enum_fetch(NULL,pqi,&list_length,&block) == DB_E_SUCCESS) &&
+ (list_length))
+ {
DPRINTF(E_SPAM,L_DAAP|L_BROW,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length);
free(block);
}
DPRINTF(E_DBG,L_DAAP|L_BROW,"Done enumerating\n");
-
- db_enum_end();
+
+ db_enum_end(NULL);
dispatch_output_end(pwsc,pqi);
return;
@@ -1151,13 +1165,14 @@ void dispatch_playlists(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
pqi->query_type = queryTypePlaylists;
pqi->index_type = indexTypeNone;
- if(db_enum_start(pqi)) {
+ if(db_enum_start(NULL,pqi)) {
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!\n");
return;
}
-
- list_length=db_enum_size(pqi,&pl_count);
+
+ /* FIXME: Error handling */
+ db_enum_size(NULL,pqi,&pl_count,&list_length);
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d playlists, dmap size: %d\n",pl_count,list_length);
@@ -1166,12 +1181,14 @@ void dispatch_playlists(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
current += db_dmap_add_char(current,"muty",0); /* 9 */
current += db_dmap_add_int(current,"mtco",pl_count); /* 12 */
current += db_dmap_add_int(current,"mrco",pl_count); /* 12 */
- current += db_dmap_add_container(current,"mlcl",list_length);
+ current += db_dmap_add_container(current,"mlcl",list_length);
dispatch_output_start(pwsc,pqi,61+list_length);
dispatch_output_write(pwsc,pqi,playlist_response,61);
- while((list_length=db_enum_fetch(pqi,&block)) > 0) {
+ while((db_enum_fetch(NULL,pqi,&list_length,&block) == DB_E_SUCCESS) &&
+ (list_length))
+ {
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length);
free(block);
@@ -1179,7 +1196,7 @@ void dispatch_playlists(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
- db_enum_end();
+ db_enum_end(NULL);
dispatch_output_end(pwsc,pqi);
return;
@@ -1200,13 +1217,14 @@ void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
pqi->query_type = queryTypeItems;
pqi->index_type=indexTypeNone;
- if(db_enum_start(pqi)) {
+ if(db_enum_start(NULL,pqi)) {
DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!");
return;
}
-
- list_length=db_enum_size(pqi,&song_count);
+
+ /* FIXME: Error handling */
+ db_enum_size(NULL,pqi,&song_count,&list_length);
DPRINTF(E_DBG,L_DAAP,"Item enum: got %d songs, dmap size: %d\n",song_count,list_length);
@@ -1215,12 +1233,14 @@ void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
current += db_dmap_add_char(current,"muty",0); /* 9 */
current += db_dmap_add_int(current,"mtco",song_count); /* 12 */
current += db_dmap_add_int(current,"mrco",song_count); /* 12 */
- current += db_dmap_add_container(current,"mlcl",list_length);
+ current += db_dmap_add_container(current,"mlcl",list_length);
dispatch_output_start(pwsc,pqi,61+list_length);
dispatch_output_write(pwsc,pqi,items_response,61);
- while((list_length=db_enum_fetch(pqi,&block)) > 0) {
+ while((db_enum_fetch(NULL,pqi,&list_length,&block) == DB_E_SUCCESS) &&
+ (list_length))
+ {
DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length);
free(block);
@@ -1228,7 +1248,7 @@ void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
- db_enum_end();
+ db_enum_end(NULL);
dispatch_output_end(pwsc,pqi);
return;
@@ -1259,7 +1279,7 @@ void dispatch_update(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
if(FD_ISSET(pwsc->fd,&rset)) {
/* can't be ready for read, must be error */
DPRINTF(E_DBG,L_DAAP,"Socket closed?\n");
-
+
return;
}
}
@@ -1280,6 +1300,7 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
unsigned char dbinfo_response[255]; /* FIXME */
unsigned char *current = dbinfo_response;
int namelen;
+ int count;
namelen=strlen(config.servername);
@@ -1289,11 +1310,13 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
current += db_dmap_add_int(current,"mtco",1); /* 12 */
current += db_dmap_add_int(current,"mrco",1); /* 12 */
current += db_dmap_add_container(current,"mlcl",52 + namelen);
- current += db_dmap_add_container(current,"mlit",44 + namelen);
+ current += db_dmap_add_container(current,"mlit",44 + namelen);
current += db_dmap_add_int(current,"miid",1); /* 12 */
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + namelen */
- current += db_dmap_add_int(current,"mimc",db_get_song_count()); /* 12 */
- current += db_dmap_add_int(current,"mctc",db_get_playlist_count()); /* 12 */
+ db_get_song_count(NULL,&count);
+ current += db_dmap_add_int(current,"mimc",count); /* 12 */
+ db_get_playlist_count(NULL,&count);
+ current += db_dmap_add_int(current,"mctc",count); /* 12 */
dispatch_output_start(pwsc,pqi,113+namelen);
dispatch_output_write(pwsc,pqi,dbinfo_response,113+namelen);
@@ -1313,7 +1336,7 @@ void dispatch_login(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
unsigned char login_response[32];
unsigned char *current = login_response;
int session;
-
+
session = config_get_next_session();
current += db_dmap_add_container(current,"mlog",24);
@@ -1342,7 +1365,7 @@ void dispatch_content_codes(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
current += db_dmap_add_container(current,"mccr",len + 12);
current += db_dmap_add_int(current,"mstt",200);
-
+
dispatch_output_start(pwsc,pqi,len+20);
dispatch_output_write(pwsc,pqi,content_codes,20);
@@ -1357,7 +1380,7 @@ void dispatch_content_codes(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
dispatch_output_write(pwsc,pqi,mdcl,len+8);
dicurrent++;
}
-
+
dispatch_output_end(pwsc,pqi);
return;
}
@@ -1376,7 +1399,7 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
}
client_version=ws_getrequestheader(pwsc,"Client-DAAP-Version");
-
+
current += db_dmap_add_container(current,"msrv",actual_length - 8);
current += db_dmap_add_int(current,"mstt",200); /* 12 */
@@ -1396,7 +1419,7 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + strlen(name) */
current += db_dmap_add_char(current,"msau", /* 9 */
- config.readpassword != NULL ? 2 : 0);
+ config.readpassword != NULL ? 2 : 0);
current += db_dmap_add_char(current,"msex",0); /* 9 */
current += db_dmap_add_char(current,"msix",0); /* 9 */
current += db_dmap_add_char(current,"msbr",0); /* 9 */
diff --git a/src/main.c b/src/main.c
index 25224a0a..42149239 100644
--- a/src/main.c
+++ b/src/main.c
@@ -117,7 +117,7 @@
*/
CONFIG config; /**< Main configuration structure, as read from configfile */
-/*
+/*
* Forwards
*/
static int daemon_start(void);
@@ -158,7 +158,7 @@ int daemon_start(void) {
if((fd = open("/dev/null", O_RDWR, 0)) != -1) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
- dup2(fd, STDERR_FILENO);
+ dup2(fd, STDERR_FILENO);
if (fd > 2)
close(fd);
}
@@ -219,7 +219,7 @@ int drop_privs(char *user) {
}
if(pw) {
- if(initgroups(user,pw->pw_gid) != 0 ||
+ if(initgroups(user,pw->pw_gid) != 0 ||
setgid(pw->pw_gid) != 0 ||
setuid(pw->pw_uid) != 0) {
err=errno;
@@ -353,13 +353,14 @@ int main(int argc, char *argv[]) {
int start_time;
int end_time;
int rescan_counter=0;
- int old_song_count;
+ int old_song_count, song_count;
int force_non_root=0;
int skip_initial=0;
pthread_t signal_tid;
int pid_fd;
FILE *pid_fp=NULL;
+ char *perr;
config.use_mdns=1;
err_debuglevel=1;
@@ -482,8 +483,8 @@ int main(int argc, char *argv[]) {
}
/* this will require that the db be readable by the runas user */
- if(db_open(config.dbdir))
- DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",strerror(errno));
+ if(db_open(&perr,config.dbdir))
+ DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",perr);
/* Initialize the database before starting */
DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n");
@@ -529,7 +530,8 @@ int main(int argc, char *argv[]) {
end_time=time(NULL);
- DPRINTF(E_LOG,L_MAIN,"Scanned %d songs in %d seconds\n",db_get_song_count(),
+ db_get_song_count(NULL,&song_count);
+ DPRINTF(E_LOG,L_MAIN,"Scanned %d songs in %d seconds\n",song_count,
end_time-start_time);
while(!config.stop) {
@@ -543,7 +545,7 @@ int main(int argc, char *argv[]) {
}
if(config.reload) {
- old_song_count = db_get_song_count();
+ old_song_count = song_count;
start_time=time(NULL);
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n");
@@ -552,8 +554,10 @@ int main(int argc, char *argv[]) {
config.stop=1;
}
config.reload=0;
- DPRINTF(E_INF,L_MAIN|L_DB|L_SCAN,"Scanned %d songs (was %d) in %d seconds\n",
- db_get_song_count(),old_song_count,time(NULL)-start_time);
+ db_get_song_count(NULL,&song_count);
+ DPRINTF(E_INF,L_MAIN|L_DB|L_SCAN,"Scanned %d songs (was %d) in "
+ "%d seconds\n",song_count,old_song_count,
+ time(NULL)-start_time);
}
sleep(MAIN_SLEEP_INTERVAL);
diff --git a/src/mp3-scanner.c b/src/mp3-scanner.c
index 7d9516ca..644c00b3 100644
--- a/src/mp3-scanner.c
+++ b/src/mp3-scanner.c
@@ -6,7 +6,7 @@
* but the name is the same for historical purposes, not to mention
* the fact that sf.net makes it virtually impossible to manage a cvs
* root reasonably. Perhaps one day soon they will move to subversion.
- *
+ *
* /me crosses his fingers
*
* Copyright (C) 2003 Ron Pedde (ron@pedde.com)
@@ -107,20 +107,20 @@ static int scan_static_playlist(char *path);
/* For known types, I'm gong to use the "official" apple
* daap.songformat, daap.songdescription, and daap.songcodecsubtype.
* If I we don't have "official" ones, we can make them up the
- * way we currently are: using extension or whatver.
+ * way we currently are: using extension or whatver.
*
* This means that you can test to see if something is, say, an un-drmed
* aac file by just testing for ->type "m4a", rather than checking every
* different flavor of file extension.
- *
+ *
* NOTE: Although they are represented here as strings, the codectype is
- * *really* an unsigned short. So when it gets serialized, it gets
- * serialized as a short int. If you put something other than 3 or 4
+ * *really* an unsigned short. So when it gets serialized, it gets
+ * serialized as a short int. If you put something other than 3 or 4
* characters as your codectype, you'll see strange results.
*
* FIXME: url != pls -- this method of dispatching handlers based on file type
- * is completely wrong. There needs to be a separate type that gets carried
- * around with it, at least outside the database that says where the info
+ * is completely wrong. There needs to be a separate type that gets carried
+ * around with it, at least outside the database that says where the info
* CAME FROM.
*
* This system is broken, and won't work with something like a .cue file
@@ -160,8 +160,8 @@ static PLAYLISTLIST scan_playlistlist = { NULL, NULL };
/**
* add a playlist to the playlistlist. The playlistlist is a
* list of playlists that need to be processed once the current
- * scan is done. THIS IS NOT REENTRANT, and it meant to be
- * called only inside the rescan loop.
+ * scan is done. THIS IS NOT REENTRANT, and it meant to be
+ * called only inside the rescan loop.
*
* \param path path of the playlist to add
*/
@@ -243,7 +243,7 @@ int scan_init(char *path) {
return -1;
scan_process_playlistlist();
-
+
if(db_end_scan())
return -1;
@@ -317,7 +317,7 @@ int scan_path(char *path) {
if(!pde)
break;
-
+
if(pde->d_name[0] == '.') /* skip hidden and directories */
continue;
@@ -344,9 +344,9 @@ int scan_path(char *path) {
(strcasestr(config.extensions, ext))) {
/* only scan if it's been changed, or empty db */
modified_time=sb.st_mtime;
- pmp3=db_fetch_path(mp3_path,0);
+ pmp3=db_fetch_path(NULL,mp3_path,0);
- if((!pmp3) || (pmp3->db_timestamp < modified_time) ||
+ if((!pmp3) || (pmp3->db_timestamp < modified_time) ||
(pmp3->force_update)) {
scan_music_file(path,pde,&sb,is_compdir);
} else {
@@ -380,6 +380,7 @@ int scan_static_playlist(char *path) {
MP3FILE *pmp3;
struct stat sb;
char *current;
+ char *perr;
DPRINTF(E_WARN,L_SCAN|L_PL,"Processing static playlist: %s\n",path);
if(stat(path,&sb)) {
@@ -399,7 +400,7 @@ int scan_static_playlist(char *path) {
*current='\x0';
}
- pm3u = db_fetch_playlist(path,0);
+ pm3u = db_fetch_playlist(NULL,path,0);
if(pm3u && (pm3u->db_timestamp > sb.st_mtime)) {
/* already up-to-date */
DPRINTF(E_DBG,L_SCAN,"Playlist already up-to-date\n");
@@ -407,13 +408,15 @@ int scan_static_playlist(char *path) {
return TRUE;
}
- if(pm3u)
- db_delete_playlist(pm3u->id);
+ if(pm3u)
+ db_delete_playlist(NULL,pm3u->id);
fd=open(path,O_RDONLY);
if(fd != -1) {
- if(db_add_playlist(base_path,PL_STATICFILE,NULL,path,0,&playlistid) != DB_E_SUCCESS) {
- DPRINTF(E_LOG,L_SCAN,"Error adding m3u playlist %s\n",path);
+ if(db_add_playlist(&perr,base_path,PL_STATICFILE,NULL,path,
+ 0,&playlistid) != DB_E_SUCCESS) {
+ DPRINTF(E_LOG,L_SCAN,"Error adding m3u %s: %s\n",path,perr);
+ free(perr);
db_dispose_playlist(pm3u);
return FALSE;
}
@@ -428,7 +431,7 @@ int scan_static_playlist(char *path) {
memset(linebuffer,0x00,sizeof(linebuffer));
while(readline(fd,linebuffer,sizeof(linebuffer)) > 0) {
while((linebuffer[strlen(linebuffer)-1] == '\n') ||
- (linebuffer[strlen(linebuffer)-1] == '\r'))
+ (linebuffer[strlen(linebuffer)-1] == '\r'))
linebuffer[strlen(linebuffer)-1] = '\0';
if((linebuffer[0] == ';') || (linebuffer[0] == '#'))
@@ -447,12 +450,14 @@ int scan_static_playlist(char *path) {
DPRINTF(E_DBG,L_SCAN|L_PL,"Checking %s\n",real_path);
// might be valid, might not...
- if((pmp3=db_fetch_path(real_path,0))) {
- db_add_playlist_item(playlistid,pmp3->id);
+ if((pmp3=db_fetch_path(&perr,real_path,0))) {
+ /* FIXME: better error handling */
+ db_add_playlist_item(NULL,playlistid,pmp3->id);
db_dispose_item(pmp3);
} else {
DPRINTF(E_WARN,L_SCAN|L_PL,"Playlist entry %s bad: %s\n",
- path,strerror(errno));
+ path,perr);
+ free(perr);
}
}
close(fd);
@@ -469,7 +474,7 @@ int scan_static_playlist(char *path) {
*
* scan a particular file as a music file
*/
-void scan_music_file(char *path, struct dirent *pde,
+void scan_music_file(char *path, struct dirent *pde,
struct stat *psb, int is_compdir) {
MP3FILE mp3file;
char mp3_path[PATH_MAX];
@@ -482,7 +487,7 @@ void scan_music_file(char *path, struct dirent *pde,
/* we found an mp3 file */
DPRINTF(E_INF,L_SCAN,"Found music file: %s\n",pde->d_name);
-
+
memset((void*)&mp3file,0,sizeof(mp3file));
mp3file.path=strdup(mp3_path);
mp3file.fname=strdup(pde->d_name);
@@ -511,14 +516,14 @@ void scan_music_file(char *path, struct dirent *pde,
*current=tolower(*current);
current++;
}
-
+
sprintf(fdescr,"%s audio file",mp3file.type);
mp3file.description = strdup(fdescr);
/* we'll just dodge the codectype */
}
}
}
-
+
/* Do the tag lookup here */
if(scan_get_info(mp3file.path,&mp3file)) {
make_composite_tags(&mp3file);
@@ -538,11 +543,12 @@ void scan_music_file(char *path, struct dirent *pde,
if(is_compdir)
mp3file.compilation = 1;
- db_add(&mp3file);
+ /* FIXME: error handling */
+ db_add(NULL,&mp3file);
} else {
DPRINTF(E_WARN,L_SCAN,"Skipping %s - scan failed\n",mp3file.path);
}
-
+
scan_freetags(&mp3file);
}
@@ -624,7 +630,7 @@ int scan_get_info(char *file, MP3FILE *pmp3) {
/**
* Manually build tags. Set artist to computer/orchestra
- * if there is already no artist. Perhaps this could be
+ * if there is already no artist. Perhaps this could be
* done better, but I'm not sure what else to do here.
*
* @param song MP3FILE of the file to build composite tags for
diff --git a/src/scan-xml.c b/src/scan-xml.c
index b10052c8..aeecf0fd 100644
--- a/src/scan-xml.c
+++ b/src/scan-xml.c
@@ -131,16 +131,16 @@ int scan_xml_datedecode(char *string) {
/**
* comparison for the red-black tree. @see redblack.c
- *
+ *
* @param pa one node to compare
* @param pb other node to compare
* @param cfg opaque pointer I'm not using
*/
int scan_xml_rb_compare(const void *pa, const void *pb, const void *cfg) {
- if(((SCAN_XML_RB*)pa)->itunes_index < ((SCAN_XML_RB*)pb)->itunes_index)
- return -1;
+ if(((SCAN_XML_RB*)pa)->itunes_index < ((SCAN_XML_RB*)pb)->itunes_index)
+ return -1;
if(((SCAN_XML_RB*)pb)->itunes_index < ((SCAN_XML_RB*)pa)->itunes_index)
- return 1;
+ return 1;
return 0;
}
@@ -156,10 +156,10 @@ int scan_xml_is_file(char *path) {
struct stat sb;
if(stat(path,&sb))
- return 0;
+ return 0;
if(sb.st_mode & S_IFREG)
- return 1;
+ return 1;
return 0;
}
@@ -182,35 +182,35 @@ int scan_xml_translate_path(char *pold, char *pnew) {
char *pbase;
if((!pold)||(!strlen(pold)))
- return FALSE;
+ return FALSE;
if(!path_found) {
- strcpy(working_path,pold);
+ strcpy(working_path,pold);
- DPRINTF(E_DBG,L_SCAN,"Translating %s\n",pold);
+ DPRINTF(E_DBG,L_SCAN,"Translating %s\n",pold);
- /* let's try to find the path by brute force.
- * We'll assume that it is under the xml file somewhere
- */
- while(!path_found && ((current = strrchr(working_path,'/')))) {
- strcpy(base_path,scan_xml_file);
- pbase = strrchr(base_path,'/');
- if(!pbase) return FALSE;
+ /* let's try to find the path by brute force.
+ * We'll assume that it is under the xml file somewhere
+ */
+ while(!path_found && ((current = strrchr(working_path,'/')))) {
+ strcpy(base_path,scan_xml_file);
+ pbase = strrchr(base_path,'/');
+ if(!pbase) return FALSE;
- strcpy(pbase,pold + (current-working_path));
- if(base_path[strlen(base_path)-1] == '/')
- base_path[strlen(base_path)-1] = '\0';
+ strcpy(pbase,pold + (current-working_path));
+ if(base_path[strlen(base_path)-1] == '/')
+ base_path[strlen(base_path)-1] = '\0';
- DPRINTF(E_DBG,L_SCAN,"Trying %s\n",base_path);
- if(scan_xml_is_file(base_path)) {
- path_found=1;
- discard = (current - working_path);
- DPRINTF(E_DBG,L_SCAN,"Found it!\n");
- }
- *current='\0';
- }
- if(!current)
- return FALSE;
+ DPRINTF(E_DBG,L_SCAN,"Trying %s\n",base_path);
+ if(scan_xml_is_file(base_path)) {
+ path_found=1;
+ discard = (current - working_path);
+ DPRINTF(E_DBG,L_SCAN,"Found it!\n");
+ }
+ *current='\0';
+ }
+ if(!current)
+ return FALSE;
}
strcpy(base_path,scan_xml_file);
@@ -219,7 +219,7 @@ int scan_xml_translate_path(char *pold, char *pnew) {
strcpy(pbase,pold + discard);
if(base_path[strlen(base_path)-1] == '/')
- base_path[strlen(base_path)-1] = '\0';
+ base_path[strlen(base_path)-1] = '\0';
realpath(base_path,pnew);
@@ -241,16 +241,16 @@ void scan_xml_add_lookup(int itunes_index, int mtd_index) {
pnew=(SCAN_XML_RB*)malloc(sizeof(SCAN_XML_RB));
if(!pnew)
- DPRINTF(E_FATAL,L_SCAN,"malloc error in scan_xml_add_lookup\n");
-
+ DPRINTF(E_FATAL,L_SCAN,"malloc error in scan_xml_add_lookup\n");
+
pnew->itunes_index = itunes_index;
pnew->mtd_index = mtd_index;
val = rbsearch((const void*)pnew,scan_xml_db);
if(!val) {
- /* couldn't alloc the rb tree structure -- if we don't
- * die now, we are going to soon enough*/
- DPRINTF(E_FATAL,L_SCAN,"redblack tree insert error\n");
+ /* couldn't alloc the rb tree structure -- if we don't
+ * die now, we are going to soon enough*/
+ DPRINTF(E_FATAL,L_SCAN,"redblack tree insert error\n");
}
}
@@ -268,9 +268,9 @@ int scan_xml_get_index(int itunes_index, int *mtd_index) {
rb.itunes_index = itunes_index;
prb = (SCAN_XML_RB*) rbfind((void*)&rb,scan_xml_db);
if(prb) {
- *mtd_index = prb->mtd_index;
- DPRINTF(E_SPAM,L_SCAN,"Matching %d to %d\n",itunes_index,*mtd_index);
- return TRUE;
+ *mtd_index = prb->mtd_index;
+ DPRINTF(E_SPAM,L_SCAN,"Matching %d to %d\n",itunes_index,*mtd_index);
+ return TRUE;
}
return FALSE;
@@ -287,12 +287,12 @@ int scan_xml_get_tagindex(char *tag) {
int index=0;
while(*ptag && (strcasecmp(tag,*ptag) != 0)) {
- ptag++;
- index++;
+ ptag++;
+ index++;
}
- if(*ptag)
- return index;
+ if(*ptag)
+ return index;
return SCAN_XML_T_UNKNOWN;
}
@@ -311,45 +311,45 @@ char *scan_xml_urldecode(char *string, int space_as_plus) {
pnew=(char*)malloc(strlen(string)+1);
if(!pnew)
- return NULL;
+ return NULL;
src=string;
dst=pnew;
while(*src) {
- switch(*src) {
- case '+':
- if(space_as_plus) {
- *dst++=' ';
- } else {
- *dst++=*src;
- }
- src++;
- break;
- case '%':
- /* this is hideous */
- src++;
- if(*src) {
- if((*src <= '9') && (*src >='0'))
- val=(*src - '0');
- else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
- val=10+(tolower(*src) - 'a');
- src++;
- }
- if(*src) {
- val *= 16;
- if((*src <= '9') && (*src >='0'))
- val+=(*src - '0');
- else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
- val+=(10+(tolower(*src) - 'a'));
- src++;
- }
- *dst++=val;
- break;
- default:
- *dst++=*src++;
- break;
- }
+ switch(*src) {
+ case '+':
+ if(space_as_plus) {
+ *dst++=' ';
+ } else {
+ *dst++=*src;
+ }
+ src++;
+ break;
+ case '%':
+ /* this is hideous */
+ src++;
+ if(*src) {
+ if((*src <= '9') && (*src >='0'))
+ val=(*src - '0');
+ else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
+ val=10+(tolower(*src) - 'a');
+ src++;
+ }
+ if(*src) {
+ val *= 16;
+ if((*src <= '9') && (*src >='0'))
+ val+=(*src - '0');
+ else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
+ val+=(10+(tolower(*src) - 'a'));
+ src++;
+ }
+ *dst++=val;
+ break;
+ default:
+ *dst++=*src++;
+ break;
+ }
}
*dst='\0';
@@ -381,31 +381,31 @@ int scan_xml_playlist(char *filename) {
/* initialize the redblack tree */
if((scan_xml_db = rbinit(scan_xml_rb_compare,NULL)) == NULL) {
- DPRINTF(E_LOG,L_SCAN,"Could not initialize red/black tree\n");
- return FALSE;
+ DPRINTF(E_LOG,L_SCAN,"Could not initialize red/black tree\n");
+ return FALSE;
}
/* find the base dir of the itunes playlist itself */
working_base = strdup(filename);
if(strrchr(working_base,'/')) {
- *(strrchr(working_base,'/') + 1) = '\x0';
- scan_xml_real_base_path = strdup(working_base);
+ *(strrchr(working_base,'/') + 1) = '\x0';
+ scan_xml_real_base_path = strdup(working_base);
} else {
- scan_xml_real_base_path = strdup("/");
+ scan_xml_real_base_path = strdup("/");
}
free(working_base);
DPRINTF(E_SPAM,L_SCAN,"Parsing xml file: %s\n",filename);
if(!rxml_open(&xml_handle,filename,scan_xml_handler,NULL)) {
- DPRINTF(E_LOG,L_SCAN,"Error opening xml file %s: %s\n",
- filename,rxml_errorstring(xml_handle));
+ DPRINTF(E_LOG,L_SCAN,"Error opening xml file %s: %s\n",
+ filename,rxml_errorstring(xml_handle));
} else {
- if(!rxml_parse(xml_handle)) {
- retval=FALSE;
- DPRINTF(E_LOG,L_SCAN,"Error parsing xml file %s: %s\n",
- filename,rxml_errorstring(xml_handle));
- }
+ if(!rxml_parse(xml_handle)) {
+ retval=FALSE;
+ DPRINTF(E_LOG,L_SCAN,"Error parsing xml file %s: %s\n",
+ filename,rxml_errorstring(xml_handle));
+ }
}
rxml_close(xml_handle);
@@ -413,11 +413,11 @@ int scan_xml_playlist(char *filename) {
/* destroy the redblack tree */
val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
while(val) {
- lookup_val.itunes_index = ((SCAN_XML_RB*)val)->itunes_index;
- lookup_ptr = (SCAN_XML_RB *)rbdelete((void*)&lookup_val,scan_xml_db);
- if(lookup_ptr)
- free(lookup_ptr);
- val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
+ lookup_val.itunes_index = ((SCAN_XML_RB*)val)->itunes_index;
+ lookup_ptr = (SCAN_XML_RB *)rbdelete((void*)&lookup_val,scan_xml_db);
+ if(lookup_ptr)
+ free(lookup_ptr);
+ val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
}
rbdestroy(scan_xml_db);
@@ -445,32 +445,32 @@ void scan_xml_handler(int action,void* puser,char* info) {
switch(action) {
case RXML_EVT_OPEN: /* file opened */
- state = XML_STATE_PREAMBLE;
- /* send this event to all dispatches to allow them
- * to reset
- */
- scan_xml_preamble_section(action,info);
- scan_xml_tracks_section(action,info);
- scan_xml_playlists_section(action,info);
- break;
+ state = XML_STATE_PREAMBLE;
+ /* send this event to all dispatches to allow them
+ * to reset
+ */
+ scan_xml_preamble_section(action,info);
+ scan_xml_tracks_section(action,info);
+ scan_xml_playlists_section(action,info);
+ break;
case RXML_EVT_BEGIN:
case RXML_EVT_END:
case RXML_EVT_TEXT:
- switch(state) {
- case XML_STATE_PREAMBLE:
- state=scan_xml_preamble_section(action,info);
- break;
- case XML_STATE_TRACKS:
- state=scan_xml_tracks_section(action,info);
- break;
- case XML_STATE_PLAYLISTS:
- state=scan_xml_playlists_section(action,info);
- break;
- default:
- break;
- }
+ switch(state) {
+ case XML_STATE_PREAMBLE:
+ state=scan_xml_preamble_section(action,info);
+ break;
+ case XML_STATE_TRACKS:
+ state=scan_xml_tracks_section(action,info);
+ break;
+ case XML_STATE_PLAYLISTS:
+ state=scan_xml_playlists_section(action,info);
+ break;
+ default:
+ break;
+ }
default:
- break;
+ break;
}
}
@@ -489,62 +489,62 @@ void scan_xml_handler(int action,void* puser,char* info) {
int scan_xml_preamble_section(int action, char *info) {
static int expecting_next;
static int done;
-
+
switch(action) {
case RXML_EVT_OPEN: /* initialization */
- expecting_next=0;
- done=0;
- break;
+ expecting_next=0;
+ done=0;
+ break;
case RXML_EVT_END:
- if(expecting_next == SCAN_XML_PRE_TRACKS) { /* end of tracks tag */
- expecting_next=0;
- DPRINTF(E_DBG,L_SCAN,"Scanning tracks\n");
- return XML_STATE_TRACKS;
- }
- if(expecting_next == SCAN_XML_PRE_PLAYLISTS) {
- expecting_next=0;
- DPRINTF(E_DBG,L_SCAN,"Scanning playlists\n");
- return XML_STATE_PLAYLISTS;
- }
- break;
+ if(expecting_next == SCAN_XML_PRE_TRACKS) { /* end of tracks tag */
+ expecting_next=0;
+ DPRINTF(E_DBG,L_SCAN,"Scanning tracks\n");
+ return XML_STATE_TRACKS;
+ }
+ if(expecting_next == SCAN_XML_PRE_PLAYLISTS) {
+ expecting_next=0;
+ DPRINTF(E_DBG,L_SCAN,"Scanning playlists\n");
+ return XML_STATE_PLAYLISTS;
+ }
+ break;
case RXML_EVT_TEXT: /* scan for the tags we expect */
- if(!expecting_next) {
- if(strcmp(info,"Application Version") == 0) {
- expecting_next = SCAN_XML_PRE_VERSION;
- } else if (strcmp(info,"Music Folder") == 0) {
- expecting_next = SCAN_XML_PRE_PATH;
- } else if (strcmp(info,"Tracks") == 0) {
- expecting_next = SCAN_XML_PRE_TRACKS;
- } else if (strcmp(info,"Playlists") == 0) {
- expecting_next = SCAN_XML_PRE_PLAYLISTS;
- }
- } else {
- /* we were expecting someting! */
- switch(expecting_next) {
- case SCAN_XML_PRE_VERSION:
- if(!scan_xml_itunes_version) {
- scan_xml_itunes_version=strdup(info);
- DPRINTF(E_DBG,L_SCAN,"iTunes Version: %s\n",info);
- }
- break;
- case SCAN_XML_PRE_PATH:
- if(!scan_xml_itunes_base_path) {
- scan_xml_itunes_base_path=strdup(info);
- scan_xml_itunes_decoded_base_path=scan_xml_urldecode(info,0);
- DPRINTF(E_DBG,L_SCAN,"iTunes base path: %s\n",info);
- }
- break;
- default:
- break;
- }
- expecting_next=0;
- }
- break; /* RXML_EVT_TEXT */
+ if(!expecting_next) {
+ if(strcmp(info,"Application Version") == 0) {
+ expecting_next = SCAN_XML_PRE_VERSION;
+ } else if (strcmp(info,"Music Folder") == 0) {
+ expecting_next = SCAN_XML_PRE_PATH;
+ } else if (strcmp(info,"Tracks") == 0) {
+ expecting_next = SCAN_XML_PRE_TRACKS;
+ } else if (strcmp(info,"Playlists") == 0) {
+ expecting_next = SCAN_XML_PRE_PLAYLISTS;
+ }
+ } else {
+ /* we were expecting someting! */
+ switch(expecting_next) {
+ case SCAN_XML_PRE_VERSION:
+ if(!scan_xml_itunes_version) {
+ scan_xml_itunes_version=strdup(info);
+ DPRINTF(E_DBG,L_SCAN,"iTunes Version: %s\n",info);
+ }
+ break;
+ case SCAN_XML_PRE_PATH:
+ if(!scan_xml_itunes_base_path) {
+ scan_xml_itunes_base_path=strdup(info);
+ scan_xml_itunes_decoded_base_path=scan_xml_urldecode(info,0);
+ DPRINTF(E_DBG,L_SCAN,"iTunes base path: %s\n",info);
+ }
+ break;
+ default:
+ break;
+ }
+ expecting_next=0;
+ }
+ break; /* RXML_EVT_TEXT */
default:
- break;
+ break;
}
return XML_STATE_PREAMBLE;
@@ -569,7 +569,7 @@ int scan_xml_preamble_section(int action, char *info) {
state = (c); \
return XML_STATE_TRACKS; \
}}
-
+
int scan_xml_tracks_section(int action, char *info) {
static int state;
static int current_track_id;
@@ -580,143 +580,144 @@ int scan_xml_tracks_section(int action, char *info) {
MP3FILE *pmp3;
if(action == RXML_EVT_OPEN) {
- state = XML_TRACK_ST_INITIAL;
- memset((void*)&mp3,0,sizeof(MP3FILE));
- song_path = NULL;
- return 0;
+ state = XML_TRACK_ST_INITIAL;
+ memset((void*)&mp3,0,sizeof(MP3FILE));
+ song_path = NULL;
+ return 0;
}
/* walk through the states */
switch(state) {
case XML_TRACK_ST_INITIAL:
- /* expection only a */
- MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT);
- return XML_STATE_ERROR;
- break;
+ /* expection only a */
+ MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT);
+ return XML_STATE_ERROR;
+ break;
case XML_TRACK_ST_MAIN_DICT:
- /* either get a , or a */
- MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
- if ((action == RXML_EVT_END) && (strcasecmp(info,"dict") == 0)) {
- return XML_STATE_PREAMBLE;
- }
- return XML_STATE_ERROR;
- break;
+ /* either get a , or a */
+ MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
+ if ((action == RXML_EVT_END) && (strcasecmp(info,"dict") == 0)) {
+ return XML_STATE_PREAMBLE;
+ }
+ return XML_STATE_ERROR;
+ break;
case XML_TRACK_ST_EXPECTING_TRACK_ID:
- /* this is somewhat loose - id */
- MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
- MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_EXPECTING_TRACK_DICT);
- if (action == RXML_EVT_TEXT) {
- current_track_id = atoi(info);
- DPRINTF(E_DBG,L_SCAN,"Scanning iTunes id #%d\n",current_track_id);
- } else {
- return XML_STATE_ERROR;
- }
- break;
+ /* this is somewhat loose - id */
+ MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
+ MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_EXPECTING_TRACK_DICT);
+ if (action == RXML_EVT_TEXT) {
+ current_track_id = atoi(info);
+ DPRINTF(E_DBG,L_SCAN,"Scanning iTunes id #%d\n",current_track_id);
+ } else {
+ return XML_STATE_ERROR;
+ }
+ break;
case XML_TRACK_ST_EXPECTING_TRACK_DICT:
- /* waiting for a dict */
- MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO);
- return XML_STATE_ERROR;
- break;
+ /* waiting for a dict */
+ MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO);
+ return XML_STATE_ERROR;
+ break;
case XML_TRACK_ST_TRACK_INFO:
- /* again, kind of loose */
- MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO);
- MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA);
- if(action == RXML_EVT_TEXT) {
- current_field=scan_xml_get_tagindex(info);
- if(current_field == SCAN_XML_T_DISABLED) {
- mp3.disabled = 1;
- } else if(current_field == SCAN_XML_T_COMPILATION) {
- mp3.compilation = 1;
- }
- } else if((action == RXML_EVT_END) && (strcmp(info,"dict")==0)) {
- state = XML_TRACK_ST_MAIN_DICT;
- /* but more importantly, we gotta process the track */
- if(scan_xml_translate_path(song_path,real_path)) {
- pmp3=db_fetch_path(real_path,0);
- if(pmp3) {
- /* Update the existing record with the
- * updated stuff we got from the iTunes xml file
- */
- MAYBECOPYSTRING(title);
- MAYBECOPYSTRING(artist);
- MAYBECOPYSTRING(album);
- MAYBECOPYSTRING(genre);
- MAYBECOPY(song_length);
- MAYBECOPY(track);
- MAYBECOPY(total_tracks);
- MAYBECOPY(year);
- MAYBECOPY(bitrate);
- MAYBECOPY(samplerate);
- MAYBECOPY(play_count);
- MAYBECOPY(rating);
- MAYBECOPY(disc);
- MAYBECOPY(total_discs);
- MAYBECOPY(time_added);
+ /* again, kind of loose */
+ MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO);
+ MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA);
+ if(action == RXML_EVT_TEXT) {
+ current_field=scan_xml_get_tagindex(info);
+ if(current_field == SCAN_XML_T_DISABLED) {
+ mp3.disabled = 1;
+ } else if(current_field == SCAN_XML_T_COMPILATION) {
+ mp3.compilation = 1;
+ }
+ } else if((action == RXML_EVT_END) && (strcmp(info,"dict")==0)) {
+ state = XML_TRACK_ST_MAIN_DICT;
+ /* but more importantly, we gotta process the track */
+ if(scan_xml_translate_path(song_path,real_path)) {
+ /* FIXME: Error handling */
+ pmp3=db_fetch_path(NULL,real_path,0);
+ if(pmp3) {
+ /* Update the existing record with the
+ * updated stuff we got from the iTunes xml file
+ */
+ MAYBECOPYSTRING(title);
+ MAYBECOPYSTRING(artist);
+ MAYBECOPYSTRING(album);
+ MAYBECOPYSTRING(genre);
+ MAYBECOPY(song_length);
+ MAYBECOPY(track);
+ MAYBECOPY(total_tracks);
+ MAYBECOPY(year);
+ MAYBECOPY(bitrate);
+ MAYBECOPY(samplerate);
+ MAYBECOPY(play_count);
+ MAYBECOPY(rating);
+ MAYBECOPY(disc);
+ MAYBECOPY(total_discs);
+ MAYBECOPY(time_added);
- /* must add to the red-black tree */
- scan_xml_add_lookup(current_track_id,pmp3->id);
+ /* must add to the red-black tree */
+ scan_xml_add_lookup(current_track_id,pmp3->id);
- db_add(pmp3);
- db_dispose_item(pmp3);
+ db_add(NULL,pmp3);
+ db_dispose_item(pmp3);
- memset((void*)&mp3,0,sizeof(MP3FILE));
- MAYBEFREE(song_path);
- }
- }
- } else {
- return XML_STATE_ERROR;
- }
- break;
+ memset((void*)&mp3,0,sizeof(MP3FILE));
+ MAYBEFREE(song_path);
+ }
+ }
+ } else {
+ return XML_STATE_ERROR;
+ }
+ break;
case XML_TRACK_ST_TRACK_DATA:
- if(action == RXML_EVT_BEGIN) {
- break;
- } else if(action == RXML_EVT_TEXT) {
- if(current_field == SCAN_XML_T_NAME) {
- mp3.title = strdup(info);
- } else if(current_field == SCAN_XML_T_ARTIST) {
- mp3.artist = strdup(info);
- } else if(current_field == SCAN_XML_T_ALBUM) {
- mp3.album = strdup(info);
- } else if(current_field == SCAN_XML_T_GENRE) {
- mp3.genre = strdup(info);
- } else if(current_field == SCAN_XML_T_TOTALTIME) {
- mp3.song_length = atoi(info);
- } else if(current_field == SCAN_XML_T_TRACKNUMBER) {
- mp3.track = atoi(info);
- } else if(current_field == SCAN_XML_T_TRACKCOUNT) {
- mp3.total_tracks = atoi(info);
- } else if(current_field == SCAN_XML_T_YEAR) {
- mp3.year = atoi(info);
- } else if(current_field == SCAN_XML_T_BITRATE) {
- mp3.bitrate = atoi(info);
- } else if(current_field == SCAN_XML_T_SAMPLERATE) {
- mp3.samplerate = atoi(info);
- } else if(current_field == SCAN_XML_T_PLAYCOUNT) {
- mp3.play_count = atoi(info);
- } else if(current_field == SCAN_XML_T_RATING) {
- mp3.rating = atoi(info);
- } else if(current_field == SCAN_XML_T_DISCNO) {
- mp3.disc = atoi(info);
- } else if(current_field == SCAN_XML_T_DISCCOUNT) {
- mp3.total_discs = atoi(info);
- } else if(current_field == SCAN_XML_T_LOCATION) {
- song_path = scan_xml_urldecode(info,0);
- } else if(current_field == SCAN_XML_T_DATE_ADDED) {
- mp3.time_added = scan_xml_datedecode(info);
- }
- } else if(action == RXML_EVT_END) {
- state = XML_TRACK_ST_TRACK_INFO;
- } else {
- return XML_STATE_ERROR;
- }
- break;
+ if(action == RXML_EVT_BEGIN) {
+ break;
+ } else if(action == RXML_EVT_TEXT) {
+ if(current_field == SCAN_XML_T_NAME) {
+ mp3.title = strdup(info);
+ } else if(current_field == SCAN_XML_T_ARTIST) {
+ mp3.artist = strdup(info);
+ } else if(current_field == SCAN_XML_T_ALBUM) {
+ mp3.album = strdup(info);
+ } else if(current_field == SCAN_XML_T_GENRE) {
+ mp3.genre = strdup(info);
+ } else if(current_field == SCAN_XML_T_TOTALTIME) {
+ mp3.song_length = atoi(info);
+ } else if(current_field == SCAN_XML_T_TRACKNUMBER) {
+ mp3.track = atoi(info);
+ } else if(current_field == SCAN_XML_T_TRACKCOUNT) {
+ mp3.total_tracks = atoi(info);
+ } else if(current_field == SCAN_XML_T_YEAR) {
+ mp3.year = atoi(info);
+ } else if(current_field == SCAN_XML_T_BITRATE) {
+ mp3.bitrate = atoi(info);
+ } else if(current_field == SCAN_XML_T_SAMPLERATE) {
+ mp3.samplerate = atoi(info);
+ } else if(current_field == SCAN_XML_T_PLAYCOUNT) {
+ mp3.play_count = atoi(info);
+ } else if(current_field == SCAN_XML_T_RATING) {
+ mp3.rating = atoi(info);
+ } else if(current_field == SCAN_XML_T_DISCNO) {
+ mp3.disc = atoi(info);
+ } else if(current_field == SCAN_XML_T_DISCCOUNT) {
+ mp3.total_discs = atoi(info);
+ } else if(current_field == SCAN_XML_T_LOCATION) {
+ song_path = scan_xml_urldecode(info,0);
+ } else if(current_field == SCAN_XML_T_DATE_ADDED) {
+ mp3.time_added = scan_xml_datedecode(info);
+ }
+ } else if(action == RXML_EVT_END) {
+ state = XML_TRACK_ST_TRACK_INFO;
+ } else {
+ return XML_STATE_ERROR;
+ }
+ break;
default:
- return XML_STATE_ERROR;
+ return XML_STATE_ERROR;
}
return XML_STATE_TRACKS;
@@ -738,7 +739,7 @@ int scan_xml_tracks_section(int action, char *info) {
state = (c); \
return XML_STATE_PLAYLISTS; \
}}
-
+
/**
* collect playlist data for each playlist in the itunes xml file
* this again is implemented as a sloppy state machine, and assumes
@@ -748,7 +749,7 @@ int scan_xml_tracks_section(int action, char *info) {
* @param info text data associated with event
*/
int scan_xml_playlists_section(int action, char *info) {
- static int state = XML_PL_ST_INITIAL;
+ static int state = XML_PL_ST_INITIAL;
static int next_value=0; /** < what's next song info id or name */
static int native_plid=0; /** < the iTunes playlist id */
static int current_id=0; /** < the mt-daapd playlist id */
@@ -761,112 +762,116 @@ int scan_xml_playlists_section(int action, char *info) {
/* do initialization */
if(action == RXML_EVT_OPEN) {
- state = XML_PL_ST_INITIAL;
- if(current_name)
- free(current_name);
- current_name = NULL;
- dont_scan=0;
- return 0;
+ state = XML_PL_ST_INITIAL;
+ if(current_name)
+ free(current_name);
+ current_name = NULL;
+ dont_scan=0;
+ return 0;
}
switch(state) {
case XML_PL_ST_INITIAL:
- /* expecting or error */
- MAYBESETSTATE_PL(RXML_EVT_BEGIN,"array",XML_PL_ST_EXPECTING_PL);
- return XML_STATE_ERROR;
+ /* expecting or error */
+ MAYBESETSTATE_PL(RXML_EVT_BEGIN,"array",XML_PL_ST_EXPECTING_PL);
+ return XML_STATE_ERROR;
case XML_PL_ST_EXPECTING_PL:
- /* either a new playlist, or end of playlist list */
- MAYBESETSTATE_PL(RXML_EVT_BEGIN,"dict",XML_PL_ST_EXPECTING_PL_DATA);
- if((action == RXML_EVT_END) && (strcasecmp(info,"array") == 0))
- return XML_STATE_PREAMBLE;
- return XML_STATE_ERROR;
+ /* either a new playlist, or end of playlist list */
+ MAYBESETSTATE_PL(RXML_EVT_BEGIN,"dict",XML_PL_ST_EXPECTING_PL_DATA);
+ if((action == RXML_EVT_END) && (strcasecmp(info,"array") == 0))
+ return XML_STATE_PREAMBLE;
+ return XML_STATE_ERROR;
case XML_PL_ST_EXPECTING_PL_DATA:
- /* either a key/data pair, or an array, signaling start of playlist
- * or the end of the dict (end of playlist data) */
- MAYBESETSTATE_PL(RXML_EVT_BEGIN,"key",XML_PL_ST_EXPECTING_PL_DATA);
- MAYBESETSTATE_PL(RXML_EVT_END,"key",XML_PL_ST_EXPECTING_PL_VALUE);
- MAYBESETSTATE_PL(RXML_EVT_END,"dict",XML_PL_ST_EXPECTING_PL);
- if(action == RXML_EVT_TEXT) {
- next_value=XML_PL_NEXT_VALUE_NONE;
- if(strcasecmp(info,"Name") == 0) {
- next_value = XML_PL_NEXT_VALUE_NAME;
- } else if(strcasecmp(info,"Playlist ID") == 0) {
- next_value = XML_PL_NEXT_VALUE_ID;
- } else if(strcasecmp(info,"Master") == 0) {
- /* No point adding the master library... we have one */
- dont_scan=1;
- }
- return XML_STATE_PLAYLISTS;
- }
- return XML_STATE_ERROR;
+ /* either a key/data pair, or an array, signaling start of playlist
+ * or the end of the dict (end of playlist data) */
+ MAYBESETSTATE_PL(RXML_EVT_BEGIN,"key",XML_PL_ST_EXPECTING_PL_DATA);
+ MAYBESETSTATE_PL(RXML_EVT_END,"key",XML_PL_ST_EXPECTING_PL_VALUE);
+ MAYBESETSTATE_PL(RXML_EVT_END,"dict",XML_PL_ST_EXPECTING_PL);
+ if(action == RXML_EVT_TEXT) {
+ next_value=XML_PL_NEXT_VALUE_NONE;
+ if(strcasecmp(info,"Name") == 0) {
+ next_value = XML_PL_NEXT_VALUE_NAME;
+ } else if(strcasecmp(info,"Playlist ID") == 0) {
+ next_value = XML_PL_NEXT_VALUE_ID;
+ } else if(strcasecmp(info,"Master") == 0) {
+ /* No point adding the master library... we have one */
+ dont_scan=1;
+ }
+ return XML_STATE_PLAYLISTS;
+ }
+ return XML_STATE_ERROR;
case XML_PL_ST_EXPECTING_PL_VALUE:
- /* any tag, value we are looking for, any close tag */
- if((action == RXML_EVT_BEGIN) && (strcasecmp(info,"array") == 0)) {
- /* we are about to get track list... must register the playlist */
- current_id=0;
- if(dont_scan == 0) {
- DPRINTF(E_DBG,L_SCAN,"Creating playlist for %s\n",current_name);
- /* delete the old one first */
- pm3u = db_fetch_playlist(scan_xml_file,native_plid);
- if(pm3u) {
- db_delete_playlist(pm3u->id);
- db_dispose_playlist(pm3u);
- }
- if(db_add_playlist(current_name,PL_STATICXML,NULL,scan_xml_file,
- native_plid,¤t_id) != DB_E_SUCCESS) {
- DPRINTF(E_LOG,L_SCAN,"err adding playlist %s\n",current_name);
- current_id=0;
- }
- }
- dont_scan=0;
- state=XML_PL_ST_EXPECTING_PL_TRACKLIST;
- return XML_STATE_PLAYLISTS;
- }
- if(action == RXML_EVT_BEGIN)
- return XML_STATE_PLAYLISTS;
- if(action == RXML_EVT_END) {
- state = XML_PL_ST_EXPECTING_PL_DATA;
- return XML_STATE_PLAYLISTS;
- }
- if(action == RXML_EVT_TEXT) {
- /* got the value we were hoping for */
- if(next_value == XML_PL_NEXT_VALUE_NAME) {
- if(current_name)
- free(current_name);
- current_name = strdup(info);
- /* disallow specific playlists */
- if(strcasecmp(current_name,"Party Shuffle") == 0) {
- dont_scan=1;
- }
- } else if(next_value == XML_PL_NEXT_VALUE_ID) {
- native_plid = atoi(info);
- }
- return XML_STATE_PLAYLISTS;
- }
- return XML_STATE_ERROR;
+ /* any tag, value we are looking for, any close tag */
+ if((action == RXML_EVT_BEGIN) && (strcasecmp(info,"array") == 0)) {
+ /* we are about to get track list... must register the playlist */
+ current_id=0;
+ if(dont_scan == 0) {
+ DPRINTF(E_DBG,L_SCAN,"Creating playlist for %s\n",current_name);
+ /* delete the old one first */
+ /* FIXME: Error handling */
+ pm3u = db_fetch_playlist(NULL,scan_xml_file,native_plid);
+ if(pm3u) {
+ db_delete_playlist(NULL,pm3u->id);
+ db_dispose_playlist(pm3u);
+ }
+ if(db_add_playlist(NULL,current_name,PL_STATICXML,NULL,
+ scan_xml_file,native_plid,
+ ¤t_id) != DB_E_SUCCESS)
+ {
+ DPRINTF(E_LOG,L_SCAN,"err adding playlist %s\n",current_name);
+ current_id=0;
+ }
+ }
+ dont_scan=0;
+ state=XML_PL_ST_EXPECTING_PL_TRACKLIST;
+ return XML_STATE_PLAYLISTS;
+ }
+ if(action == RXML_EVT_BEGIN)
+ return XML_STATE_PLAYLISTS;
+ if(action == RXML_EVT_END) {
+ state = XML_PL_ST_EXPECTING_PL_DATA;
+ return XML_STATE_PLAYLISTS;
+ }
+ if(action == RXML_EVT_TEXT) {
+ /* got the value we were hoping for */
+ if(next_value == XML_PL_NEXT_VALUE_NAME) {
+ if(current_name)
+ free(current_name);
+ current_name = strdup(info);
+ /* disallow specific playlists */
+ if(strcasecmp(current_name,"Party Shuffle") == 0) {
+ dont_scan=1;
+ }
+ } else if(next_value == XML_PL_NEXT_VALUE_ID) {
+ native_plid = atoi(info);
+ }
+ return XML_STATE_PLAYLISTS;
+ }
+ return XML_STATE_ERROR;
case XML_PL_ST_EXPECTING_PL_TRACKLIST:
- if((strcasecmp(info,"dict") == 0) || (strcasecmp(info,"key") == 0))
- return XML_STATE_PLAYLISTS;
- MAYBESETSTATE_PL(RXML_EVT_END,"array",XML_PL_ST_EXPECTING_PL_DATA);
+ if((strcasecmp(info,"dict") == 0) || (strcasecmp(info,"key") == 0))
+ return XML_STATE_PLAYLISTS;
+ MAYBESETSTATE_PL(RXML_EVT_END,"array",XML_PL_ST_EXPECTING_PL_DATA);
if(action == RXML_EVT_TEXT) {
- if(strcasecmp(info,"Track ID") != 0) {
- native_track_id = atoi(info);
- DPRINTF(E_DBG,L_SCAN,"Adding itunes track #%s\n",info);
- /* add it to the current playlist (current_id) */
- if(current_id && scan_xml_get_index(native_track_id, &track_id)) {
- db_add_playlist_item(current_id,track_id);
- }
- }
+ if(strcasecmp(info,"Track ID") != 0) {
+ native_track_id = atoi(info);
+ DPRINTF(E_DBG,L_SCAN,"Adding itunes track #%s\n",info);
+ /* add it to the current playlist (current_id) */
+ if(current_id && scan_xml_get_index(native_track_id, &track_id)) {
+ /* FIXME: Error handling */
+ db_add_playlist_item(NULL,current_id,track_id);
+ }
+ }
- return XML_STATE_PLAYLISTS;
- }
- return XML_STATE_PLAYLISTS;
+ return XML_STATE_PLAYLISTS;
+ }
+ return XML_STATE_PLAYLISTS;
default:
- return XML_STATE_ERROR;
+ return XML_STATE_ERROR;
}
-
+
return XML_STATE_PLAYLISTS;
}
diff --git a/src/xml-rpc.c b/src/xml-rpc.c
index 932eb467..92589dc4 100644
--- a/src/xml-rpc.c
+++ b/src/xml-rpc.c
@@ -27,7 +27,7 @@ typedef struct tag_xmlstack {
typedef struct tag_xmlstruct {
WS_CONNINFO *pwsc;
int stack_level;
- XMLSTACK stack;
+ XMLSTACK stack;
} XMLSTRUCT;
/* Forwards */
@@ -50,16 +50,16 @@ void xml_deinit(XMLSTRUCT *pxml);
*/
XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
XMLSTRUCT *pxml;
-
+
pxml=(XMLSTRUCT*)malloc(sizeof(XMLSTRUCT));
if(!pxml) {
DPRINTF(E_FATAL,L_XML,"Malloc error\n");
}
-
+
memset(pxml,0,sizeof(XMLSTRUCT));
-
+
pxml->pwsc = pwsc;
-
+
if(emit_header) {
ws_addresponseheader(pwsc,"Content-Type","text/xml; charset=utf-8");
ws_writefd(pwsc,"HTTP/1.0 200 OK\r\n");
@@ -67,7 +67,7 @@ XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
ws_writefd(pwsc,"");
}
-
+
return pxml;
}
@@ -75,16 +75,16 @@ XMLSTRUCT *xml_init(WS_CONNINFO *pwsc, int emit_header) {
* push a new term on the stack
*
* @param pxml xml struct obtained from xml_init
- * @param term next xlm section to start
+ * @param term next xlm section to start
*/
void xml_push(XMLSTRUCT *pxml, char *term) {
XMLSTACK *pstack;
-
+
pstack = (XMLSTACK *)malloc(sizeof(XMLSTACK));
pstack->next=pxml->stack.next;
pstack->tag=strdup(term);
pxml->stack.next=pstack;
-
+
pxml->stack_level++;
ws_writefd(pxml->pwsc,"<%s>",term);
@@ -97,19 +97,19 @@ void xml_push(XMLSTRUCT *pxml, char *term) {
*/
void xml_pop(XMLSTRUCT *pxml) {
XMLSTACK *pstack;
-
+
pstack=pxml->stack.next;
if(!pstack) {
DPRINTF(E_LOG,L_XML,"xml_pop: tried to pop an empty stack\n");
return;
}
-
+
pxml->stack.next = pstack->next;
-
+
ws_writefd(pxml->pwsc,"%s>",pstack->tag);
free(pstack->tag);
free(pstack);
-
+
pxml->stack_level--;
}
@@ -143,11 +143,11 @@ void xml_output(XMLSTRUCT *pxml, char *section, char *fmt, ...) {
*/
void xml_deinit(XMLSTRUCT *pxml) {
XMLSTACK *pstack;
-
+
if(pxml->stack.next) {
DPRINTF(E_LOG,L_XML,"xml_deinit: entries still on stack (%s)\n",pxml->stack.next->tag);
}
-
+
while((pstack=pxml->stack.next)) {
pxml->stack.next=pstack->next;
free(pstack->tag);
@@ -187,18 +187,18 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
WS_CONNINFO *pci;
SCAN_STATUS *pss;
WSTHREADENUM wste;
-
+ int count;
XMLSTRUCT *pxml;
-
+
pxml=xml_init(pwsc,1);
xml_push(pxml,"status");
xml_push(pxml,"service_status");
xml_push(pxml,"service");
-
+
xml_output(pxml,"name","Rendezvous");
-
+
#ifndef WITHOUT_MDNS
if(config.use_mdns) {
xml_output(pxml,"status",rend_running() ? "Stopped" : "Running"); /* ??? */
@@ -222,7 +222,7 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
xml_pop(pxml); /* service */
xml_pop(pxml); /* service_status */
-
+
xml_push(pxml,"thread_status");
pci = ws_thread_enum_first(config.server,&wste);
@@ -237,11 +237,11 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
}
pci=ws_thread_enum_next(config.server,&wste);
}
-
+
xml_pop(pxml); /* thread_status */
xml_push(pxml,"statistics");
-
+
r_secs=time(NULL)-config.stats.start_time;
r_days=r_secs/(3600 * 24);
@@ -254,36 +254,37 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
r_secs -= 60 * r_mins;
memset(buf,0x0,sizeof(buf));
- if(r_days)
- sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
- r_days == 1 ? "" : "s");
+ if(r_days)
+ sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
+ r_days == 1 ? "" : "s");
- if(r_days || r_hours)
- sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours,
- r_hours == 1 ? "" : "s");
+ if(r_days || r_hours)
+ sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours,
+ r_hours == 1 ? "" : "s");
if(r_days || r_hours || r_mins)
- sprintf((char*)&buf[strlen(buf)],"%d minute%s, ", r_mins,
- r_mins == 1 ? "" : "s");
+ sprintf((char*)&buf[strlen(buf)],"%d minute%s, ", r_mins,
+ r_mins == 1 ? "" : "s");
sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs,
- r_secs == 1 ? "" : "s");
-
+ r_secs == 1 ? "" : "s");
+
xml_push(pxml,"stat");
xml_output(pxml,"name","Uptime");
xml_output(pxml,"value","%s",buf);
xml_pop(pxml); /* stat */
-
+
xml_push(pxml,"stat");
xml_output(pxml,"name","Songs");
- xml_output(pxml,"value","%d",db_get_song_count());
+ db_get_song_count(NULL,&count);
+ xml_output(pxml,"value","%d",count);
xml_pop(pxml); /* stat */
-
+
xml_push(pxml,"stat");
xml_output(pxml,"name","Songs Served");
xml_output(pxml,"value","%d",config.stats.songs_served);
xml_pop(pxml); /* stat */
-
+
xml_pop(pxml); /* statistics */
xml_pop(pxml); /* status */