abstract database somewhat, improve error handling

This commit is contained in:
Ron Pedde 2006-01-04 20:30:44 +00:00
parent 24f40d7956
commit 86b45a028e
15 changed files with 3127 additions and 2842 deletions

View File

@ -30,7 +30,7 @@ MUSEPACKSRC=scan-mpc.c
endif endif
if COND_SQLITE 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 endif
wavstreamer_SOURCES = wavstreamer.c wavstreamer_SOURCES = wavstreamer.c

View File

@ -868,6 +868,7 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
char buf[256]; char buf[256];
int r_days, r_hours, r_mins, r_secs; int r_days, r_hours, r_mins, r_secs;
int scanning; int scanning;
int song_count;
ws_writefd(pwsc,"<table><tr><th align=\"left\">Service</th>"); ws_writefd(pwsc,"<table><tr><th align=\"left\">Service</th>");
ws_writefd(pwsc,"<th align=\"left\">Status</th><th align=\"left\">Control</th></tr>\n"); ws_writefd(pwsc,"<th align=\"left\">Status</th><th align=\"left\">Control</th></tr>\n");
@ -945,7 +946,8 @@ void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg) {
ws_writefd(pwsc,"<tr>\n"); ws_writefd(pwsc,"<tr>\n");
ws_writefd(pwsc," <th>Songs</th>\n"); ws_writefd(pwsc," <th>Songs</th>\n");
ws_writefd(pwsc," <td>%d</td>\n",db_get_song_count()); db_get_song_count(NULL,&song_count);
ws_writefd(pwsc," <td>%d</td>\n",song_count);
ws_writefd(pwsc,"</tr>\n"); ws_writefd(pwsc,"</tr>\n");
ws_writefd(pwsc,"<tr>\n"); ws_writefd(pwsc,"<tr>\n");
@ -1269,15 +1271,15 @@ void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...) {
} }
if(pfirst) { if(pfirst) {
/* just update */ /* just update */
if(pfirst->what) { if(pfirst->what) {
free(pfirst->what); free(pfirst->what);
} }
pfirst->what=newmsg; pfirst->what=newmsg;
pfirst->session=session; pfirst->session=session;
} else { } else {
if(newmsg) if(newmsg)
free(newmsg); free(newmsg);
} }
ws_unlock_local_storage(pwsc); ws_unlock_local_storage(pwsc);

View File

@ -27,6 +27,7 @@
#include <pthread.h> #include <pthread.h>
#include <stdio.h> #include <stdio.h>
#include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -34,7 +35,7 @@
#include "err.h" #include "err.h"
#include "mp3-scanner.h" #include "mp3-scanner.h"
#include "dbs-sqlite.h" #include "db-sql.h"
#define DB_VERSION 1 #define DB_VERSION 1
#define MAYBEFREE(a) { if((a)) free((a)); }; #define MAYBEFREE(a) { if((a)) free((a)); };
@ -42,61 +43,59 @@
/** pointers to database-specific functions */ /** pointers to database-specific functions */
typedef struct tag_db_functions { typedef struct tag_db_functions {
char *name; char *name;
int(*dbs_open)(char *); int(*dbs_open)(char **, char *);
int(*dbs_init)(int); int(*dbs_init)(int);
int(*dbs_deinit)(void); int(*dbs_deinit)(void);
int(*dbs_add)(MP3FILE*); int(*dbs_add)(char **, MP3FILE*);
int(*dbs_add_playlist)(char *, int, char *,char *, int, int *); int(*dbs_add_playlist)(char **, char *, int, char *,char *, int, int *);
int(*dbs_add_playlist_item)(int, int); int(*dbs_add_playlist_item)(char **, int, int);
int(*dbs_delete_playlist)(int); int(*dbs_delete_playlist)(char **, int);
int(*dbs_delete_playlist_item)(int, int); int(*dbs_delete_playlist_item)(char **, int, int);
int(*dbs_edit_playlist)(int, char*, char*); int(*dbs_edit_playlist)(char **, int, char*, char*);
int(*dbs_enum_start)(DBQUERYINFO *); int(*dbs_enum_start)(char **, DBQUERYINFO *);
int(*dbs_enum_size)(DBQUERYINFO *, int *); int(*dbs_enum_size)(char **, DBQUERYINFO *, int *, int *);
int(*dbs_enum_fetch)(DBQUERYINFO *, unsigned char **); int(*dbs_enum_fetch)(char **, DBQUERYINFO *, int *, unsigned char **);
int(*dbs_enum_reset)(DBQUERYINFO *); int(*dbs_enum_reset)(char **, DBQUERYINFO *);
int(*dbs_enum_end)(void); int(*dbs_enum_end)(char **);
int(*dbs_start_scan)(void); int(*dbs_start_scan)(void);
int(*dbs_end_song_scan)(void); int(*dbs_end_song_scan)(void);
int(*dbs_end_scan)(void); int(*dbs_end_scan)(void);
int(*dbs_get_count)(CountType_t); int(*dbs_get_count)(char **, int *, CountType_t);
MP3FILE*(*dbs_fetch_item)(int); MP3FILE*(*dbs_fetch_item)(char **, int);
MP3FILE*(*dbs_fetch_path)(char *,int); MP3FILE*(*dbs_fetch_path)(char **, char *,int);
M3UFILE*(*dbs_fetch_playlist)(char *, int); M3UFILE*(*dbs_fetch_playlist)(char **, char *, int);
void(*dbs_dispose_item)(MP3FILE*); void(*dbs_dispose_item)(MP3FILE*);
void(*dbs_dispose_playlist)(M3UFILE*); void(*dbs_dispose_playlist)(M3UFILE*);
}DB_FUNCTIONS; }DB_FUNCTIONS;
/** All supported backend databases, and pointers to the db specific implementations */ /** All supported backend databases, and pointers to the db specific implementations */
DB_FUNCTIONS db_functions[] = { DB_FUNCTIONS db_functions[] = {
#ifdef HAVE_LIBSQLITE
{ {
"sqlite", "sql",
db_sqlite_open, db_sql_open,
db_sqlite_init, db_sql_init,
db_sqlite_deinit, db_sql_deinit,
db_sqlite_add, db_sql_add,
db_sqlite_add_playlist, db_sql_add_playlist,
db_sqlite_add_playlist_item, db_sql_add_playlist_item,
db_sqlite_delete_playlist, db_sql_delete_playlist,
db_sqlite_delete_playlist_item, db_sql_delete_playlist_item,
db_sqlite_edit_playlist, db_sql_edit_playlist,
db_sqlite_enum_start, db_sql_enum_start,
db_sqlite_enum_size, db_sql_enum_size,
db_sqlite_enum_fetch, db_sql_enum_fetch,
db_sqlite_enum_reset, db_sql_enum_reset,
db_sqlite_enum_end, db_sql_enum_end,
db_sqlite_start_scan, db_sql_start_scan,
db_sqlite_end_song_scan, db_sql_end_song_scan,
db_sqlite_end_scan, db_sql_end_scan,
db_sqlite_get_count, db_sql_get_count,
db_sqlite_fetch_item, db_sql_fetch_item,
db_sqlite_fetch_path, db_sql_fetch_path,
db_sqlite_fetch_playlist, db_sql_fetch_playlist,
db_sqlite_dispose_item, db_sql_dispose_item,
db_sqlite_dispose_playlist db_sql_dispose_playlist
}, },
#endif
{ NULL,NULL } { NULL,NULL }
}; };
@ -210,43 +209,43 @@ DAAP_ITEMS taglist[] = {
}; };
/** map the string names specified in the meta= tag to bit numbers */ /** map the string names specified in the meta= tag to bit numbers */
static METAMAP db_metamap[] = { static METAMAP db_metamap[] = {
{ "dmap.itemid", metaItemId }, { "dmap.itemid", metaItemId },
{ "dmap.itemname", metaItemName }, { "dmap.itemname", metaItemName },
{ "dmap.itemkind", metaItemKind }, { "dmap.itemkind", metaItemKind },
{ "dmap.persistentid", metaPersistentId }, { "dmap.persistentid", metaPersistentId },
{ "dmap.containeritemid", metaContainerItemId }, { "dmap.containeritemid", metaContainerItemId },
{ "dmap.parentcontainerid", metaParentContainerId }, { "dmap.parentcontainerid", metaParentContainerId },
/* end generics */ /* end generics */
{ "daap.songalbum", metaSongAlbum }, { "daap.songalbum", metaSongAlbum },
{ "daap.songartist", metaSongArtist }, { "daap.songartist", metaSongArtist },
{ "daap.songbitrate", metaSongBitRate }, { "daap.songbitrate", metaSongBitRate },
{ "daap.songbeatsperminute", metaSongBPM }, { "daap.songbeatsperminute", metaSongBPM },
{ "daap.songcomment", metaSongComment }, { "daap.songcomment", metaSongComment },
{ "daap.songcompilation", metaSongCompilation }, { "daap.songcompilation", metaSongCompilation },
{ "daap.songcomposer", metaSongComposer }, { "daap.songcomposer", metaSongComposer },
{ "daap.songdatakind", metaSongDataKind }, { "daap.songdatakind", metaSongDataKind },
{ "daap.songdataurl", metaSongDataURL }, { "daap.songdataurl", metaSongDataURL },
{ "daap.songdateadded", metaSongDateAdded }, { "daap.songdateadded", metaSongDateAdded },
{ "daap.songdatemodified", metaSongDateModified }, { "daap.songdatemodified", metaSongDateModified },
{ "daap.songdescription", metaSongDescription }, { "daap.songdescription", metaSongDescription },
{ "daap.songdisabled", metaSongDisabled }, { "daap.songdisabled", metaSongDisabled },
{ "daap.songdisccount", metaSongDiscCount }, { "daap.songdisccount", metaSongDiscCount },
{ "daap.songdiscnumber", metaSongDiscNumber }, { "daap.songdiscnumber", metaSongDiscNumber },
{ "daap.songeqpreset", metaSongEqPreset }, { "daap.songeqpreset", metaSongEqPreset },
{ "daap.songformat", metaSongFormat }, { "daap.songformat", metaSongFormat },
{ "daap.songgenre", metaSongGenre }, { "daap.songgenre", metaSongGenre },
{ "daap.songgrouping", metaSongGrouping }, { "daap.songgrouping", metaSongGrouping },
{ "daap.songrelativevolume", metaSongRelativeVolume }, { "daap.songrelativevolume", metaSongRelativeVolume },
{ "daap.songsamplerate", metaSongSampleRate }, { "daap.songsamplerate", metaSongSampleRate },
{ "daap.songsize", metaSongSize }, { "daap.songsize", metaSongSize },
{ "daap.songstarttime", metaSongStartTime }, { "daap.songstarttime", metaSongStartTime },
{ "daap.songstoptime", metaSongStopTime }, { "daap.songstoptime", metaSongStopTime },
{ "daap.songtime", metaSongTime }, { "daap.songtime", metaSongTime },
{ "daap.songtrackcount", metaSongTrackCount }, { "daap.songtrackcount", metaSongTrackCount },
{ "daap.songtracknumber", metaSongTrackNumber }, { "daap.songtracknumber", metaSongTrackNumber },
{ "daap.songuserrating", metaSongUserRating }, { "daap.songuserrating", metaSongUserRating },
{ "daap.songyear", metaSongYear }, { "daap.songyear", metaSongYear },
/* iTunes 4.5+ (forgot exactly when) */ /* iTunes 4.5+ (forgot exactly when) */
{ "daap.songcodectype", metaSongCodecType }, { "daap.songcodectype", metaSongCodecType },
{ "daap.songcodecsubtype", metaSongCodecSubType }, { "daap.songcodecsubtype", metaSongCodecSubType },
@ -261,9 +260,20 @@ static METAMAP db_metamap[] = {
/* mt-daapd specific */ /* mt-daapd specific */
{ "org.mt-daapd.smart-playlist-spec", metaMPlaylistSpec }, { "org.mt-daapd.smart-playlist-spec", metaMPlaylistSpec },
{ "org.mt-daapd.playlist-type", metaMPlaylistType }, { "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 */ /* Globals */
static DB_FUNCTIONS *db_current=&db_functions[0]; /**< current database backend */ 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 * \param meta meta string variable from GET request
*/ */
MetaField_t db_encode_meta(char *meta) { MetaField_t db_encode_meta(char *meta) {
MetaField_t bits = 0; MetaField_t bits = 0;
char *start; char *start;
char *end; char *end;
METAMAP *m; METAMAP *m;
for(start = meta ; *start ; start = end) { for(start = meta ; *start ; start = end) {
int len; int len;
if(0 == (end = strchr(start, ','))) if(0 == (end = strchr(start, ',')))
end = start + strlen(start); end = start + strlen(start);
len = end - start; len = end - start;
if(*end != 0) if(*end != 0)
end++; end++;
for(m = db_metamap ; m->tag ; ++m) for(m = db_metamap ; m->tag ; ++m)
if(!strncmp(m->tag, start, len)) if(!strncmp(m->tag, start, len))
break; break;
if(m->tag) if(m->tag)
bits |= (((MetaField_t) 1) << m->bit); bits |= (((MetaField_t) 1) << m->bit);
else else
DPRINTF(E_WARN,L_DAAP,"Unknown meta code: %.*s\n", len, start); DPRINTF(E_WARN,L_DAAP,"Unknown meta code: %.*s\n", len, start);
} }
DPRINTF(E_DBG, L_DAAP, "meta codes: %llu\n", bits); DPRINTF(E_DBG, L_DAAP, "meta codes: %llu\n", bits);
@ -339,7 +349,7 @@ void db_readlock(void) {
int err; int err;
if((err=pthread_rwlock_rdlock(&db_rwlock))) { 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));
} }
} }
@ -352,7 +362,7 @@ void db_writelock(void) {
int err; int err;
if((err=pthread_rwlock_wrlock(&db_rwlock))) { 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));
} }
} }
@ -365,6 +375,22 @@ int db_unlock(void) {
return pthread_rwlock_unlock(&db_rwlock); 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) * 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); DPRINTF(E_DBG,L_DB,"Setting backend database to %s\n",type);
if(!db_functions[0].name) { 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]; db_current=&db_functions[0];
while((db_current->name) && (strcasecmp(db_current->name,type))) { while((db_current->name) && (strcasecmp(db_current->name,type))) {
db_current++; db_current++;
} }
if(!db_current->name) { if(!db_current->name) {
DPRINTF(E_WARN,L_DB,"Could not find db backend %s. Aborting.\n",type); DPRINTF(E_WARN,L_DB,"Could not find db backend %s. Aborting.\n",type);
return -1; return -1;
} }
DPRINTF(E_DBG,L_DB,"Backend database set\n"); 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) * \param parameters This is backend-specific (mysql, sqlite, etc)
*/ */
int db_open(char *parameters) { int db_open(char **pe, char *parameters) {
int result; int result;
DPRINTF(E_DBG,L_DB,"Opening database\n"); DPRINTF(E_DBG,L_DB,"Opening database\n");
if(pthread_once(&db_initlock,db_init_once)) 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); DPRINTF(E_DBG,L_DB,"Results: %d\n",result);
return result; return result;
@ -463,13 +489,13 @@ int db_scanning(void) {
/** /**
* add (or update) a file * add (or update) a file
*/ */
int db_add(MP3FILE *pmp3) { int db_add(char **pe, MP3FILE *pmp3) {
int retval; int retval;
db_writelock(); db_writelock();
db_utf8_validate(pmp3); db_utf8_validate(pmp3);
db_trim_strings(pmp3); db_trim_strings(pmp3);
retval=db_current->dbs_add(pmp3); retval=db_current->dbs_add(pe,pmp3);
db_revision_no++; db_revision_no++;
db_unlock(); db_unlock();
@ -485,13 +511,13 @@ int db_add(MP3FILE *pmp3) {
* \param playlistid returns the id of the playlist created * \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; int retval;
db_writelock(); 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) if(retval == DB_E_SUCCESS)
db_revision_no++; db_revision_no++;
db_unlock(); db_unlock();
return retval; 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 * \param songid song to add to playlist
* \returns 0 on success, DB_E_ code otherwise * \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; int retval;
db_writelock(); 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) if(retval == DB_E_SUCCESS)
db_revision_no++; db_revision_no++;
db_unlock(); db_unlock();
return retval; return retval;
@ -522,13 +548,13 @@ int db_add_playlist_item(int playlistid, int songid) {
* \param playlistid id of the playlist to delete * \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; int retval;
db_writelock(); db_writelock();
retval=db_current->dbs_delete_playlist(playlistid); retval=db_current->dbs_delete_playlist(pe,playlistid);
if(retval == DB_E_SUCCESS) if(retval == DB_E_SUCCESS)
db_revision_no++; db_revision_no++;
db_unlock(); db_unlock();
return retval; return retval;
@ -541,13 +567,13 @@ int db_delete_playlist(int playlistid) {
* \param songid id of the song 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; int retval;
db_writelock(); 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) if(retval == DB_E_SUCCESS)
db_revision_no++; db_revision_no++;
db_unlock(); db_unlock();
return retval; return retval;
@ -560,12 +586,12 @@ int db_delete_playlist_item(int playlistid, int songid) {
* @param name new name of playlist * @param name new name of playlist
* @param clause new where clause * @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; int retval;
db_writelock(); db_writelock();
retval = db_current->dbs_edit_playlist(id, name, clause); retval = db_current->dbs_edit_playlist(pe, id, name, clause);
db_unlock(); db_unlock();
return retval; return retval;
} }
@ -577,15 +603,15 @@ int db_edit_playlist(int id, char *name, char *clause) {
* \param pinfo pointer to DBQUERYINFO struction * \param pinfo pointer to DBQUERYINFO struction
* \returns 0 on success, -1 on failure * \returns 0 on success, -1 on failure
*/ */
int db_enum_start(DBQUERYINFO *pinfo) { int db_enum_start(char **pe, DBQUERYINFO *pinfo) {
int retval; int retval;
db_writelock(); db_writelock();
retval=db_current->dbs_enum_start(pinfo); retval=db_current->dbs_enum_start(pe, pinfo);
if(retval) { if(retval) {
db_unlock(); db_unlock();
return retval; return retval;
} }
return 0; return 0;
@ -596,8 +622,8 @@ int db_enum_start(DBQUERYINFO *pinfo) {
* db_<dbase>_enum_reset, so it should be positioned at the head * db_<dbase>_enum_reset, so it should be positioned at the head
* of the list of returned items. * of the list of returned items.
*/ */
int db_enum_size(DBQUERYINFO *pinfo, int *count) { int db_enum_size(char **pe, DBQUERYINFO *pinfo, int *size, int *count) {
return db_current->dbs_enum_size(pinfo,count); return db_current->dbs_enum_size(pe,pinfo,size,count);
} }
@ -609,25 +635,27 @@ int db_enum_size(DBQUERYINFO *pinfo, int *count) {
* \param plen length of the dmap item returned * \param plen length of the dmap item returned
* \returns dmap item * \returns dmap item
*/ */
int db_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap) { int db_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size,
return db_current->dbs_enum_fetch(pinfo,pdmap); unsigned char **pdmap)
{
return db_current->dbs_enum_fetch(pe,pinfo,size,pdmap);
} }
/** /**
* reset the enum, without coming out the the db_writelock * reset the enum, without coming out the the db_writelock
*/ */
int db_enum_reset(DBQUERYINFO *pinfo) { int db_enum_reset(char **pe, DBQUERYINFO *pinfo) {
return db_current->dbs_enum_reset(pinfo); return db_current->dbs_enum_reset(pe,pinfo);
} }
/** /**
* finish the enumeration * finish the enumeration
*/ */
int db_enum_end(void) { int db_enum_end(char **pe) {
int retval; int retval;
retval=db_current->dbs_enum_end(); retval=db_current->dbs_enum_end(pe);
db_unlock(); db_unlock();
return retval; return retval;
} }
@ -639,31 +667,31 @@ int db_enum_end(void) {
* *
* \param id id of the item to get details for * \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; MP3FILE *retval;
db_readlock(); db_readlock();
retval=db_current->dbs_fetch_item(id); retval=db_current->dbs_fetch_item(pe, id);
db_unlock(); db_unlock();
return retval; return retval;
} }
MP3FILE *db_fetch_path(char *path,int index) { MP3FILE *db_fetch_path(char **pe, char *path,int index) {
MP3FILE *retval; MP3FILE *retval;
db_readlock(); db_readlock();
retval=db_current->dbs_fetch_path(path, index); retval=db_current->dbs_fetch_path(pe,path, index);
db_unlock(); db_unlock();
return retval; return retval;
} }
M3UFILE *db_fetch_playlist(char *path, int index) { M3UFILE *db_fetch_playlist(char **pe, char *path, int index) {
M3UFILE *retval; M3UFILE *retval;
db_readlock(); db_readlock();
retval=db_current->dbs_fetch_playlist(path,index); retval=db_current->dbs_fetch_playlist(pe,path,index);
db_unlock(); db_unlock();
return retval; return retval;
@ -709,11 +737,11 @@ void db_dispose_playlist(M3UFILE *pm3u) {
return db_current->dbs_dispose_playlist(pm3u); return db_current->dbs_dispose_playlist(pm3u);
} }
int db_get_count(CountType_t type) { int db_get_count(char **pe, int *count, CountType_t type) {
int retval; int retval;
db_readlock(); db_readlock();
retval=db_current->dbs_get_count(type); retval=db_current->dbs_get_count(pe,count,type);
db_unlock(); db_unlock();
return retval; return retval;
@ -723,12 +751,12 @@ int db_get_count(CountType_t type) {
/* /*
* FIXME: clearly a stub * FIXME: clearly a stub
*/ */
int db_get_song_count() { int db_get_song_count(char **pe, int *count) {
return db_get_count(countSongs); return db_get_count(pe, count, countSongs);
} }
int db_get_playlist_count() { int db_get_playlist_count(char **pe, int *count) {
return db_get_count(countPlaylists); return db_get_count(pe, count, countPlaylists);
} }
@ -836,7 +864,7 @@ int db_dmap_add_string(unsigned char *where, char *tag, char *value) {
* \param size how much data to cram in there * \param size how much data to cram in there
*/ */
int db_dmap_add_literal(unsigned char *where, char *tag, int db_dmap_add_literal(unsigned char *where, char *tag,
char *value, int size) { char *value, int size) {
/* tag */ /* tag */
memcpy(where,tag,4); memcpy(where,tag,4);
@ -902,7 +930,7 @@ void db_utf8_validate(MP3FILE *pmp3) {
is_invalid |= db_utf8_validate_string(pmp3->url); is_invalid |= db_utf8_validate_string(pmp3->url);
if(is_invalid) { 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; int retval=0;
if(!string) if(!string)
return 0; return 0;
while(*current) { while(*current) {
if(!((*current) & 0x80)) { if(!((*current) & 0x80)) {
current++; current++;
} else { } else {
run=0; run=0;
/* it's a lead utf-8 character */ /* it's a lead utf-8 character */
if((*current & 0xE0) == 0xC0) run=1; if((*current & 0xE0) == 0xC0) run=1;
if((*current & 0xF0) == 0xE0) run=2; if((*current & 0xF0) == 0xE0) run=2;
if((*current & 0xF8) == 0xF0) run=3; if((*current & 0xF8) == 0xF0) run=3;
if(!run) { if(!run) {
/* high bit set, but invalid */ /* high bit set, but invalid */
*current++='?'; *current++='?';
retval=1; retval=1;
} else { } else {
r_current=0; r_current=0;
while((r_current != run) && (*(current + r_current + 1)) && while((r_current != run) && (*(current + r_current + 1)) &&
((*(current + r_current + 1) & 0xC0) == 0x80)) { ((*(current + r_current + 1) & 0xC0) == 0x80)) {
r_current++; r_current++;
} }
if(r_current != run) { if(r_current != run) {
*current++ = '?'; *current++ = '?';
retval=1; retval=1;
} else { } else {
current += (1 + run); current += (1 + run);
} }
} }
} }
} }
return retval; return retval;
@ -986,9 +1014,9 @@ void db_trim_strings(MP3FILE *pmp3) {
*/ */
void db_trim_string(char *string) { void db_trim_string(char *string) {
if(!string) if(!string)
return; return;
while(strlen(string) && (string[strlen(string) - 1] == ' ')) while(strlen(string) && (string[strlen(string) - 1] == ' '))
string[strlen(string) - 1] = '\0'; string[strlen(string) - 1] = '\0';
} }

View File

@ -123,7 +123,7 @@ typedef struct tag_dbqueryinfo {
} DBQUERYINFO; } DBQUERYINFO;
typedef struct { typedef struct {
const char* tag; const char* tag;
MetaFieldName_t bit; MetaFieldName_t bit;
} METAMAP; } METAMAP;
@ -137,34 +137,36 @@ extern DAAP_ITEMS taglist[];
extern int db_set_backend(char *type); 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_init(int reload);
extern int db_deinit(void); extern int db_deinit(void);
extern int db_revision(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_start(char **pe, DBQUERYINFO *pinfo);
extern int db_enum_size(DBQUERYINFO *pinfo, int *count); extern int db_enum_size(char **pe, DBQUERYINFO *pinfo, int *count, int *total_size);
extern int db_enum_fetch(DBQUERYINFO *pinfo, unsigned char **pdmap); extern int db_enum_fetch(char **pe, DBQUERYINFO *pinfo, int *size, unsigned char **pdmap);
extern int db_enum_reset(DBQUERYINFO *pinfo); extern int db_enum_reset(char **pe, DBQUERYINFO *pinfo);
extern int db_enum_end(void); extern int db_enum_end(char **pe);
extern int db_start_scan(void); extern int db_start_scan(void);
extern int db_end_song_scan(void); extern int db_end_song_scan(void);
extern int db_end_scan(void); extern int db_end_scan(void);
extern int db_exists(char *path); extern int db_exists(char *path);
extern int db_scanning(void); 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(char **pe, 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_add_playlist_item(char **pe, int playlistid, int songid);
extern int db_edit_playlist(int id, char *name, char *clause); extern int db_edit_playlist(char **pe, int id, char *name, char *clause);
extern int db_delete_playlist(int playlistid); extern int db_delete_playlist(char **pe, int playlistid);
extern int db_delete_playlist_item(int playlistid, int songid); extern int db_delete_playlist_item(char **pe, int playlistid, int songid);
extern MP3FILE *db_fetch_item(int id); extern void db_get_error(char **pe, int err, ...);
extern MP3FILE *db_fetch_path(char *path, int index);
extern M3UFILE *db_fetch_playlist(char *path, int index); 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 */ /* 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... /* Holdover functions from old db interface...
* should these be removed? Refactored? * 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_item(MP3FILE *pmp3);
extern void db_dispose_playlist(M3UFILE *pm3u); extern void db_dispose_playlist(M3UFILE *pm3u);

456
src/db-sql-sqlite2.c Normal file
View File

@ -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 <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite.h>
#include <unistd.h>
#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";

42
src/db-sql-sqlite2.h Normal file
View File

@ -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_ */

1636
src/db-sql.c Normal file

File diff suppressed because it is too large Load Diff

114
src/db-sql.h Normal file
View File

@ -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_ */

File diff suppressed because it is too large Load Diff

View File

@ -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_ */

View File

@ -151,7 +151,7 @@ void daap_handler(WS_CONNINFO *pwsc) {
/* nm... backing this out. Really do need a "quirks" mode /* nm... backing this out. Really do need a "quirks" mode
pwsc->close=0; pwsc->close=0;
if(ws_testrequestheader(pwsc,"Connection","Close")) { if(ws_testrequestheader(pwsc,"Connection","Close")) {
pwsc->close = 1; pwsc->close = 1;
} }
*/ */
@ -633,7 +633,8 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
offset=(off_t)atol(ws_getrequestheader(pwsc,"range") + 6); 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) { if(!pmp3) {
DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item); DPRINTF(E_LOG,L_DAAP|L_WS|L_DB,"Could not find requested item %lu\n",item);
ws_returnerror(pwsc,404,"File Not Found"); 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, file_ptr = server_side_convert_open(pmp3->path,
offset, offset,
pmp3->song_length, pmp3->song_length,
pmp3->codectype); pmp3->codectype);
if (file_ptr) { if (file_ptr) {
file_fd = fileno(file_ptr); file_fd = fileno(file_ptr);
} else { } else {
@ -836,7 +837,8 @@ void dispatch_addplaylistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
while((token=strsep((char**)&current,","))) { while((token=strsep((char**)&current,","))) {
if(token) { if(token) {
db_add_playlist_item(pqi->playlist_id,atoi(token)); /* FIXME: error handling */
db_add_playlist_item(NULL,pqi->playlist_id,atoi(token));
} }
} }
@ -869,7 +871,8 @@ void dispatch_deleteplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
return; 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 */ /* success(ish)... spool out a dmap block */
current = playlist_response; current = playlist_response;
@ -905,7 +908,8 @@ void dispatch_deleteplaylistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
while((token=strsep((char**)&current,","))) { while((token=strsep((char**)&current,","))) {
if(token) { if(token) {
db_delete_playlist_item(pqi->playlist_id,atoi(token)); /* FIXME: Error handling */
db_delete_playlist_item(NULL,pqi->playlist_id,atoi(token));
} }
} }
@ -946,7 +950,8 @@ void dispatch_addplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
name=ws_getvar(pwsc,"dmap.itemname"); name=ws_getvar(pwsc,"dmap.itemname");
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec"); 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) { if(retval != DB_E_SUCCESS) {
DPRINTF(E_LOG,L_DAAP,"error adding playlist. aborting\n"); DPRINTF(E_LOG,L_DAAP,"error adding playlist. aborting\n");
ws_returnerror(pwsc,500,"error adding playlist"); ws_returnerror(pwsc,500,"error adding playlist");
@ -990,7 +995,8 @@ void dispatch_editplaylist(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec"); query=ws_getvar(pwsc,"org.mt-daapd.smart-playlist-spec");
id=atoi(ws_getvar(pwsc,"dmap.itemid")); 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) { if(retval != DB_E_SUCCESS) {
DPRINTF(E_LOG,L_DAAP,"error editing playlist."); DPRINTF(E_LOG,L_DAAP,"error editing playlist.");
ws_returnerror(pwsc,500,"Error editing playlist"); ws_returnerror(pwsc,500,"Error editing playlist");
@ -1031,13 +1037,16 @@ void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
pqi->query_type = queryTypePlaylistItems; pqi->query_type = queryTypePlaylistItems;
pqi->index_type=indexTypeNone; 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"); DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!"); ws_returnerror(pwsc,500,"Internal server error: out of memory!");
return; 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); DPRINTF(E_DBG,L_DAAP,"Item enum: got %d songs, dmap size: %d\n",song_count,list_length);
@ -1051,7 +1060,9 @@ void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
dispatch_output_start(pwsc,pqi,61+list_length); dispatch_output_start(pwsc,pqi,61+list_length);
dispatch_output_write(pwsc,pqi,items_response,61); 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); DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length); dispatch_output_write(pwsc,pqi,block,list_length);
free(block); free(block);
@ -1059,7 +1070,7 @@ void dispatch_playlistitems(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n"); DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
db_enum_end(); db_enum_end(NULL);
dispatch_output_end(pwsc,pqi); dispatch_output_end(pwsc,pqi);
return; return;
@ -1095,7 +1106,7 @@ void dispatch_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
pqi->index_type = indexTypeNone; 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"); DPRINTF(E_LOG,L_DAAP|L_BROW,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!\n"); ws_returnerror(pwsc,500,"Internal server error: out of memory!\n");
return; return;
@ -1103,7 +1114,8 @@ void dispatch_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_DBG,L_DAAP|L_BROW,"Getting enum size.\n"); 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", DPRINTF(E_DBG,L_DAAP|L_BROW,"Item enum: got %d items, dmap size: %d\n",
item_count,list_length); item_count,list_length);
@ -1117,7 +1129,9 @@ void dispatch_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
dispatch_output_start(pwsc,pqi,52+list_length); dispatch_output_start(pwsc,pqi,52+list_length);
dispatch_output_write(pwsc,pqi,browse_response,52); 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); DPRINTF(E_SPAM,L_DAAP|L_BROW,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length); dispatch_output_write(pwsc,pqi,block,list_length);
free(block); free(block);
@ -1125,7 +1139,7 @@ void dispatch_browse(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_DBG,L_DAAP|L_BROW,"Done enumerating\n"); DPRINTF(E_DBG,L_DAAP|L_BROW,"Done enumerating\n");
db_enum_end(); db_enum_end(NULL);
dispatch_output_end(pwsc,pqi); dispatch_output_end(pwsc,pqi);
return; return;
@ -1151,13 +1165,14 @@ void dispatch_playlists(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
pqi->query_type = queryTypePlaylists; pqi->query_type = queryTypePlaylists;
pqi->index_type = indexTypeNone; 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"); DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!\n"); ws_returnerror(pwsc,500,"Internal server error: out of memory!\n");
return; 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); DPRINTF(E_DBG,L_DAAP,"Item enum: got %d playlists, dmap size: %d\n",pl_count,list_length);
@ -1171,7 +1186,9 @@ void dispatch_playlists(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
dispatch_output_start(pwsc,pqi,61+list_length); dispatch_output_start(pwsc,pqi,61+list_length);
dispatch_output_write(pwsc,pqi,playlist_response,61); 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); DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length); dispatch_output_write(pwsc,pqi,block,list_length);
free(block); free(block);
@ -1179,7 +1196,7 @@ void dispatch_playlists(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n"); DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
db_enum_end(); db_enum_end(NULL);
dispatch_output_end(pwsc,pqi); dispatch_output_end(pwsc,pqi);
return; return;
@ -1200,13 +1217,14 @@ void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
pqi->query_type = queryTypeItems; pqi->query_type = queryTypeItems;
pqi->index_type=indexTypeNone; 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"); DPRINTF(E_LOG,L_DAAP,"Could not start enum\n");
ws_returnerror(pwsc,500,"Internal server error: out of memory!"); ws_returnerror(pwsc,500,"Internal server error: out of memory!");
return; 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); DPRINTF(E_DBG,L_DAAP,"Item enum: got %d songs, dmap size: %d\n",song_count,list_length);
@ -1220,7 +1238,9 @@ void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
dispatch_output_start(pwsc,pqi,61+list_length); dispatch_output_start(pwsc,pqi,61+list_length);
dispatch_output_write(pwsc,pqi,items_response,61); 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); DPRINTF(E_SPAM,L_DAAP,"Got block of size %d\n",list_length);
dispatch_output_write(pwsc,pqi,block,list_length); dispatch_output_write(pwsc,pqi,block,list_length);
free(block); free(block);
@ -1228,7 +1248,7 @@ void dispatch_items(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n"); DPRINTF(E_DBG,L_DAAP,"Done enumerating.\n");
db_enum_end(); db_enum_end(NULL);
dispatch_output_end(pwsc,pqi); dispatch_output_end(pwsc,pqi);
return; return;
@ -1280,6 +1300,7 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
unsigned char dbinfo_response[255]; /* FIXME */ unsigned char dbinfo_response[255]; /* FIXME */
unsigned char *current = dbinfo_response; unsigned char *current = dbinfo_response;
int namelen; int namelen;
int count;
namelen=strlen(config.servername); namelen=strlen(config.servername);
@ -1292,8 +1313,10 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
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_int(current,"miid",1); /* 12 */
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + namelen */ current += db_dmap_add_string(current,"minm",config.servername); /* 8 + namelen */
current += db_dmap_add_int(current,"mimc",db_get_song_count()); /* 12 */ db_get_song_count(NULL,&count);
current += db_dmap_add_int(current,"mctc",db_get_playlist_count()); /* 12 */ 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_start(pwsc,pqi,113+namelen);
dispatch_output_write(pwsc,pqi,dbinfo_response,113+namelen); dispatch_output_write(pwsc,pqi,dbinfo_response,113+namelen);

View File

@ -353,13 +353,14 @@ int main(int argc, char *argv[]) {
int start_time; int start_time;
int end_time; int end_time;
int rescan_counter=0; int rescan_counter=0;
int old_song_count; int old_song_count, song_count;
int force_non_root=0; int force_non_root=0;
int skip_initial=0; int skip_initial=0;
pthread_t signal_tid; pthread_t signal_tid;
int pid_fd; int pid_fd;
FILE *pid_fp=NULL; FILE *pid_fp=NULL;
char *perr;
config.use_mdns=1; config.use_mdns=1;
err_debuglevel=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 */ /* this will require that the db be readable by the runas user */
if(db_open(config.dbdir)) if(db_open(&perr,config.dbdir))
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",strerror(errno)); DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",perr);
/* Initialize the database before starting */ /* Initialize the database before starting */
DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n"); DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n");
@ -529,7 +530,8 @@ int main(int argc, char *argv[]) {
end_time=time(NULL); 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); end_time-start_time);
while(!config.stop) { while(!config.stop) {
@ -543,7 +545,7 @@ int main(int argc, char *argv[]) {
} }
if(config.reload) { if(config.reload) {
old_song_count = db_get_song_count(); old_song_count = song_count;
start_time=time(NULL); start_time=time(NULL);
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n"); 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.stop=1;
} }
config.reload=0; 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(NULL,&song_count);
db_get_song_count(),old_song_count,time(NULL)-start_time); 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); sleep(MAIN_SLEEP_INTERVAL);

View File

@ -344,7 +344,7 @@ int scan_path(char *path) {
(strcasestr(config.extensions, ext))) { (strcasestr(config.extensions, ext))) {
/* only scan if it's been changed, or empty db */ /* only scan if it's been changed, or empty db */
modified_time=sb.st_mtime; 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)) { (pmp3->force_update)) {
@ -380,6 +380,7 @@ int scan_static_playlist(char *path) {
MP3FILE *pmp3; MP3FILE *pmp3;
struct stat sb; struct stat sb;
char *current; char *current;
char *perr;
DPRINTF(E_WARN,L_SCAN|L_PL,"Processing static playlist: %s\n",path); DPRINTF(E_WARN,L_SCAN|L_PL,"Processing static playlist: %s\n",path);
if(stat(path,&sb)) { if(stat(path,&sb)) {
@ -399,7 +400,7 @@ int scan_static_playlist(char *path) {
*current='\x0'; *current='\x0';
} }
pm3u = db_fetch_playlist(path,0); pm3u = db_fetch_playlist(NULL,path,0);
if(pm3u && (pm3u->db_timestamp > sb.st_mtime)) { if(pm3u && (pm3u->db_timestamp > sb.st_mtime)) {
/* already up-to-date */ /* already up-to-date */
DPRINTF(E_DBG,L_SCAN,"Playlist already up-to-date\n"); DPRINTF(E_DBG,L_SCAN,"Playlist already up-to-date\n");
@ -408,12 +409,14 @@ int scan_static_playlist(char *path) {
} }
if(pm3u) if(pm3u)
db_delete_playlist(pm3u->id); db_delete_playlist(NULL,pm3u->id);
fd=open(path,O_RDONLY); fd=open(path,O_RDONLY);
if(fd != -1) { if(fd != -1) {
if(db_add_playlist(base_path,PL_STATICFILE,NULL,path,0,&playlistid) != DB_E_SUCCESS) { if(db_add_playlist(&perr,base_path,PL_STATICFILE,NULL,path,
DPRINTF(E_LOG,L_SCAN,"Error adding m3u playlist %s\n",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); db_dispose_playlist(pm3u);
return FALSE; return FALSE;
} }
@ -447,12 +450,14 @@ int scan_static_playlist(char *path) {
DPRINTF(E_DBG,L_SCAN|L_PL,"Checking %s\n",real_path); DPRINTF(E_DBG,L_SCAN|L_PL,"Checking %s\n",real_path);
// might be valid, might not... // might be valid, might not...
if((pmp3=db_fetch_path(real_path,0))) { if((pmp3=db_fetch_path(&perr,real_path,0))) {
db_add_playlist_item(playlistid,pmp3->id); /* FIXME: better error handling */
db_add_playlist_item(NULL,playlistid,pmp3->id);
db_dispose_item(pmp3); db_dispose_item(pmp3);
} else { } else {
DPRINTF(E_WARN,L_SCAN|L_PL,"Playlist entry %s bad: %s\n", DPRINTF(E_WARN,L_SCAN|L_PL,"Playlist entry %s bad: %s\n",
path,strerror(errno)); path,perr);
free(perr);
} }
} }
close(fd); close(fd);
@ -538,7 +543,8 @@ void scan_music_file(char *path, struct dirent *pde,
if(is_compdir) if(is_compdir)
mp3file.compilation = 1; mp3file.compilation = 1;
db_add(&mp3file); /* FIXME: error handling */
db_add(NULL,&mp3file);
} else { } else {
DPRINTF(E_WARN,L_SCAN,"Skipping %s - scan failed\n",mp3file.path); DPRINTF(E_WARN,L_SCAN,"Skipping %s - scan failed\n",mp3file.path);
} }

View File

@ -138,9 +138,9 @@ int scan_xml_datedecode(char *string) {
*/ */
int scan_xml_rb_compare(const void *pa, const void *pb, const void *cfg) { 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) if(((SCAN_XML_RB*)pa)->itunes_index < ((SCAN_XML_RB*)pb)->itunes_index)
return -1; return -1;
if(((SCAN_XML_RB*)pb)->itunes_index < ((SCAN_XML_RB*)pa)->itunes_index) if(((SCAN_XML_RB*)pb)->itunes_index < ((SCAN_XML_RB*)pa)->itunes_index)
return 1; return 1;
return 0; return 0;
} }
@ -156,10 +156,10 @@ int scan_xml_is_file(char *path) {
struct stat sb; struct stat sb;
if(stat(path,&sb)) if(stat(path,&sb))
return 0; return 0;
if(sb.st_mode & S_IFREG) if(sb.st_mode & S_IFREG)
return 1; return 1;
return 0; return 0;
} }
@ -182,35 +182,35 @@ int scan_xml_translate_path(char *pold, char *pnew) {
char *pbase; char *pbase;
if((!pold)||(!strlen(pold))) if((!pold)||(!strlen(pold)))
return FALSE; return FALSE;
if(!path_found) { 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. /* let's try to find the path by brute force.
* We'll assume that it is under the xml file somewhere * We'll assume that it is under the xml file somewhere
*/ */
while(!path_found && ((current = strrchr(working_path,'/')))) { while(!path_found && ((current = strrchr(working_path,'/')))) {
strcpy(base_path,scan_xml_file); strcpy(base_path,scan_xml_file);
pbase = strrchr(base_path,'/'); pbase = strrchr(base_path,'/');
if(!pbase) return FALSE; if(!pbase) return FALSE;
strcpy(pbase,pold + (current-working_path)); strcpy(pbase,pold + (current-working_path));
if(base_path[strlen(base_path)-1] == '/') if(base_path[strlen(base_path)-1] == '/')
base_path[strlen(base_path)-1] = '\0'; base_path[strlen(base_path)-1] = '\0';
DPRINTF(E_DBG,L_SCAN,"Trying %s\n",base_path); DPRINTF(E_DBG,L_SCAN,"Trying %s\n",base_path);
if(scan_xml_is_file(base_path)) { if(scan_xml_is_file(base_path)) {
path_found=1; path_found=1;
discard = (current - working_path); discard = (current - working_path);
DPRINTF(E_DBG,L_SCAN,"Found it!\n"); DPRINTF(E_DBG,L_SCAN,"Found it!\n");
} }
*current='\0'; *current='\0';
} }
if(!current) if(!current)
return FALSE; return FALSE;
} }
strcpy(base_path,scan_xml_file); strcpy(base_path,scan_xml_file);
@ -219,7 +219,7 @@ int scan_xml_translate_path(char *pold, char *pnew) {
strcpy(pbase,pold + discard); strcpy(pbase,pold + discard);
if(base_path[strlen(base_path)-1] == '/') 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); 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)); pnew=(SCAN_XML_RB*)malloc(sizeof(SCAN_XML_RB));
if(!pnew) 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->itunes_index = itunes_index;
pnew->mtd_index = mtd_index; pnew->mtd_index = mtd_index;
val = rbsearch((const void*)pnew,scan_xml_db); val = rbsearch((const void*)pnew,scan_xml_db);
if(!val) { if(!val) {
/* couldn't alloc the rb tree structure -- if we don't /* couldn't alloc the rb tree structure -- if we don't
* die now, we are going to soon enough*/ * die now, we are going to soon enough*/
DPRINTF(E_FATAL,L_SCAN,"redblack tree insert error\n"); 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; rb.itunes_index = itunes_index;
prb = (SCAN_XML_RB*) rbfind((void*)&rb,scan_xml_db); prb = (SCAN_XML_RB*) rbfind((void*)&rb,scan_xml_db);
if(prb) { if(prb) {
*mtd_index = prb->mtd_index; *mtd_index = prb->mtd_index;
DPRINTF(E_SPAM,L_SCAN,"Matching %d to %d\n",itunes_index,*mtd_index); DPRINTF(E_SPAM,L_SCAN,"Matching %d to %d\n",itunes_index,*mtd_index);
return TRUE; return TRUE;
} }
return FALSE; return FALSE;
@ -287,12 +287,12 @@ int scan_xml_get_tagindex(char *tag) {
int index=0; int index=0;
while(*ptag && (strcasecmp(tag,*ptag) != 0)) { while(*ptag && (strcasecmp(tag,*ptag) != 0)) {
ptag++; ptag++;
index++; index++;
} }
if(*ptag) if(*ptag)
return index; return index;
return SCAN_XML_T_UNKNOWN; 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); pnew=(char*)malloc(strlen(string)+1);
if(!pnew) if(!pnew)
return NULL; return NULL;
src=string; src=string;
dst=pnew; dst=pnew;
while(*src) { while(*src) {
switch(*src) { switch(*src) {
case '+': case '+':
if(space_as_plus) { if(space_as_plus) {
*dst++=' '; *dst++=' ';
} else { } else {
*dst++=*src; *dst++=*src;
} }
src++; src++;
break; break;
case '%': case '%':
/* this is hideous */ /* this is hideous */
src++; src++;
if(*src) { if(*src) {
if((*src <= '9') && (*src >='0')) if((*src <= '9') && (*src >='0'))
val=(*src - '0'); val=(*src - '0');
else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a')) else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
val=10+(tolower(*src) - 'a'); val=10+(tolower(*src) - 'a');
src++; src++;
} }
if(*src) { if(*src) {
val *= 16; val *= 16;
if((*src <= '9') && (*src >='0')) if((*src <= '9') && (*src >='0'))
val+=(*src - '0'); val+=(*src - '0');
else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a')) else if((tolower(*src) <= 'f')&&(tolower(*src) >= 'a'))
val+=(10+(tolower(*src) - 'a')); val+=(10+(tolower(*src) - 'a'));
src++; src++;
} }
*dst++=val; *dst++=val;
break; break;
default: default:
*dst++=*src++; *dst++=*src++;
break; break;
} }
} }
*dst='\0'; *dst='\0';
@ -381,31 +381,31 @@ int scan_xml_playlist(char *filename) {
/* initialize the redblack tree */ /* initialize the redblack tree */
if((scan_xml_db = rbinit(scan_xml_rb_compare,NULL)) == NULL) { if((scan_xml_db = rbinit(scan_xml_rb_compare,NULL)) == NULL) {
DPRINTF(E_LOG,L_SCAN,"Could not initialize red/black tree\n"); DPRINTF(E_LOG,L_SCAN,"Could not initialize red/black tree\n");
return FALSE; return FALSE;
} }
/* find the base dir of the itunes playlist itself */ /* find the base dir of the itunes playlist itself */
working_base = strdup(filename); working_base = strdup(filename);
if(strrchr(working_base,'/')) { if(strrchr(working_base,'/')) {
*(strrchr(working_base,'/') + 1) = '\x0'; *(strrchr(working_base,'/') + 1) = '\x0';
scan_xml_real_base_path = strdup(working_base); scan_xml_real_base_path = strdup(working_base);
} else { } else {
scan_xml_real_base_path = strdup("/"); scan_xml_real_base_path = strdup("/");
} }
free(working_base); free(working_base);
DPRINTF(E_SPAM,L_SCAN,"Parsing xml file: %s\n",filename); DPRINTF(E_SPAM,L_SCAN,"Parsing xml file: %s\n",filename);
if(!rxml_open(&xml_handle,filename,scan_xml_handler,NULL)) { if(!rxml_open(&xml_handle,filename,scan_xml_handler,NULL)) {
DPRINTF(E_LOG,L_SCAN,"Error opening xml file %s: %s\n", DPRINTF(E_LOG,L_SCAN,"Error opening xml file %s: %s\n",
filename,rxml_errorstring(xml_handle)); filename,rxml_errorstring(xml_handle));
} else { } else {
if(!rxml_parse(xml_handle)) { if(!rxml_parse(xml_handle)) {
retval=FALSE; retval=FALSE;
DPRINTF(E_LOG,L_SCAN,"Error parsing xml file %s: %s\n", DPRINTF(E_LOG,L_SCAN,"Error parsing xml file %s: %s\n",
filename,rxml_errorstring(xml_handle)); filename,rxml_errorstring(xml_handle));
} }
} }
rxml_close(xml_handle); rxml_close(xml_handle);
@ -413,11 +413,11 @@ int scan_xml_playlist(char *filename) {
/* destroy the redblack tree */ /* destroy the redblack tree */
val = rblookup(RB_LUFIRST,NULL,scan_xml_db); val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
while(val) { while(val) {
lookup_val.itunes_index = ((SCAN_XML_RB*)val)->itunes_index; lookup_val.itunes_index = ((SCAN_XML_RB*)val)->itunes_index;
lookup_ptr = (SCAN_XML_RB *)rbdelete((void*)&lookup_val,scan_xml_db); lookup_ptr = (SCAN_XML_RB *)rbdelete((void*)&lookup_val,scan_xml_db);
if(lookup_ptr) if(lookup_ptr)
free(lookup_ptr); free(lookup_ptr);
val = rblookup(RB_LUFIRST,NULL,scan_xml_db); val = rblookup(RB_LUFIRST,NULL,scan_xml_db);
} }
rbdestroy(scan_xml_db); rbdestroy(scan_xml_db);
@ -445,32 +445,32 @@ void scan_xml_handler(int action,void* puser,char* info) {
switch(action) { switch(action) {
case RXML_EVT_OPEN: /* file opened */ case RXML_EVT_OPEN: /* file opened */
state = XML_STATE_PREAMBLE; state = XML_STATE_PREAMBLE;
/* send this event to all dispatches to allow them /* send this event to all dispatches to allow them
* to reset * to reset
*/ */
scan_xml_preamble_section(action,info); scan_xml_preamble_section(action,info);
scan_xml_tracks_section(action,info); scan_xml_tracks_section(action,info);
scan_xml_playlists_section(action,info); scan_xml_playlists_section(action,info);
break; break;
case RXML_EVT_BEGIN: case RXML_EVT_BEGIN:
case RXML_EVT_END: case RXML_EVT_END:
case RXML_EVT_TEXT: case RXML_EVT_TEXT:
switch(state) { switch(state) {
case XML_STATE_PREAMBLE: case XML_STATE_PREAMBLE:
state=scan_xml_preamble_section(action,info); state=scan_xml_preamble_section(action,info);
break; break;
case XML_STATE_TRACKS: case XML_STATE_TRACKS:
state=scan_xml_tracks_section(action,info); state=scan_xml_tracks_section(action,info);
break; break;
case XML_STATE_PLAYLISTS: case XML_STATE_PLAYLISTS:
state=scan_xml_playlists_section(action,info); state=scan_xml_playlists_section(action,info);
break; break;
default: default:
break; break;
} }
default: default:
break; break;
} }
} }
@ -492,59 +492,59 @@ int scan_xml_preamble_section(int action, char *info) {
switch(action) { switch(action) {
case RXML_EVT_OPEN: /* initialization */ case RXML_EVT_OPEN: /* initialization */
expecting_next=0; expecting_next=0;
done=0; done=0;
break; break;
case RXML_EVT_END: case RXML_EVT_END:
if(expecting_next == SCAN_XML_PRE_TRACKS) { /* end of tracks tag */ if(expecting_next == SCAN_XML_PRE_TRACKS) { /* end of tracks tag */
expecting_next=0; expecting_next=0;
DPRINTF(E_DBG,L_SCAN,"Scanning tracks\n"); DPRINTF(E_DBG,L_SCAN,"Scanning tracks\n");
return XML_STATE_TRACKS; return XML_STATE_TRACKS;
} }
if(expecting_next == SCAN_XML_PRE_PLAYLISTS) { if(expecting_next == SCAN_XML_PRE_PLAYLISTS) {
expecting_next=0; expecting_next=0;
DPRINTF(E_DBG,L_SCAN,"Scanning playlists\n"); DPRINTF(E_DBG,L_SCAN,"Scanning playlists\n");
return XML_STATE_PLAYLISTS; return XML_STATE_PLAYLISTS;
} }
break; break;
case RXML_EVT_TEXT: /* scan for the tags we expect */ case RXML_EVT_TEXT: /* scan for the tags we expect */
if(!expecting_next) { if(!expecting_next) {
if(strcmp(info,"Application Version") == 0) { if(strcmp(info,"Application Version") == 0) {
expecting_next = SCAN_XML_PRE_VERSION; expecting_next = SCAN_XML_PRE_VERSION;
} else if (strcmp(info,"Music Folder") == 0) { } else if (strcmp(info,"Music Folder") == 0) {
expecting_next = SCAN_XML_PRE_PATH; expecting_next = SCAN_XML_PRE_PATH;
} else if (strcmp(info,"Tracks") == 0) { } else if (strcmp(info,"Tracks") == 0) {
expecting_next = SCAN_XML_PRE_TRACKS; expecting_next = SCAN_XML_PRE_TRACKS;
} else if (strcmp(info,"Playlists") == 0) { } else if (strcmp(info,"Playlists") == 0) {
expecting_next = SCAN_XML_PRE_PLAYLISTS; expecting_next = SCAN_XML_PRE_PLAYLISTS;
} }
} else { } else {
/* we were expecting someting! */ /* we were expecting someting! */
switch(expecting_next) { switch(expecting_next) {
case SCAN_XML_PRE_VERSION: case SCAN_XML_PRE_VERSION:
if(!scan_xml_itunes_version) { if(!scan_xml_itunes_version) {
scan_xml_itunes_version=strdup(info); scan_xml_itunes_version=strdup(info);
DPRINTF(E_DBG,L_SCAN,"iTunes Version: %s\n",info); DPRINTF(E_DBG,L_SCAN,"iTunes Version: %s\n",info);
} }
break; break;
case SCAN_XML_PRE_PATH: case SCAN_XML_PRE_PATH:
if(!scan_xml_itunes_base_path) { if(!scan_xml_itunes_base_path) {
scan_xml_itunes_base_path=strdup(info); scan_xml_itunes_base_path=strdup(info);
scan_xml_itunes_decoded_base_path=scan_xml_urldecode(info,0); scan_xml_itunes_decoded_base_path=scan_xml_urldecode(info,0);
DPRINTF(E_DBG,L_SCAN,"iTunes base path: %s\n",info); DPRINTF(E_DBG,L_SCAN,"iTunes base path: %s\n",info);
} }
break; break;
default: default:
break; break;
} }
expecting_next=0; expecting_next=0;
} }
break; /* RXML_EVT_TEXT */ break; /* RXML_EVT_TEXT */
default: default:
break; break;
} }
return XML_STATE_PREAMBLE; return XML_STATE_PREAMBLE;
@ -580,143 +580,144 @@ int scan_xml_tracks_section(int action, char *info) {
MP3FILE *pmp3; MP3FILE *pmp3;
if(action == RXML_EVT_OPEN) { if(action == RXML_EVT_OPEN) {
state = XML_TRACK_ST_INITIAL; state = XML_TRACK_ST_INITIAL;
memset((void*)&mp3,0,sizeof(MP3FILE)); memset((void*)&mp3,0,sizeof(MP3FILE));
song_path = NULL; song_path = NULL;
return 0; return 0;
} }
/* walk through the states */ /* walk through the states */
switch(state) { switch(state) {
case XML_TRACK_ST_INITIAL: case XML_TRACK_ST_INITIAL:
/* expection only a <dict> */ /* expection only a <dict> */
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT); MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_MAIN_DICT);
return XML_STATE_ERROR; return XML_STATE_ERROR;
break; break;
case XML_TRACK_ST_MAIN_DICT: case XML_TRACK_ST_MAIN_DICT:
/* either get a <key>, or a </dict> */ /* either get a <key>, or a </dict> */
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID); MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_ID);
if ((action == RXML_EVT_END) && (strcasecmp(info,"dict") == 0)) { if ((action == RXML_EVT_END) && (strcasecmp(info,"dict") == 0)) {
return XML_STATE_PREAMBLE; return XML_STATE_PREAMBLE;
} }
return XML_STATE_ERROR; return XML_STATE_ERROR;
break; break;
case XML_TRACK_ST_EXPECTING_TRACK_ID: case XML_TRACK_ST_EXPECTING_TRACK_ID:
/* this is somewhat loose - <key>id</key> */ /* this is somewhat loose - <key>id</key> */
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_EXPECTING_TRACK_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); MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_EXPECTING_TRACK_DICT);
if (action == RXML_EVT_TEXT) { if (action == RXML_EVT_TEXT) {
current_track_id = atoi(info); current_track_id = atoi(info);
DPRINTF(E_DBG,L_SCAN,"Scanning iTunes id #%d\n",current_track_id); DPRINTF(E_DBG,L_SCAN,"Scanning iTunes id #%d\n",current_track_id);
} else { } else {
return XML_STATE_ERROR; return XML_STATE_ERROR;
} }
break; break;
case XML_TRACK_ST_EXPECTING_TRACK_DICT: case XML_TRACK_ST_EXPECTING_TRACK_DICT:
/* waiting for a dict */ /* waiting for a dict */
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO); MAYBESETSTATE_TR(RXML_EVT_BEGIN,"dict",XML_TRACK_ST_TRACK_INFO);
return XML_STATE_ERROR; return XML_STATE_ERROR;
break; break;
case XML_TRACK_ST_TRACK_INFO: case XML_TRACK_ST_TRACK_INFO:
/* again, kind of loose */ /* again, kind of loose */
MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO); MAYBESETSTATE_TR(RXML_EVT_BEGIN,"key",XML_TRACK_ST_TRACK_INFO);
MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA); MAYBESETSTATE_TR(RXML_EVT_END,"key",XML_TRACK_ST_TRACK_DATA);
if(action == RXML_EVT_TEXT) { if(action == RXML_EVT_TEXT) {
current_field=scan_xml_get_tagindex(info); current_field=scan_xml_get_tagindex(info);
if(current_field == SCAN_XML_T_DISABLED) { if(current_field == SCAN_XML_T_DISABLED) {
mp3.disabled = 1; mp3.disabled = 1;
} else if(current_field == SCAN_XML_T_COMPILATION) { } else if(current_field == SCAN_XML_T_COMPILATION) {
mp3.compilation = 1; mp3.compilation = 1;
} }
} else if((action == RXML_EVT_END) && (strcmp(info,"dict")==0)) { } else if((action == RXML_EVT_END) && (strcmp(info,"dict")==0)) {
state = XML_TRACK_ST_MAIN_DICT; state = XML_TRACK_ST_MAIN_DICT;
/* but more importantly, we gotta process the track */ /* but more importantly, we gotta process the track */
if(scan_xml_translate_path(song_path,real_path)) { if(scan_xml_translate_path(song_path,real_path)) {
pmp3=db_fetch_path(real_path,0); /* FIXME: Error handling */
if(pmp3) { pmp3=db_fetch_path(NULL,real_path,0);
/* Update the existing record with the if(pmp3) {
* updated stuff we got from the iTunes xml file /* Update the existing record with the
*/ * updated stuff we got from the iTunes xml file
MAYBECOPYSTRING(title); */
MAYBECOPYSTRING(artist); MAYBECOPYSTRING(title);
MAYBECOPYSTRING(album); MAYBECOPYSTRING(artist);
MAYBECOPYSTRING(genre); MAYBECOPYSTRING(album);
MAYBECOPY(song_length); MAYBECOPYSTRING(genre);
MAYBECOPY(track); MAYBECOPY(song_length);
MAYBECOPY(total_tracks); MAYBECOPY(track);
MAYBECOPY(year); MAYBECOPY(total_tracks);
MAYBECOPY(bitrate); MAYBECOPY(year);
MAYBECOPY(samplerate); MAYBECOPY(bitrate);
MAYBECOPY(play_count); MAYBECOPY(samplerate);
MAYBECOPY(rating); MAYBECOPY(play_count);
MAYBECOPY(disc); MAYBECOPY(rating);
MAYBECOPY(total_discs); MAYBECOPY(disc);
MAYBECOPY(time_added); MAYBECOPY(total_discs);
MAYBECOPY(time_added);
/* must add to the red-black tree */ /* must add to the red-black tree */
scan_xml_add_lookup(current_track_id,pmp3->id); scan_xml_add_lookup(current_track_id,pmp3->id);
db_add(pmp3); db_add(NULL,pmp3);
db_dispose_item(pmp3); db_dispose_item(pmp3);
memset((void*)&mp3,0,sizeof(MP3FILE)); memset((void*)&mp3,0,sizeof(MP3FILE));
MAYBEFREE(song_path); MAYBEFREE(song_path);
} }
} }
} else { } else {
return XML_STATE_ERROR; return XML_STATE_ERROR;
} }
break; break;
case XML_TRACK_ST_TRACK_DATA: case XML_TRACK_ST_TRACK_DATA:
if(action == RXML_EVT_BEGIN) { if(action == RXML_EVT_BEGIN) {
break; break;
} else if(action == RXML_EVT_TEXT) { } else if(action == RXML_EVT_TEXT) {
if(current_field == SCAN_XML_T_NAME) { if(current_field == SCAN_XML_T_NAME) {
mp3.title = strdup(info); mp3.title = strdup(info);
} else if(current_field == SCAN_XML_T_ARTIST) { } else if(current_field == SCAN_XML_T_ARTIST) {
mp3.artist = strdup(info); mp3.artist = strdup(info);
} else if(current_field == SCAN_XML_T_ALBUM) { } else if(current_field == SCAN_XML_T_ALBUM) {
mp3.album = strdup(info); mp3.album = strdup(info);
} else if(current_field == SCAN_XML_T_GENRE) { } else if(current_field == SCAN_XML_T_GENRE) {
mp3.genre = strdup(info); mp3.genre = strdup(info);
} else if(current_field == SCAN_XML_T_TOTALTIME) { } else if(current_field == SCAN_XML_T_TOTALTIME) {
mp3.song_length = atoi(info); mp3.song_length = atoi(info);
} else if(current_field == SCAN_XML_T_TRACKNUMBER) { } else if(current_field == SCAN_XML_T_TRACKNUMBER) {
mp3.track = atoi(info); mp3.track = atoi(info);
} else if(current_field == SCAN_XML_T_TRACKCOUNT) { } else if(current_field == SCAN_XML_T_TRACKCOUNT) {
mp3.total_tracks = atoi(info); mp3.total_tracks = atoi(info);
} else if(current_field == SCAN_XML_T_YEAR) { } else if(current_field == SCAN_XML_T_YEAR) {
mp3.year = atoi(info); mp3.year = atoi(info);
} else if(current_field == SCAN_XML_T_BITRATE) { } else if(current_field == SCAN_XML_T_BITRATE) {
mp3.bitrate = atoi(info); mp3.bitrate = atoi(info);
} else if(current_field == SCAN_XML_T_SAMPLERATE) { } else if(current_field == SCAN_XML_T_SAMPLERATE) {
mp3.samplerate = atoi(info); mp3.samplerate = atoi(info);
} else if(current_field == SCAN_XML_T_PLAYCOUNT) { } else if(current_field == SCAN_XML_T_PLAYCOUNT) {
mp3.play_count = atoi(info); mp3.play_count = atoi(info);
} else if(current_field == SCAN_XML_T_RATING) { } else if(current_field == SCAN_XML_T_RATING) {
mp3.rating = atoi(info); mp3.rating = atoi(info);
} else if(current_field == SCAN_XML_T_DISCNO) { } else if(current_field == SCAN_XML_T_DISCNO) {
mp3.disc = atoi(info); mp3.disc = atoi(info);
} else if(current_field == SCAN_XML_T_DISCCOUNT) { } else if(current_field == SCAN_XML_T_DISCCOUNT) {
mp3.total_discs = atoi(info); mp3.total_discs = atoi(info);
} else if(current_field == SCAN_XML_T_LOCATION) { } else if(current_field == SCAN_XML_T_LOCATION) {
song_path = scan_xml_urldecode(info,0); song_path = scan_xml_urldecode(info,0);
} else if(current_field == SCAN_XML_T_DATE_ADDED) { } else if(current_field == SCAN_XML_T_DATE_ADDED) {
mp3.time_added = scan_xml_datedecode(info); mp3.time_added = scan_xml_datedecode(info);
} }
} else if(action == RXML_EVT_END) { } else if(action == RXML_EVT_END) {
state = XML_TRACK_ST_TRACK_INFO; state = XML_TRACK_ST_TRACK_INFO;
} else { } else {
return XML_STATE_ERROR; return XML_STATE_ERROR;
} }
break; break;
default: default:
return XML_STATE_ERROR; return XML_STATE_ERROR;
} }
return XML_STATE_TRACKS; return XML_STATE_TRACKS;
@ -761,110 +762,114 @@ int scan_xml_playlists_section(int action, char *info) {
/* do initialization */ /* do initialization */
if(action == RXML_EVT_OPEN) { if(action == RXML_EVT_OPEN) {
state = XML_PL_ST_INITIAL; state = XML_PL_ST_INITIAL;
if(current_name) if(current_name)
free(current_name); free(current_name);
current_name = NULL; current_name = NULL;
dont_scan=0; dont_scan=0;
return 0; return 0;
} }
switch(state) { switch(state) {
case XML_PL_ST_INITIAL: case XML_PL_ST_INITIAL:
/* expecting <array> or error */ /* expecting <array> or error */
MAYBESETSTATE_PL(RXML_EVT_BEGIN,"array",XML_PL_ST_EXPECTING_PL); MAYBESETSTATE_PL(RXML_EVT_BEGIN,"array",XML_PL_ST_EXPECTING_PL);
return XML_STATE_ERROR; return XML_STATE_ERROR;
case XML_PL_ST_EXPECTING_PL: case XML_PL_ST_EXPECTING_PL:
/* either a new playlist, or end of playlist list */ /* either a new playlist, or end of playlist list */
MAYBESETSTATE_PL(RXML_EVT_BEGIN,"dict",XML_PL_ST_EXPECTING_PL_DATA); MAYBESETSTATE_PL(RXML_EVT_BEGIN,"dict",XML_PL_ST_EXPECTING_PL_DATA);
if((action == RXML_EVT_END) && (strcasecmp(info,"array") == 0)) if((action == RXML_EVT_END) && (strcasecmp(info,"array") == 0))
return XML_STATE_PREAMBLE; return XML_STATE_PREAMBLE;
return XML_STATE_ERROR; return XML_STATE_ERROR;
case XML_PL_ST_EXPECTING_PL_DATA: case XML_PL_ST_EXPECTING_PL_DATA:
/* either a key/data pair, or an array, signaling start of playlist /* either a key/data pair, or an array, signaling start of playlist
* or the end of the dict (end of playlist data) */ * 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_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,"key",XML_PL_ST_EXPECTING_PL_VALUE);
MAYBESETSTATE_PL(RXML_EVT_END,"dict",XML_PL_ST_EXPECTING_PL); MAYBESETSTATE_PL(RXML_EVT_END,"dict",XML_PL_ST_EXPECTING_PL);
if(action == RXML_EVT_TEXT) { if(action == RXML_EVT_TEXT) {
next_value=XML_PL_NEXT_VALUE_NONE; next_value=XML_PL_NEXT_VALUE_NONE;
if(strcasecmp(info,"Name") == 0) { if(strcasecmp(info,"Name") == 0) {
next_value = XML_PL_NEXT_VALUE_NAME; next_value = XML_PL_NEXT_VALUE_NAME;
} else if(strcasecmp(info,"Playlist ID") == 0) { } else if(strcasecmp(info,"Playlist ID") == 0) {
next_value = XML_PL_NEXT_VALUE_ID; next_value = XML_PL_NEXT_VALUE_ID;
} else if(strcasecmp(info,"Master") == 0) { } else if(strcasecmp(info,"Master") == 0) {
/* No point adding the master library... we have one */ /* No point adding the master library... we have one */
dont_scan=1; dont_scan=1;
} }
return XML_STATE_PLAYLISTS; return XML_STATE_PLAYLISTS;
} }
return XML_STATE_ERROR; return XML_STATE_ERROR;
case XML_PL_ST_EXPECTING_PL_VALUE: case XML_PL_ST_EXPECTING_PL_VALUE:
/* any tag, value we are looking for, any close tag */ /* any tag, value we are looking for, any close tag */
if((action == RXML_EVT_BEGIN) && (strcasecmp(info,"array") == 0)) { if((action == RXML_EVT_BEGIN) && (strcasecmp(info,"array") == 0)) {
/* we are about to get track list... must register the playlist */ /* we are about to get track list... must register the playlist */
current_id=0; current_id=0;
if(dont_scan == 0) { if(dont_scan == 0) {
DPRINTF(E_DBG,L_SCAN,"Creating playlist for %s\n",current_name); DPRINTF(E_DBG,L_SCAN,"Creating playlist for %s\n",current_name);
/* delete the old one first */ /* delete the old one first */
pm3u = db_fetch_playlist(scan_xml_file,native_plid); /* FIXME: Error handling */
if(pm3u) { pm3u = db_fetch_playlist(NULL,scan_xml_file,native_plid);
db_delete_playlist(pm3u->id); if(pm3u) {
db_dispose_playlist(pm3u); db_delete_playlist(NULL,pm3u->id);
} db_dispose_playlist(pm3u);
if(db_add_playlist(current_name,PL_STATICXML,NULL,scan_xml_file, }
native_plid,&current_id) != DB_E_SUCCESS) { if(db_add_playlist(NULL,current_name,PL_STATICXML,NULL,
DPRINTF(E_LOG,L_SCAN,"err adding playlist %s\n",current_name); scan_xml_file,native_plid,
current_id=0; &current_id) != DB_E_SUCCESS)
} {
} DPRINTF(E_LOG,L_SCAN,"err adding playlist %s\n",current_name);
dont_scan=0; current_id=0;
state=XML_PL_ST_EXPECTING_PL_TRACKLIST; }
return XML_STATE_PLAYLISTS; }
} dont_scan=0;
if(action == RXML_EVT_BEGIN) state=XML_PL_ST_EXPECTING_PL_TRACKLIST;
return XML_STATE_PLAYLISTS; return XML_STATE_PLAYLISTS;
if(action == RXML_EVT_END) { }
state = XML_PL_ST_EXPECTING_PL_DATA; if(action == RXML_EVT_BEGIN)
return XML_STATE_PLAYLISTS; return XML_STATE_PLAYLISTS;
} if(action == RXML_EVT_END) {
if(action == RXML_EVT_TEXT) { state = XML_PL_ST_EXPECTING_PL_DATA;
/* got the value we were hoping for */ return XML_STATE_PLAYLISTS;
if(next_value == XML_PL_NEXT_VALUE_NAME) { }
if(current_name) if(action == RXML_EVT_TEXT) {
free(current_name); /* got the value we were hoping for */
current_name = strdup(info); if(next_value == XML_PL_NEXT_VALUE_NAME) {
/* disallow specific playlists */ if(current_name)
if(strcasecmp(current_name,"Party Shuffle") == 0) { free(current_name);
dont_scan=1; current_name = strdup(info);
} /* disallow specific playlists */
} else if(next_value == XML_PL_NEXT_VALUE_ID) { if(strcasecmp(current_name,"Party Shuffle") == 0) {
native_plid = atoi(info); dont_scan=1;
} }
return XML_STATE_PLAYLISTS; } else if(next_value == XML_PL_NEXT_VALUE_ID) {
} native_plid = atoi(info);
return XML_STATE_ERROR; }
return XML_STATE_PLAYLISTS;
}
return XML_STATE_ERROR;
case XML_PL_ST_EXPECTING_PL_TRACKLIST: case XML_PL_ST_EXPECTING_PL_TRACKLIST:
if((strcasecmp(info,"dict") == 0) || (strcasecmp(info,"key") == 0)) if((strcasecmp(info,"dict") == 0) || (strcasecmp(info,"key") == 0))
return XML_STATE_PLAYLISTS; return XML_STATE_PLAYLISTS;
MAYBESETSTATE_PL(RXML_EVT_END,"array",XML_PL_ST_EXPECTING_PL_DATA); MAYBESETSTATE_PL(RXML_EVT_END,"array",XML_PL_ST_EXPECTING_PL_DATA);
if(action == RXML_EVT_TEXT) { if(action == RXML_EVT_TEXT) {
if(strcasecmp(info,"Track ID") != 0) { if(strcasecmp(info,"Track ID") != 0) {
native_track_id = atoi(info); native_track_id = atoi(info);
DPRINTF(E_DBG,L_SCAN,"Adding itunes track #%s\n",info); DPRINTF(E_DBG,L_SCAN,"Adding itunes track #%s\n",info);
/* add it to the current playlist (current_id) */ /* add it to the current playlist (current_id) */
if(current_id && scan_xml_get_index(native_track_id, &track_id)) { if(current_id && scan_xml_get_index(native_track_id, &track_id)) {
db_add_playlist_item(current_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: default:
return XML_STATE_ERROR; return XML_STATE_ERROR;
} }
return XML_STATE_PLAYLISTS; return XML_STATE_PLAYLISTS;

View File

@ -187,7 +187,7 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
WS_CONNINFO *pci; WS_CONNINFO *pci;
SCAN_STATUS *pss; SCAN_STATUS *pss;
WSTHREADENUM wste; WSTHREADENUM wste;
int count;
XMLSTRUCT *pxml; XMLSTRUCT *pxml;
pxml=xml_init(pwsc,1); pxml=xml_init(pwsc,1);
@ -255,19 +255,19 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
memset(buf,0x0,sizeof(buf)); memset(buf,0x0,sizeof(buf));
if(r_days) if(r_days)
sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days, sprintf((char*)&buf[strlen(buf)],"%d day%s, ", r_days,
r_days == 1 ? "" : "s"); r_days == 1 ? "" : "s");
if(r_days || r_hours) if(r_days || r_hours)
sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours, sprintf((char*)&buf[strlen(buf)],"%d hour%s, ", r_hours,
r_hours == 1 ? "" : "s"); r_hours == 1 ? "" : "s");
if(r_days || r_hours || r_mins) if(r_days || r_hours || r_mins)
sprintf((char*)&buf[strlen(buf)],"%d minute%s, ", r_mins, sprintf((char*)&buf[strlen(buf)],"%d minute%s, ", r_mins,
r_mins == 1 ? "" : "s"); r_mins == 1 ? "" : "s");
sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs, sprintf((char*)&buf[strlen(buf)],"%d second%s ", r_secs,
r_secs == 1 ? "" : "s"); r_secs == 1 ? "" : "s");
xml_push(pxml,"stat"); xml_push(pxml,"stat");
xml_output(pxml,"name","Uptime"); xml_output(pxml,"name","Uptime");
@ -276,7 +276,8 @@ void xml_get_stats(WS_CONNINFO *pwsc) {
xml_push(pxml,"stat"); xml_push(pxml,"stat");
xml_output(pxml,"name","Songs"); 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_pop(pxml); /* stat */
xml_push(pxml,"stat"); xml_push(pxml,"stat");