add ini style config handling

This commit is contained in:
Ron Pedde 2006-02-27 22:48:42 +00:00
parent 47f6eeb8c5
commit 73636a5a4c
14 changed files with 254 additions and 543 deletions

View File

@ -52,7 +52,7 @@ mt_daapd_SOURCES = main.c daapd.h rend.h uici.c uici.h webserver.c \
rxml.c rxml.h redblack.c redblack.h scan-mp3.c scan-mp4.c \ rxml.c rxml.h redblack.c redblack.h scan-mp3.c scan-mp4.c \
scan-xml.c scan-wma.c scan-aac.c scan-aac.h scan-wav.c scan-url.c \ scan-xml.c scan-wma.c scan-aac.c scan-aac.h scan-wav.c scan-url.c \
smart-parser.c smart-parser.h xml-rpc.c xml-rpc.h \ smart-parser.c smart-parser.h xml-rpc.c xml-rpc.h \
os.h strptime.c strptime.h \ os.h strptime.c strptime.h ll.c ll.h conf.c conf.h \
strtok_r.c strtok_r.h os-unix.h os-unix.c os.h \ strtok_r.c strtok_r.h os-unix.h os-unix.c os.h \
$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC) $(FLACSRC) \ $(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC) $(FLACSRC) \
$(MUSEPACKSRC) $(SQLITEDB) $(SQLITE3DB) $(SQLDB) $(MUSEPACKSRC) $(SQLITEDB) $(SQLITE3DB) $(SQLDB)

View File

@ -567,4 +567,23 @@ int _conf_write(FILE *fp, LL *pll, int sublevel) {
} }
/**
* determine if a configuration entry is actually set
*
* @param section section to test
* @key key to check
* @return TRUE if set, FALSE otherwise
*/
int conf_isset(char *section, char *key) {
int retval = FALSE;
_conf_lock();
if(_conf_fetch_item(conf_main,section,key)) {
retval = TRUE;
}
_conf_unlock();
return retval;
}

View File

@ -39,6 +39,7 @@ extern int conf_get_string(char *section, char *key, char *dflt,
extern int conf_set_int(char *section, char *key, int value); extern int conf_set_int(char *section, char *key, int value);
extern int conf_set_string(char *section, char *key, char *value); extern int conf_set_string(char *section, char *key, char *value);
extern int conf_isset(char *section, char *key);
extern int conf_iswritable(void); extern int conf_iswritable(void);
extern int conf_write(void); extern int conf_write(void);

View File

@ -48,6 +48,7 @@
# include <sys/wait.h> # include <sys/wait.h>
#endif #endif
#include "conf.h"
#include "configfile.h" #include "configfile.h"
#include "db-generic.h" #include "db-generic.h"
#include "err.h" #include "err.h"
@ -62,9 +63,7 @@
/* /*
* Forwards * Forwards
*/ */
static void config_emit_string(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_literal(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_literal(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_int(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_threadstatus(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_threadstatus(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_ispage(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_ispage(WS_CONNINFO *pwsc, void *value, char *arg);
@ -73,14 +72,10 @@ static void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg
static void config_emit_user(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_user(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_version(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_version(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_debuglevel(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_system(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_system(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_flags(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_flags(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg); static void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg);
static void config_subst_stream(WS_CONNINFO *pwsc, int fd_src); static void config_subst_stream(WS_CONNINFO *pwsc, int fd_src);
static int config_file_is_readonly(void);
static int config_existdir(char *path);
static int config_makedir(char *path);
static void config_content_type(WS_CONNINFO *pwsc, char *path); static void config_content_type(WS_CONNINFO *pwsc, char *path);
/* /*
@ -114,6 +109,7 @@ typedef struct tag_configelement {
/** List of all valid config entries and web interface directives */ /** List of all valid config entries and web interface directives */
CONFIGELEMENT config_elements[] = { CONFIGELEMENT config_elements[] = {
/*
{ 1,1,0,CONFIG_TYPE_STRING,"runas",(void*)&config.runas,config_emit_string }, { 1,1,0,CONFIG_TYPE_STRING,"runas",(void*)&config.runas,config_emit_string },
{ 1,1,0,CONFIG_TYPE_STRING,"web_root",(void*)&config.web_root,config_emit_string }, { 1,1,0,CONFIG_TYPE_STRING,"web_root",(void*)&config.web_root,config_emit_string },
{ 1,1,0,CONFIG_TYPE_INT,"port",(void*)&config.port,config_emit_int }, { 1,1,0,CONFIG_TYPE_INT,"port",(void*)&config.port,config_emit_int },
@ -138,6 +134,8 @@ CONFIGELEMENT config_elements[] = {
{ 1,0,0,CONFIG_TYPE_STRING,"password",(void*)&config.readpassword, config_emit_string }, { 1,0,0,CONFIG_TYPE_STRING,"password",(void*)&config.readpassword, config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"compdirs",(void*)&config.compdirs, config_emit_string }, { 1,0,0,CONFIG_TYPE_STRING,"compdirs",(void*)&config.compdirs, config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"logfile",(void*)&config.logfile, config_emit_string }, { 1,0,0,CONFIG_TYPE_STRING,"logfile",(void*)&config.logfile, config_emit_string },
{ 1,0,0,CONFIG_TYPE_STRING,"art_filename",(void*)&config.artfilename,config_emit_string },
*/
{ 0,0,0,CONFIG_TYPE_SPECIAL,"host",(void*)NULL,config_emit_host }, { 0,0,0,CONFIG_TYPE_SPECIAL,"host",(void*)NULL,config_emit_host },
{ 0,0,0,CONFIG_TYPE_SPECIAL,"release",(void*)VERSION,config_emit_literal }, { 0,0,0,CONFIG_TYPE_SPECIAL,"release",(void*)VERSION,config_emit_literal },
{ 0,0,0,CONFIG_TYPE_SPECIAL,"package",(void*)PACKAGE,config_emit_literal }, { 0,0,0,CONFIG_TYPE_SPECIAL,"package",(void*)PACKAGE,config_emit_literal },
@ -150,7 +148,6 @@ CONFIGELEMENT config_elements[] = {
{ 0,0,0,CONFIG_TYPE_SPECIAL,"readonly",(void*)NULL,config_emit_readonly }, { 0,0,0,CONFIG_TYPE_SPECIAL,"readonly",(void*)NULL,config_emit_readonly },
{ 0,0,0,CONFIG_TYPE_SPECIAL,"version",(void*)NULL,config_emit_version }, { 0,0,0,CONFIG_TYPE_SPECIAL,"version",(void*)NULL,config_emit_version },
{ 0,0,0,CONFIG_TYPE_SPECIAL,"system",(void*)NULL,config_emit_system }, { 0,0,0,CONFIG_TYPE_SPECIAL,"system",(void*)NULL,config_emit_system },
{ 1,0,0,CONFIG_TYPE_STRING,"art_filename",(void*)&config.artfilename,config_emit_string },
{ 0,0,0,CONFIG_TYPE_SPECIAL,"flags",(void*)NULL,config_emit_flags }, { 0,0,0,CONFIG_TYPE_SPECIAL,"flags",(void*)NULL,config_emit_flags },
{ -1,1,0,CONFIG_TYPE_STRING,NULL,NULL,NULL } { -1,1,0,CONFIG_TYPE_STRING,NULL,NULL,NULL }
}; };
@ -177,6 +174,7 @@ void config_content_type(WS_CONNINFO *pwsc, char *path) {
} }
} }
#if 0
/** /**
* Try and create a directory, including parents. * Try and create a directory, including parents.
* *
@ -241,356 +239,8 @@ int config_existdir(char *path) {
errno=ENOTDIR; errno=ENOTDIR;
return 0; return 0;
} }
/**
* Read the specified config file, padding the config structure
* appropriately.
*
* \param file config file to process
* \returns 0 on success, -1 otherwise with errno set
*/
int config_read(char *file) {
FILE *fin;
char *buffer;
int err=0;
char *value;
char *comment;
char path_buffer[PATH_MAX];
CONFIGELEMENT *pce;
int handled;
char *term;
int compterms=0,currentterm;
char *term_begin, *term_end;
char *compdirs;
buffer=(char*)malloc(MAX_LINE+1);
if(!buffer)
return -1;
if((fin=fopen(file,"r")) == NULL) {
err=errno;
free(buffer);
if(ENOENT == err) {
DPRINTF(E_LOG,L_CONF,"Whoops! Can't find the config file! If you are running this for the first\n");
DPRINTF(E_LOG,L_CONF,"time, then perhaps you've forgotten to copy the sample config in the \n");
DPRINTF(E_LOG,L_CONF,"contrib directory into /etc/mt-daapd.conf. Just a suggestion...\n\n");
}
errno=err;
return -1;
}
#ifdef NSLU2
config.always_scan=0;
#else
config.always_scan=1;
#endif #endif
config.configfile=strdup(file);
config.web_root=NULL;
config.adminpassword=NULL;
config.readpassword=NULL;
config.iface=NULL;
config.mp3dir=NULL;
config.playlist=NULL;
config.runas=NULL;
config.artfilename=NULL;
config.logfile=NULL;
config.rescan_interval=0;
config.process_m3u=0;
config.scan_type=0;
config.compress=0;
config.latin1_tags=0;
config.compdirs=NULL;
config.complist = NULL;
/* DWB: use alloced space so it can be freed without errors */
config.extensions=strdup(".mp3");
config.ssc_codectypes=strdup("");
config.ssc_prog=strdup("");
/* DWB: use alloced space so it can be freed without errors */
config.servername=strdup("mt-daapd " VERSION);
while(fgets(buffer,MAX_LINE,fin)) {
buffer[MAX_LINE] = '\0';
comment=strchr(buffer,'#');
if(comment)
*comment = '\0';
while(strlen(buffer) && (strchr("\n\r ",buffer[strlen(buffer)-1])))
buffer[strlen(buffer)-1] = '\0';
term=buffer;
while((*term=='\t') || (*term==' '))
term++;
value=term;
strsep(&value,"\t ");
if((value) && (term) && (strlen(term))) {
while(strlen(value) && (strchr("\t ",*value)))
value++;
pce=config_elements;
handled=0;
while((!handled) && (pce->config_element != -1)) {
if((strcasecmp(term,pce->name)==0) && (pce->config_element)) {
/* valid config directive */
handled=1;
pce->changed=1;
DPRINTF(E_DBG,L_CONF,"Read %s: %s\n",pce->name,value);
switch(pce->type) {
case CONFIG_TYPE_STRING:
/* DWB: free space to prevent small leak */
if(*((char **)(pce->var)))
free(*((char **)(pce->var)));
*((char **)(pce->var)) = (void*)strdup(value);
break;
case CONFIG_TYPE_INT:
*((int*)(pce->var)) = atoi(value);
break;
}
}
pce++;
}
if(!handled) {
fprintf(stderr,"Invalid config directive: %s\n",buffer);
fclose(fin);
return -1;
}
}
}
fclose(fin);
free(buffer);
/* check to see if all required elements are satisfied */
pce=config_elements;
err=0;
while((pce->config_element != -1)) {
if(pce->required && pce->config_element && !pce->changed) {
DPRINTF(E_LOG,L_CONF,"Required config entry '%s' not specified\n",pce->name);
err=-1;
}
/* too much spam on startup
if((pce->config_element) && (pce->changed)) {
switch(pce->type) {
case CONFIG_TYPE_STRING:
DPRINTF(E_INF,"%s: %s\n",pce->name,*((char**)pce->var));
break;
case CONFIG_TYPE_INT:
DPRINTF(E_INF,"%s: %d\n",pce->name,*((int*)pce->var));
break;
}
}
*/
pce->changed=0;
pce++;
}
/* Set the directory components to realpaths */
realpath(config.web_root,path_buffer);
free(config.web_root);
config.web_root=strdup(path_buffer);
realpath(config.mp3dir,path_buffer);
free(config.mp3dir);
config.mp3dir=strdup(path_buffer);
if(config.dbdir) {
DPRINTF(E_LOG,L_MISC,"You are using db_dir rather than "
"db_type/db_parms. This will stop working at "
"some point. Please fix your config\n");
realpath(config.dbdir,path_buffer);
free(config.dbdir);
config.dbdir=strdup(path_buffer);
}
/* sanity check the paths */
sprintf(path_buffer,"%s/index.html",config.web_root);
if((fin=fopen(path_buffer,"r")) == NULL) {
err=-1;
DPRINTF(E_LOG,L_CONF,"Invalid web_root\n");
/* check for the common error */
if(strcasecmp(config.web_root,"/usr/share/mt-daapd/admin-root") == 0) {
/* see if /usr/local is any better */
if((fin=fopen("/usr/local/share/mt-daapd/admin-root","r")) != NULL) {
fclose(fin);
DPRINTF(E_LOG,L_CONF,"Should it be /usr/local/share/mt-daapd/admin-root?");
}
}
} else {
fclose(fin);
}
/* should really check the mp3 path */
if(!config_existdir(config.mp3dir)) {
DPRINTF(E_LOG,L_CONF,"Bad mp3 directory (%s): %s\n",config.mp3dir,strerror(errno));
return -1;
}
if((config.dbdir)&&(!config_existdir(config.dbdir))) {
/* try to make it */
if(config_makedir(config.dbdir)) {
DPRINTF(E_LOG,L_CONF,"Database dir %s does not exist, cannot create: %s\n",
config.dbdir,strerror(errno));
return -1;
}
}
/* must have zlib 1.2 or better for gzip encoding support */
if(!strncmp(ZLIB_VERSION,"0.",2) ||
!strncmp(ZLIB_VERSION,"1.0",3) ||
!strncmp(ZLIB_VERSION,"1.1",3)) {
if(config.compress) {
config.compress=0;
DPRINTF(E_LOG,L_CONF,"Must have zlib > 1.2.0 to use gzip content encoding. You have %s. Disabling.\n",ZLIB_VERSION);
}
}
/* See how many compilation dirs we have */
compterms=0;
term_begin=config.compdirs;
while(term_begin) {
compterms++;
term_begin=strchr(term_begin,',');
if(term_begin)
term_begin++;
}
/* Now allocate comp dirs */
if(compterms) {
config.complist=(char**)malloc((compterms+1) * sizeof(char*));
if(!config.complist)
DPRINTF(E_FATAL,L_MISC,"Alloc error.\n");
currentterm=0;
term_begin=config.compdirs;
while(*term_begin && *term_begin ==' ')
term_begin++;
compdirs = strdup(term_begin);
term_begin = term_end = compdirs;
while(term_end) {
term_end = strchr(term_begin,',');
while((*term_begin)&&(*term_begin == ' '))
term_begin++;
if(term_end)
*term_end='\0';
while(strlen(term_begin) && term_begin[strlen(term_begin)-1]==' ')
term_begin[strlen(term_begin)-1] = '\0';
if(strlen(term_begin)) {
config.complist[currentterm++] = term_begin;
}
term_begin = term_end + 1;
}
config.complist[currentterm] = NULL;
}
return err;
}
/**
* free up any memory used
*/
void config_close(void) {
CONFIGELEMENT *pce;
int err;
if(config.complist) {
if(config.complist[0])
free(config.complist[0]);
free(config.complist);
}
free(config.configfile);
pce=config_elements;
err=0;
while((pce->config_element != -1)) {
if((pce->config_element) &&
(pce->type == CONFIG_TYPE_STRING) &&
(*((char**)pce->var))) {
DPRINTF(E_DBG,L_CONF,"Freeing %s\n",pce->name);
free(*((char**)pce->var));
}
pce++;
}
}
/**
* Write the config specified in the web post to a file. This
* doesn't change the running config, just updates the config
* file.
*
* \param pwsc the connection struct from the config-update url
*/
int config_write(WS_CONNINFO *pwsc) {
FILE *configfile;
char ctime_buf[27];
time_t now;
configfile=fopen(config.configfile,"w");
if(!configfile)
return -1;
now=time(NULL);
ctime_r(&now,ctime_buf);
fprintf(configfile,"#\n# mt-daapd.conf\n#\n");
fprintf(configfile,"# Edited: %s",ctime_buf);
fprintf(configfile,"# By: %s\n",ws_getvar(pwsc,"HTTP_USER"));
fprintf(configfile,"#\n");
fprintf(configfile,"web_root\t%s\n",ws_getvar(pwsc,"web_root"));
fprintf(configfile,"port\t\t%s\n",ws_getvar(pwsc,"port"));
fprintf(configfile,"admin_pw\t%s\n",ws_getvar(pwsc,"admin_pw"));
fprintf(configfile,"mp3_dir\t\t%s\n",ws_getvar(pwsc,"mp3_dir"));
fprintf(configfile,"servername\t%s\n",ws_getvar(pwsc,"servername"));
fprintf(configfile,"runas\t\t%s\n",ws_getvar(pwsc,"runas"));
fprintf(configfile,"playlist\t%s\n",ws_getvar(pwsc,"playlist"));
if(ws_getvar(pwsc,"password") && strlen(ws_getvar(pwsc,"password")))
fprintf(configfile,"password\t%s\n",ws_getvar(pwsc,"password"));
fprintf(configfile,"extensions\t%s\n",ws_getvar(pwsc,"extensions"));
fprintf(configfile,"ssc_codectypes\t%s\n",ws_getvar(pwsc,"ssc_codectypes"));
fprintf(configfile,"ssc_prog\t%s\n",ws_getvar(pwsc,"ssc_prog"));
fprintf(configfile,"db_dir\t\t%s\n",ws_getvar(pwsc,"db_dir"));
fprintf(configfile,"rescan_interval\t%s\n",ws_getvar(pwsc,"rescan_interval"));
fprintf(configfile,"scan_type\t%s\n",ws_getvar(pwsc,"scan_type"));
if(ws_getvar(pwsc,"always_scan") && strlen(ws_getvar(pwsc,"always_scan")))
fprintf(configfile,"always_scan\t%s\n",ws_getvar(pwsc,"always_scan"));
if(ws_getvar(pwsc,"art_filename") && strlen(ws_getvar(pwsc,"art_filename")))
fprintf(configfile,"art_filename\t%s\n",ws_getvar(pwsc,"art_filename"));
if(ws_getvar(pwsc,"logfile") && strlen(ws_getvar(pwsc,"logfile")))
fprintf(configfile,"logfile\t\t%s\n",ws_getvar(pwsc,"logfile"));
fprintf(configfile,"process_m3u\t%s\n",ws_getvar(pwsc,"process_m3u"));
fprintf(configfile,"compress\t%s\n",ws_getvar(pwsc,"compress"));
fprintf(configfile,"debuglevel\t%s\n",ws_getvar(pwsc,"debuglevel"));
fprintf(configfile,"compdirs\t%s\n",ws_getvar(pwsc,"compdirs"));
fclose(configfile);
return 0;
}
/** /**
* walk through a stream doing substitution on the * walk through a stream doing substitution on the
* meta commands. This is what fills in the fields * meta commands. This is what fills in the fields
@ -670,6 +320,13 @@ void config_handler(WS_CONNINFO *pwsc) {
int file_fd; int file_fd;
struct stat sb; struct stat sb;
char *pw; char *pw;
char web_root[PATH_MAX];
int size;
size = PATH_MAX;
if(!conf_get_string("general","web_root","",web_root,&size))
DPRINTF(E_FATAL,L_CONF,"No web root!\n"); /* shouldnt' happen */
DPRINTF(E_DBG,L_CONF|L_WS,"Entering config_handler\n"); DPRINTF(E_DBG,L_CONF|L_WS,"Entering config_handler\n");
@ -687,7 +344,7 @@ void config_handler(WS_CONNINFO *pwsc) {
return; return;
} }
snprintf(path,PATH_MAX,"%s/%s",config.web_root,pwsc->uri); snprintf(path,PATH_MAX,"%s/%s",web_root,pwsc->uri);
if(!realpath(path,resolved_path)) { if(!realpath(path,resolved_path)) {
pwsc->error=errno; pwsc->error=errno;
DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path); DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path);
@ -704,8 +361,7 @@ void config_handler(WS_CONNINFO *pwsc) {
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n", DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n",
pwsc->threadno, resolved_path); pwsc->threadno, resolved_path);
if(strncmp(resolved_path,config.web_root, if(strncmp(resolved_path,web_root,strlen(web_root))) {
strlen(config.web_root))) {
pwsc->error=EINVAL; pwsc->error=EINVAL;
DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n", DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n",
pwsc->threadno,resolved_path); pwsc->threadno,resolved_path);
@ -736,32 +392,6 @@ void config_handler(WS_CONNINFO *pwsc) {
} else if (strcasecmp(pw,"rescan")==0) { } else if (strcasecmp(pw,"rescan")==0) {
config.reload=1; config.reload=1;
} }
} else if (ws_getvar(pwsc,"web_root") != NULL) {
/* Make sure we got here from a post, and then
* we need to update stuff */
pw=ws_getvar(pwsc,"admin_pw");
if(pw) {
if(config.adminpassword)
free(config.adminpassword);
config.adminpassword=strdup(pw);
}
pw=ws_getvar(pwsc,"password");
if(pw) {
if(config.readpassword)
free(config.readpassword);
config.readpassword=strdup(pw);
}
pw=ws_getvar(pwsc,"rescan_interval");
if(pw) {
config.rescan_interval=atoi(pw);
}
if(!config_file_is_readonly()) {
DPRINTF(E_INF,L_CONF|L_WS,"Updating config file\n");
config_write(pwsc);
}
} }
} }
@ -789,9 +419,17 @@ void config_handler(WS_CONNINFO *pwsc) {
* \param password password passed in the auth request * \param password password passed in the auth request
*/ */
int config_auth(char *user, char *password) { int config_auth(char *user, char *password) {
if((!password)||(!config.adminpassword)) char adminpassword[80];
return 0; int size = sizeof(adminpassword);
return !strcmp(password,config.adminpassword);
if(!conf_get_string("general","admin_pw","",adminpassword,&size)) {
return FALSE;
}
if(!password)
return FALSE;
return !strcmp(password,adminpassword);
} }
@ -822,18 +460,6 @@ void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg) {
return; return;
} }
/**
* Used to emit a string configuration value to an admin page
*
* \param pwsc web connection
* \param value the variable that was requested
* \param arg any args passwd with the meta command
*/
void config_emit_string(WS_CONNINFO *pwsc, void *value, char *arg) {
if(*((char**)value))
ws_writefd(pwsc,"%s",*((char**)value));
}
/** /**
* Emit a string to the admin page. This is an actual string, * Emit a string to the admin page. This is an actual string,
* not a pointer to a string pointer, like emit_string. * not a pointer to a string pointer, like emit_string.
@ -846,18 +472,6 @@ void config_emit_literal(WS_CONNINFO *pwsc, void *value, char *arg) {
ws_writefd(pwsc,"%s",(char*)value); ws_writefd(pwsc,"%s",(char*)value);
} }
/**
* Emit an integer valut to the web page.
*
* \param pwsc web connection
* \param value the variable that was requested
* \param arg any args passwd with the meta command
*/
void config_emit_int(WS_CONNINFO *pwsc, void *value, char *arg) {
ws_writefd(pwsc,"%d",*((int*)value));
}
/** /**
* Dumps the status and control block to the web page. This is the * Dumps the status and control block to the web page. This is the
* page that offers the user the ability to rescan, or stop the * page that offers the user the ability to rescan, or stop the
@ -1130,24 +744,6 @@ void config_emit_user(WS_CONNINFO *pwsc, void *value, char *arg) {
} }
return; return;
} }
/**
* Check to see if the config file is read only
*
* \returns 1 if the file is readonly, 0 otherwise
*/
int config_file_is_readonly(void) {
FILE *fin;
fin=fopen(config.configfile,"r+");
if(!fin) {
return 1;
}
fclose(fin);
return 0;
}
/** /**
* implement the READONLY command. This is really a hack so * implement the READONLY command. This is really a hack so
* that the html config form isn't editable if the config file * that the html config form isn't editable if the config file
@ -1158,7 +754,7 @@ int config_file_is_readonly(void) {
* \param arg any args passwd with the meta command. Unused * \param arg any args passwd with the meta command. Unused
*/ */
void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg) { void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg) {
if(config_file_is_readonly()) { if(!conf_iswritable()) {
ws_writefd(pwsc,"readonly=\"readonly\""); ws_writefd(pwsc,"readonly=\"readonly\"");
} }
} }
@ -1176,10 +772,17 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
char path[PATH_MAX]; char path[PATH_MAX];
int file_fd; int file_fd;
struct stat sb; struct stat sb;
char web_root[PATH_MAX];
int size;
size = sizeof(web_root);
if(!conf_get_string("general","web_root","/tmp",web_root,&size)) {
DPRINTF(E_FATAL,L_WS,"No web root!\n");
}
DPRINTF(E_DBG,L_CONF|L_WS,"Preparing to include %s\n",arg); DPRINTF(E_DBG,L_CONF|L_WS,"Preparing to include %s\n",arg);
snprintf(path,PATH_MAX,"%s/%s",config.web_root,arg); snprintf(path,PATH_MAX,"%s/%s",web_root,arg);
if(!realpath(path,resolved_path)) { if(!realpath(path,resolved_path)) {
pwsc->error=errno; pwsc->error=errno;
DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path); DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path);
@ -1198,8 +801,7 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n", DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n",
pwsc->threadno, resolved_path); pwsc->threadno, resolved_path);
if(strncmp(resolved_path,config.web_root, if(strncmp(resolved_path,web_root,strlen(web_root))) {
strlen(config.web_root))) {
pwsc->error=EINVAL; pwsc->error=EINVAL;
DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n", DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n",
pwsc->threadno,resolved_path); pwsc->threadno,resolved_path);
@ -1322,17 +924,6 @@ void config_emit_version(WS_CONNINFO *pwsc, void *value, char *arg) {
ws_writefd(pwsc,"%s",VERSION); ws_writefd(pwsc,"%s",VERSION);
} }
/**
* implement the DEBUGLEVEL command
*
* @param pwsc web connection
* @param value the variable that was requested. Unused.
* @param arg any args passed with the meta command. Unused.
*/
void config_emit_debuglevel(WS_CONNINFO *pwsc, void *value, char *arg) {
ws_writefd(pwsc,"%d",err_getlevel());
}
/** /**
* implement the SYSTEM command. * implement the SYSTEM command.
* *

View File

@ -25,14 +25,11 @@
#include "daapd.h" #include "daapd.h"
#include "webserver.h" #include "webserver.h"
extern int config_read(char *file);
extern int config_write(WS_CONNINFO *pwsc);
extern int config_auth(char *user, char *password); extern int config_auth(char *user, char *password);
extern void config_handler(WS_CONNINFO *pwsc); extern void config_handler(WS_CONNINFO *pwsc);
extern void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...); extern void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...);
extern int config_get_session_count(void); extern int config_get_session_count(void);
extern int config_get_next_session(void); extern int config_get_next_session(void);
extern void config_close(void);
/** thread local storage */ /** thread local storage */
typedef struct tag_scan_status { typedef struct tag_scan_status {

View File

@ -50,6 +50,8 @@ typedef struct tag_config {
int use_mdns; /**< Should we do rendezvous advertisements? */ int use_mdns; /**< Should we do rendezvous advertisements? */
int stop; /**< Time to exit? */ int stop; /**< Time to exit? */
int reload; /**< Time to reload and/or rescan the database? */ int reload; /**< Time to reload and/or rescan the database? */
#if 0
char *configfile; /**< path to config file */ char *configfile; /**< path to config file */
char *web_root; /**< path to the dir containing the web files */ char *web_root; /**< path to the dir containing the web files */
char *iface; /**< interface to advertise on */ char *iface; /**< interface to advertise on */
@ -76,6 +78,8 @@ typedef struct tag_config {
char *dbtype; /**< db backend type */ char *dbtype; /**< db backend type */
char *dbparms; /**< parameters for the db backend */ char *dbparms; /**< parameters for the db backend */
char **complist; /**< list of compilation directories */ char **complist; /**< list of compilation directories */
#endif
STATS stats; /**< Stats structure (see above) */ STATS stats; /**< Stats structure (see above) */
WSHANDLE server; /**< webserver handle */ WSHANDLE server; /**< webserver handle */
} CONFIG; } CONFIG;

View File

@ -37,6 +37,7 @@
#include <netinet/in.h> #include <netinet/in.h>
#endif #endif
#include "conf.h"
#include "db-generic.h" #include "db-generic.h"
#include "configfile.h" #include "configfile.h"
#include "err.h" #include "err.h"
@ -111,14 +112,19 @@ typedef struct tag_output_info {
* \returns 1 if auth successful, 0 otherwise * \returns 1 if auth successful, 0 otherwise
*/ */
int daap_auth(char *username, char *password) { int daap_auth(char *username, char *password) {
char readpassword[40];
int size = sizeof(readpassword);
conf_get_string("general","password","",readpassword,&size);
if((password == NULL) && if((password == NULL) &&
((config.readpassword == NULL) || (strlen(config.readpassword)==0))) ((readpassword == NULL) || (strlen(readpassword)==0)))
return 1; return 1;
if(password == NULL) if(password == NULL)
return 0; return 0;
return !strcasecmp(password,config.readpassword); return !strcasecmp(password,readpassword);
} }
/** /**
@ -770,7 +776,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
lseek(file_fd,0,SEEK_SET); lseek(file_fd,0,SEEK_SET);
/* Re-adjust content length for cover art */ /* Re-adjust content length for cover art */
if((config.artfilename) && if((conf_isset("general","art_filename")) &&
((img_fd=da_get_image_fd(pmp3->path)) != -1)) { ((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
fstat(img_fd, &sb); fstat(img_fd, &sb);
img_size = sb.st_size; img_size = sb.st_size;
@ -820,7 +826,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
if(!offset) if(!offset)
config.stats.songs_served++; /* FIXME: remove stat races */ config.stats.songs_served++; /* FIXME: remove stat races */
if((config.artfilename) && if((conf_isset("general","art_filenaem")) &&
(!offset) && (!offset) &&
((img_fd=da_get_image_fd(pmp3->path)) != -1)) { ((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
if (strncasecmp(pmp3->type,"mp3",4) ==0) { if (strncasecmp(pmp3->type,"mp3",4) ==0) {
@ -1341,8 +1347,12 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
unsigned char *current = dbinfo_response; unsigned char *current = dbinfo_response;
int namelen; int namelen;
int count; int count;
char servername[80];
int size;
namelen=(int) strlen(config.servername); conf_get_string("general","servername","mt-daapd",servername,&size);
namelen=(int) strlen(servername);
current += db_dmap_add_container(current,"avdb",105 + namelen); current += db_dmap_add_container(current,"avdb",105 + namelen);
current += db_dmap_add_int(current,"mstt",200); /* 12 */ current += db_dmap_add_int(current,"mstt",200); /* 12 */
@ -1352,7 +1362,7 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
current += db_dmap_add_container(current,"mlcl",52 + namelen); current += db_dmap_add_container(current,"mlcl",52 + namelen);
current += db_dmap_add_container(current,"mlit",44 + namelen); current += db_dmap_add_container(current,"mlit",44 + namelen);
current += db_dmap_add_int(current,"miid",1); /* 12 */ current += db_dmap_add_int(current,"miid",1); /* 12 */
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + namelen */ current += db_dmap_add_string(current,"minm",servername); /* 8 + namelen */
db_get_song_count(NULL,&count); db_get_song_count(NULL,&count);
current += db_dmap_add_int(current,"mimc",count); /* 12 */ current += db_dmap_add_int(current,"mimc",count); /* 12 */
db_get_playlist_count(NULL,&count); db_get_playlist_count(NULL,&count);
@ -1431,8 +1441,12 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
char *client_version; char *client_version;
int mpro = 2 << 16; int mpro = 2 << 16;
int apro = 3 << 16; int apro = 3 << 16;
char servername[80];
int size;
int actual_length=130 + (int) strlen(config.servername); conf_get_string("general","servername","mt-daapd",servername,&size);
int actual_length=130 + (int) strlen(servername);
if(actual_length > sizeof(server_info)) { if(actual_length > sizeof(server_info)) {
DPRINTF(E_FATAL,L_DAAP,"Server name too long.\n"); DPRINTF(E_FATAL,L_DAAP,"Server name too long.\n");
@ -1456,10 +1470,10 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
current += db_dmap_add_int(current,"mpro",mpro); /* 12 */ current += db_dmap_add_int(current,"mpro",mpro); /* 12 */
current += db_dmap_add_int(current,"apro",apro); /* 12 */ current += db_dmap_add_int(current,"apro",apro); /* 12 */
current += db_dmap_add_int(current,"mstm",1800); /* 12 */ current += db_dmap_add_int(current,"mstm",1800); /* 12 */
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + strlen(name) */ current += db_dmap_add_string(current,"minm",servername); /* 8 + strlen(name) */
current += db_dmap_add_char(current,"msau", /* 9 */ current += db_dmap_add_char(current,"msau", /* 9 */
config.readpassword != NULL ? 2 : 0); conf_isset("general","password") ? 2 : 0);
current += db_dmap_add_char(current,"msex",0); /* 9 */ current += db_dmap_add_char(current,"msex",0); /* 9 */
current += db_dmap_add_char(current,"msix",0); /* 9 */ current += db_dmap_add_char(current,"msix",0); /* 9 */
current += db_dmap_add_char(current,"msbr",0); /* 9 */ current += db_dmap_add_char(current,"msbr",0); /* 9 */
@ -1481,24 +1495,24 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
void dispatch_error(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, char *container, char *error) { void dispatch_error(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, char *container, char *error) {
unsigned char *block, *current; unsigned char *block, *current;
int len; int len;
len = 12 + 8 + 8 + (int) strlen(error); len = 12 + 8 + 8 + (int) strlen(error);
block = (unsigned char *)malloc(len); block = (unsigned char *)malloc(len);
if(!block) if(!block)
DPRINTF(E_FATAL,L_DAAP,"Malloc error\n"); DPRINTF(E_FATAL,L_DAAP,"Malloc error\n");
current = block; current = block;
current += db_dmap_add_container(current,container,len - 8); current += db_dmap_add_container(current,container,len - 8);
current += db_dmap_add_int(current,"mstt",500); current += db_dmap_add_int(current,"mstt",500);
current += db_dmap_add_string(current,"msts",error); current += db_dmap_add_string(current,"msts",error);
dispatch_output_start(pwsc,pqi,len); dispatch_output_start(pwsc,pqi,len);
dispatch_output_write(pwsc,pqi,block,len); dispatch_output_write(pwsc,pqi,block,len);
dispatch_output_end(pwsc,pqi); dispatch_output_end(pwsc,pqi);
free(block); free(block);
pwsc->close=1; pwsc->close=1;
} }

View File

@ -37,6 +37,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "conf.h"
#include "configfile.h" #include "configfile.h"
#include "err.h" #include "err.h"
#include "restart.h" #include "restart.h"
@ -85,17 +86,26 @@ int fcopyblock(FILE *fromfp, int tofd, size_t size);
/* /*
* get_image_fd * get_image_fd
* *
* Get a file descriptor for a piece of cover art. * Get a file descriptor for a piece of cover art.
*/ */
int da_get_image_fd(char *filename) { int da_get_image_fd(char *filename) {
char buffer[PATH_MAX]; char buffer[PATH_MAX];
char *path_end; char *path_end;
int fd; int fd;
char artfilename[PATH_MAX];
int size;
size = sizeof(artfilename);
if(!conf_get_string("general","art_filename","_folderOpenImage.jpg",
artfilename,&size)) {
return -1;
}
strncpy(buffer,filename,sizeof(buffer)); strncpy(buffer,filename,sizeof(buffer));
path_end = strrchr(buffer,'/'); path_end = strrchr(buffer,'/');
strcpy(path_end+1,config.artfilename); strcpy(path_end+1,artfilename);
fd = open(buffer,O_RDONLY); fd = open(buffer,O_RDONLY);
if(fd != -1) if(fd != -1)
DPRINTF(E_INF,L_ART,"Found image file %s (fd %d)\n",buffer,fd); DPRINTF(E_INF,L_ART,"Found image file %s (fd %d)\n",buffer,fd);
return fd; return fd;
@ -104,16 +114,16 @@ int da_get_image_fd(char *filename) {
/* /*
* get_current_tag_info * get_current_tag_info
* *
* Get the current tag from the file. We need to do this to determine whether we * Get the current tag from the file. We need to do this to determine whether we
* are dealing with an id3v2.2 or id3v2.3 tag so we can decide which artwork to * are dealing with an id3v2.2 or id3v2.3 tag so we can decide which artwork to
* attach to the song * attach to the song
*/ */
int *da_get_current_tag_info(int file_fd) { int *da_get_current_tag_info(int file_fd) {
unsigned char buffer[10]; unsigned char buffer[10];
int *tag_info; int *tag_info;
tag_info = (int *) calloc(2,sizeof(int)); tag_info = (int *) calloc(2,sizeof(int));
r_read(file_fd,buffer,10); r_read(file_fd,buffer,10);
if (strncmp((char*)buffer,"ID3", 3) == 0 ) { if (strncmp((char*)buffer,"ID3", 3) == 0 ) {
tag_info[0] = buffer[3]; tag_info[0] = buffer[3];
@ -176,9 +186,9 @@ int da_attach_image(int img_fd, int out_fd, int mp3_fd, int offset)
buffer[2] = ( tag_size & 0x3F80 ) >> 7; buffer[2] = ( tag_size & 0x3F80 ) >> 7;
buffer[1] = ( tag_size & 0x1FC000 ) >> 14; buffer[1] = ( tag_size & 0x1FC000 ) >> 14;
buffer[0] = ( tag_size & 0xFE00000 ) >> 21; buffer[0] = ( tag_size & 0xFE00000 ) >> 21;
r_write(out_fd,buffer,4); r_write(out_fd,buffer,4);
if (tag_info[0] == 3) { if (tag_info[0] == 3) {
r_write(out_fd,"APIC\0",5); r_write(out_fd,"APIC\0",5);
img_size = id3v3_image_size(img_size); img_size = id3v3_image_size(img_size);
@ -288,8 +298,17 @@ off_t da_aac_insert_covr_atom(off_t extra_size, int out_fd, FILE *aac_fp,
char *cp; char *cp;
unsigned char img_type_flag = 0; unsigned char img_type_flag = 0;
char artfilename[PATH_MAX];
int size;
size = sizeof(artfilename);
if(!conf_get_string("general","art_filename","_folderOpenImage.jpg",
artfilename,&size)) {
return 0;
}
/* Figure out image file type since this needs to be encoded in the atom. */ /* Figure out image file type since this needs to be encoded in the atom. */
cp = strrchr(config.artfilename, '.'); cp = strrchr(artfilename, '.');
if (cp) { if (cp) {
if (!strcasecmp(cp, ".jpeg") || !strcasecmp(cp, ".jpg")) { if (!strcasecmp(cp, ".jpeg") || !strcasecmp(cp, ".jpg")) {
img_type_flag = 0x0d; img_type_flag = 0x0d;
@ -381,7 +400,7 @@ off_t da_aac_insert_covr_atom(off_t extra_size, int out_fd, FILE *aac_fp,
buffer[1] = ( atom_length >> 16 ) & 0xFF; buffer[1] = ( atom_length >> 16 ) & 0xFF;
buffer[0] = ( atom_length >> 24 ) & 0xFF; buffer[0] = ( atom_length >> 24 ) & 0xFF;
r_write(out_fd, buffer, 4); r_write(out_fd, buffer, 4);
r_write(out_fd, "covr", 4); r_write(out_fd, "covr", 4);
/* Write out 'data' atom */ /* Write out 'data' atom */
@ -476,7 +495,7 @@ off_t da_aac_attach_image(int img_fd, int out_fd, int aac_fd, int offset)
aac_fp = fdopen(dup(aac_fd), "r"); aac_fp = fdopen(dup(aac_fd), "r");
stco_atom_pos = scan_aac_drilltoatom(aac_fp, stco_atom_pos = scan_aac_drilltoatom(aac_fp,
"moov:trak:mdia:minf:stbl:stco", "moov:trak:mdia:minf:stbl:stco",
&atom_length); &atom_length);
ilst_atom_pos = scan_aac_drilltoatom(aac_fp, "moov:udta:meta:ilst", ilst_atom_pos = scan_aac_drilltoatom(aac_fp, "moov:udta:meta:ilst",
@ -485,7 +504,7 @@ off_t da_aac_attach_image(int img_fd, int out_fd, int aac_fd, int offset)
if (last_pos != -1) { if (last_pos != -1) {
if (offset >= last_pos) { if (offset >= last_pos) {
/* Offset is in the actual music data so don't bother processing /* Offset is in the actual music data so don't bother processing
meta data. */ meta data. */
return 0; return 0;
} }

View File

@ -247,6 +247,9 @@ int _ll_update_item(LL_ITEM *pli, void* vpval, int ival, int type) {
LL_ITEM *ll_fetch_item(LL *pl, char *key) { LL_ITEM *ll_fetch_item(LL *pl, char *key) {
LL_ITEM *current; LL_ITEM *current;
if(!pl)
return NULL;
current = pl->itemlist.next; current = pl->itemlist.next;
while(current) { while(current) {
if(pl->flags & LL_FLAG_HONORCASE) { if(pl->flags & LL_FLAG_HONORCASE) {

View File

@ -69,6 +69,7 @@
#include <sys/wait.h> #include <sys/wait.h>
#endif #endif
#include "conf.h"
#include "configfile.h" #include "configfile.h"
#include "dispatch.h" #include "dispatch.h"
#include "err.h" #include "err.h"
@ -170,6 +171,16 @@ int main(int argc, char *argv[]) {
int force_non_root=0; int force_non_root=0;
int skip_initial=0; int skip_initial=0;
int size;
char logfile[PATH_MAX];
char db_type[40];
char db_parms[PATH_MAX];
char mp3_dir[PATH_MAX];
char web_root[PATH_MAX];
char runas[40];
char servername[PATH_MAX];
char iface[20];
int err; int err;
char *perr; char *perr;
@ -246,7 +257,7 @@ int main(int argc, char *argv[]) {
config.stats.start_time=start_time=(int)time(NULL); config.stats.start_time=start_time=(int)time(NULL);
config.stop=0; config.stop=0;
if(config_read(configfile)) { if(conf_read(configfile) != CONF_E_SUCCESS) {
fprintf(stderr,"Error reading config file (%s)\n",configfile); fprintf(stderr,"Error reading config file (%s)\n",configfile);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -254,8 +265,9 @@ int main(int argc, char *argv[]) {
DPRINTF(E_LOG,L_MAIN,"Starting with debuglevel %d\n",err_getlevel()); DPRINTF(E_LOG,L_MAIN,"Starting with debuglevel %d\n",err_getlevel());
if(!foreground) { if(!foreground) {
if(config.logfile) { size = PATH_MAX;
err_setdest(config.logfile,LOGDEST_LOGFILE); if(conf_get_string("general","logfile",NULL,logfile,&size)) {
err_setdest(logfile,LOGDEST_LOGFILE);
} else { } else {
err_setdest("mt-daapd",LOGDEST_SYSLOG); err_setdest("mt-daapd",LOGDEST_SYSLOG);
} }
@ -264,7 +276,9 @@ int main(int argc, char *argv[]) {
#ifndef WITHOUT_MDNS #ifndef WITHOUT_MDNS
if(config.use_mdns) { if(config.use_mdns) {
DPRINTF(E_LOG,L_MAIN,"Starting rendezvous daemon\n"); DPRINTF(E_LOG,L_MAIN,"Starting rendezvous daemon\n");
if(rend_init(config.runas)) { size = sizeof(runas);
conf_get_string("general","runas","nobody",runas,&size);
if(rend_init(runas)) {
DPRINTF(E_FATAL,L_MAIN|L_REND,"Error in rend_init: %s\n",strerror(errno)); DPRINTF(E_FATAL,L_MAIN|L_REND,"Error in rend_init: %s\n",strerror(errno));
} }
} }
@ -277,10 +291,16 @@ 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 */
err=db_open(&perr,config.dbtype,config.dbparms); size = sizeof(db_type);
conf_get_string("general","db_type","sqlite",db_type,&size);
size = sizeof(db_parms);
conf_get_string("general","db_parms","/var/cache/mt-daapd",db_parms,&size);
err=db_open(&perr,db_type,db_parms);
if(err) if(err) {
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",perr); DPRINTF(E_FATAL,L_MAIN|L_DB,"Error: db_open %s/%s: %s\n",
db_type,db_parms,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");
@ -288,19 +308,24 @@ int main(int argc, char *argv[]) {
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_init: %s\n",strerror(errno)); DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_init: %s\n",strerror(errno));
} }
size = sizeof(mp3_dir);
conf_get_string("general","mp3_dir","/mnt/mp3",mp3_dir,&size);
if(!skip_initial) { if(!skip_initial) {
DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan of %s\n",config.mp3dir); DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan of %s\n",mp3_dir);
if(scan_init(config.mp3dir)) {
if(scan_init(mp3_dir)) {
DPRINTF(E_FATAL,L_MAIN|L_SCAN,"Error scanning MP3 files: %s\n",strerror(errno)); DPRINTF(E_FATAL,L_MAIN|L_SCAN,"Error scanning MP3 files: %s\n",strerror(errno));
} }
} }
/* start up the web server */ /* start up the web server */
ws_config.web_root=config.web_root; size = sizeof(web_root);
ws_config.port=config.port; conf_get_string("general","web_root",NULL,web_root,&size);
ws_config.web_root=web_root;
ws_config.port=conf_get_int("general","port",3689);
DPRINTF(E_LOG,L_MAIN|L_WS,"Starting web server from %s on port %d\n", DPRINTF(E_LOG,L_MAIN|L_WS,"Starting web server from %s on port %d\n",
config.web_root, config.port); ws_config.web_root, ws_config.port);
config.server=ws_start(&ws_config); config.server=ws_start(&ws_config);
if(!config.server) { if(!config.server) {
@ -319,8 +344,12 @@ int main(int argc, char *argv[]) {
#ifndef WITHOUT_MDNS #ifndef WITHOUT_MDNS
if(config.use_mdns) { /* register services */ if(config.use_mdns) { /* register services */
DPRINTF(E_LOG,L_MAIN|L_REND,"Registering rendezvous names\n"); DPRINTF(E_LOG,L_MAIN|L_REND,"Registering rendezvous names\n");
rend_register(config.servername,"_daap._tcp",config.port,config.iface); size = sizeof(servername);
rend_register(config.servername,"_http._tcp",config.port,config.iface); conf_get_string("general","servername","mt-daapd",servername,&size);
size = sizeof(iface);
conf_get_string("general","interface","",iface,&size);
rend_register(servername,"_daap._tcp",ws_config.port,iface);
rend_register(servername,"_http._tcp",ws_config.port,iface);
} }
#endif #endif
@ -331,8 +360,10 @@ int main(int argc, char *argv[]) {
end_time-start_time); end_time-start_time);
while(!config.stop) { while(!config.stop) {
if((config.rescan_interval) && (rescan_counter > config.rescan_interval)) { if((conf_get_int("general","rescan_interval",0) &&
if((config.always_scan) || (config_get_session_count())) { (rescan_counter > conf_get_int("general","rescan_interval",0)))) {
if((conf_get_int("general","always_scan",0)) ||
(config_get_session_count())) {
config.reload=1; config.reload=1;
} else { } else {
DPRINTF(E_DBG,L_MAIN|L_SCAN|L_DB,"Skipped bground scan... no users\n"); DPRINTF(E_DBG,L_MAIN|L_SCAN|L_DB,"Skipped bground scan... no users\n");
@ -345,7 +376,9 @@ int main(int argc, char *argv[]) {
start_time=(int) time(NULL); start_time=(int) 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");
if(scan_init(config.mp3dir)) { size = PATH_MAX;
conf_get_string("general","mp3_dir","/mnt/mp3",mp3_dir,&size);
if(scan_init(mp3_dir)) {
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Error rescanning... exiting\n"); DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Error rescanning... exiting\n");
config.stop=1; config.stop=1;
} }
@ -378,7 +411,7 @@ int main(int argc, char *argv[]) {
ws_stop(config.server); ws_stop(config.server);
*/ */
config_close(); conf_close();
DPRINTF(E_LOG,L_MAIN|L_DB,"Closing database\n"); DPRINTF(E_LOG,L_MAIN|L_DB,"Closing database\n");
db_deinit(); db_deinit();

View File

@ -46,6 +46,7 @@
#endif #endif
#include <sys/stat.h> #include <sys/stat.h>
#include "conf.h"
#include "daapd.h" #include "daapd.h"
#include "db-generic.h" #include "db-generic.h"
#include "err.h" #include "err.h"
@ -261,6 +262,7 @@ int scan_init(char *path) {
* @returns 1 if it is a compilation path, 0 otherwise * @returns 1 if it is a compilation path, 0 otherwise
*/ */
int scan_is_compdir(char *path) { int scan_is_compdir(char *path) {
#if 0
int current=0; int current=0;
if(!config.complist) if(!config.complist)
@ -271,7 +273,7 @@ int scan_is_compdir(char *path) {
return 1; return 1;
current++; current++;
} }
#endif
return 0; return 0;
} }
@ -294,6 +296,11 @@ int scan_path(char *path) {
MP3FILE *pmp3; MP3FILE *pmp3;
int is_compdir; int is_compdir;
char extensions[PATH_MAX];
int size = sizeof(extensions);
conf_get_string("general","extensions",".mp3,.m4a,.m4p",extensions,&size);
if((current_dir=opendir(path)) == NULL) { if((current_dir=opendir(path)) == NULL) {
DPRINTF(E_WARN,L_SCAN,"opendir: %s\n",strerror(errno)); DPRINTF(E_WARN,L_SCAN,"opendir: %s\n",strerror(errno));
return -1; return -1;
@ -339,13 +346,13 @@ int scan_path(char *path) {
/* process the file */ /* process the file */
if(strlen(pde->d_name) > 4) { if(strlen(pde->d_name) > 4) {
if((strcasecmp(".m3u",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0) && if((strcasecmp(".m3u",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0) &&
config.process_m3u){ conf_get_int("general","process_m3u",0)){
/* we found an m3u file */ /* we found an m3u file */
scan_add_playlistlist(mp3_path); scan_add_playlistlist(mp3_path);
} else if((strcasecmp(".xml",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0)) { } else if((strcasecmp(".xml",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0)) {
scan_add_playlistlist(mp3_path); scan_add_playlistlist(mp3_path);
} else if (((ext = strrchr(pde->d_name, '.')) != NULL) && } else if (((ext = strrchr(pde->d_name, '.')) != NULL) &&
(strcasestr(config.extensions, ext))) { (strcasestr(extensions, ext))) {
/* only scan if it's been changed, or empty db */ /* only scan if it's been changed, or empty db */
modified_time=(int) sb.st_mtime; modified_time=(int) sb.st_mtime;
pmp3=db_fetch_path(NULL,mp3_path,0); pmp3=db_fetch_path(NULL,mp3_path,0);

View File

@ -33,6 +33,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <syslog.h> #include <syslog.h>
#include <wait.h>
#ifdef HAVE_UNISTD_H #ifdef HAVE_UNISTD_H
# include <unistd.h> # include <unistd.h>
#endif #endif
@ -44,6 +46,7 @@
#include <sys/time.h> #include <sys/time.h>
#include <sys/resource.h> #include <sys/resource.h>
#include "conf.h"
#include "err.h" #include "err.h"
#include "daapd.h" #include "daapd.h"
#include "os-unix.h" #include "os-unix.h"
@ -71,13 +74,17 @@ char *_os_pidfile = PIDFILE;
/** /**
* this initializes the platform... sets up signal handlers, forks to the * this initializes the platform... sets up signal handlers, forks to the
* background, etc * background, etc
* *
* @param foreground whether to run in fg or fork to bg * @param foreground whether to run in fg or fork to bg
* @returns TRUE on success, FALSE otherwise * @returns TRUE on success, FALSE otherwise
*/ */
int os_init(int foreground) { int os_init(int foreground) {
int pid_fd; int pid_fd;
FILE *pid_fp=NULL; FILE *pid_fp=NULL;
char runas[80];
int size = sizeof(runas);
conf_get_string("general","runas","nobody",runas,&size);
/* open the pidfile, so it can be written once we detach */ /* open the pidfile, so it can be written once we detach */
if(!foreground) { if(!foreground) {
@ -94,7 +101,7 @@ int os_init(int foreground) {
} }
// Drop privs here // Drop privs here
if(os_drop_privs(config.runas)) { if(os_drop_privs(runas)) {
DPRINTF(E_FATAL,L_MAIN,"Error in drop_privs: %s\n",strerror(errno)); DPRINTF(E_FATAL,L_MAIN,"Error in drop_privs: %s\n",strerror(errno));
} }
@ -113,7 +120,7 @@ int os_init(int foreground) {
fprintf(pid_fp,"%d\n",_os_signal_pid); fprintf(pid_fp,"%d\n",_os_signal_pid);
fclose(pid_fp); fclose(pid_fp);
} }
return TRUE; return TRUE;
} }
@ -129,7 +136,7 @@ void os_deinit(void) {
/** /**
* start syslogging * start syslogging
* *
* @returns TRUE on success, FALSE otherwise * @returns TRUE on success, FALSE otherwise
*/ */
int os_opensyslog(void) { int os_opensyslog(void) {
@ -140,7 +147,7 @@ int os_opensyslog(void) {
/** /**
* stop syslogging * stop syslogging
* *
* @returns TRUE on success, FALSE otherwise * @returns TRUE on success, FALSE otherwise
*/ */
int os_closesyslog(void) { int os_closesyslog(void) {
@ -150,7 +157,7 @@ int os_closesyslog(void) {
/** /**
* log a syslog message * log a syslog message
* *
* @param level log level (1-9: 1=fatal, 9=debug) * @param level log level (1-9: 1=fatal, 9=debug)
* @param msg message to log to the syslog * @param msg message to log to the syslog
* @returns TRUE on success, FALSE otherwise * @returns TRUE on success, FALSE otherwise
@ -258,7 +265,7 @@ int os_drop_privs(char *user) {
if(atoi(user)) { if(atoi(user)) {
pw=getpwuid((uid_t)atoi(user)); /* doh! */ pw=getpwuid((uid_t)atoi(user)); /* doh! */
} else { } else {
pw=getpwnam(config.runas); pw=getpwnam(user);
} }
if(pw) { if(pw) {
@ -376,4 +383,4 @@ int _os_start_signal_handler(pthread_t *handler_tid) {
void os_set_pidfile(char *file) { void os_set_pidfile(char *file) {
_os_pidfile = file; _os_pidfile = file;
} }

View File

@ -31,6 +31,7 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include "conf.h"
#include "daapd.h" #include "daapd.h"
#include "err.h" #include "err.h"
#include "mp3-scanner.h" #include "mp3-scanner.h"
@ -198,7 +199,7 @@ char *scan_winamp_genre[] = {
"Chanson", "Chanson",
"Opera", "Opera",
"Chamber Music", "Chamber Music",
"Sonata", // 105 "Sonata", // 105
"Symphony", "Symphony",
"Booty Bass", "Booty Bass",
"Primus", "Primus",
@ -267,7 +268,7 @@ int scan_get_mp3info(char *filename, MP3FILE *pmp3) {
} }
/** /**
* decide if a string is numeric or not... * decide if a string is numeric or not...
* *
* @param str string to evaluate * @param str string to evaluate
* @returns 1 if number, 0 otherwise * @returns 1 if number, 0 otherwise
@ -305,7 +306,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
} }
pid3tag=id3_file_tag(pid3file); pid3tag=id3_file_tag(pid3file);
if(!pid3tag) { if(!pid3tag) {
err=errno; err=errno;
id3_file_close(pid3file); id3_file_close(pid3file);
@ -332,7 +333,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
} }
if(((pid3frame->id[0] == 'T')||(strcmp(pid3frame->id,"COMM")==0)) && if(((pid3frame->id[0] == 'T')||(strcmp(pid3frame->id,"COMM")==0)) &&
(id3_field_getnstrings(&pid3frame->fields[1]))) (id3_field_getnstrings(&pid3frame->fields[1])))
have_text=1; have_text=1;
if(have_text) { if(have_text) {
@ -345,7 +346,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
* it's just plain wrong. * it's just plain wrong.
*/ */
have_utf8=1; have_utf8=1;
if(config.latin1_tags) { if(conf_get_int("general","latin1_tags",0)) {
utf8_text=(char *)id3_ucs4_latin1duplicate(native_text); utf8_text=(char *)id3_ucs4_latin1duplicate(native_text);
} else { } else {
utf8_text=(char *)id3_ucs4_utf8duplicate(native_text); utf8_text=(char *)id3_ucs4_utf8duplicate(native_text);
@ -394,7 +395,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
} else if ((pmp3->genre[0] == '(') && (isdigit(pmp3->genre[1]))) { } else if ((pmp3->genre[0] == '(') && (isdigit(pmp3->genre[1]))) {
genre=atoi((char*)&pmp3->genre[1]); genre=atoi((char*)&pmp3->genre[1]);
got_numeric_genre=1; got_numeric_genre=1;
} }
if(got_numeric_genre) { if(got_numeric_genre) {
if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN)) if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN))
@ -450,7 +451,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
* *
* iTunes_CDDB_IDs * iTunes_CDDB_IDs
* iTunNORM * iTunNORM
* *
* If other apps stuff crap into comment fields, then we'll ignore them * If other apps stuff crap into comment fields, then we'll ignore them
* here. * here.
*/ */
@ -487,7 +488,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
} }
/** /**
* Decode an mp3 frame header. Determine layer, bitrate, * Decode an mp3 frame header. Determine layer, bitrate,
* samplerate, etc, and fill in the passed structure. * samplerate, etc, and fill in the passed structure.
* *
* @param frame 4 byte mp3 frame header * @param frame 4 byte mp3 frame header
@ -521,7 +522,7 @@ int scan_mp3_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi) {
if((pfi->layer == 2) || (pfi->layer == 3)) if((pfi->layer == 2) || (pfi->layer == 3))
layer_index = 4; layer_index = 4;
break; break;
case 2: case 2:
pfi->version = 2.0; pfi->version = 2.0;
sample_index=1; sample_index=1;
if(pfi->layer == 1) if(pfi->layer == 1)
@ -573,11 +574,11 @@ int scan_mp3_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi) {
pfi->is_valid=0; pfi->is_valid=0;
return -1; return -1;
} }
pfi->bitrate = scan_br_table[layer_index][bitrate_index]; pfi->bitrate = scan_br_table[layer_index][bitrate_index];
pfi->samplerate = scan_sample_table[sample_index][samplerate_index]; pfi->samplerate = scan_sample_table[sample_index][samplerate_index];
if((frame[3] & 0xC0 >> 6) == 3) if((frame[3] & 0xC0 >> 6) == 3)
pfi->stereo = 0; pfi->stereo = 0;
else else
pfi->stereo = 1; pfi->stereo = 1;
@ -647,7 +648,7 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
/* now, find the first frame */ /* now, find the first frame */
fseek(infile,pos,SEEK_SET); fseek(infile,pos,SEEK_SET);
if(fread(frame_buffer,1,sizeof(frame_buffer),infile) != sizeof(frame_buffer)) if(fread(frame_buffer,1,sizeof(frame_buffer),infile) != sizeof(frame_buffer))
return; return;
while(!found) { while(!found) {
@ -658,8 +659,8 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
DPRINTF(E_DBG,L_SCAN,"Could not find frame... quitting\n"); DPRINTF(E_DBG,L_SCAN,"Could not find frame... quitting\n");
return; return;
} }
if(!scan_mp3_decode_mp3_frame(&frame_buffer[index],&fi)) { if(!scan_mp3_decode_mp3_frame(&frame_buffer[index],&fi)) {
/* see if next frame is valid */ /* see if next frame is valid */
fseek(infile,pos + index + fi.frame_length,SEEK_SET); fseek(infile,pos + index + fi.frame_length,SEEK_SET);
if(fread(header,1,sizeof(header),infile) != sizeof(header)) { if(fread(header,1,sizeof(header),infile) != sizeof(header)) {
@ -670,7 +671,7 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
if(!scan_mp3_decode_mp3_frame(header,&fi)) if(!scan_mp3_decode_mp3_frame(header,&fi))
found=1; found=1;
} }
if(!found) if(!found)
index++; index++;
} }
@ -688,7 +689,7 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
DPRINTF(E_DBG,L_SCAN,"Invalid frame header while averaging\n"); DPRINTF(E_DBG,L_SCAN,"Invalid frame header while averaging\n");
return; return;
} }
bitrate_total += fi.bitrate; bitrate_total += fi.bitrate;
frame_count++; frame_count++;
pos += fi.frame_length; pos += fi.frame_length;
@ -705,7 +706,7 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
* do a full frame-by-frame scan of the file, counting frames * do a full frame-by-frame scan of the file, counting frames
* as we go to try and get a more accurate song length estimate. * as we go to try and get a more accurate song length estimate.
* If the song turns out to be CBR, then we'll not set the frame * If the song turns out to be CBR, then we'll not set the frame
* length. Instead we'll use the file size estimate, since it is * length. Instead we'll use the file size estimate, since it is
* more consistent with iTunes. * more consistent with iTunes.
* *
* @param infile file to scan for frame count * @param infile file to scan for frame count
@ -722,7 +723,7 @@ void scan_mp3_get_frame_count(FILE *infile, SCAN_FRAMEINFO *pfi) {
int last_bitrate=0; int last_bitrate=0;
DPRINTF(E_DBG,L_SCAN,"Starting frame count\n"); DPRINTF(E_DBG,L_SCAN,"Starting frame count\n");
fseek(infile,0,SEEK_END); fseek(infile,0,SEEK_END);
file_size=ftell(infile); file_size=ftell(infile);
@ -821,7 +822,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
if(strncmp((char*)pid3->id,"ID3",3)==0) { if(strncmp((char*)pid3->id,"ID3",3)==0) {
/* found an ID3 header... */ /* found an ID3 header... */
DPRINTF(E_DBG,L_SCAN,"Found ID3 header\n"); DPRINTF(E_DBG,L_SCAN,"Found ID3 header\n");
size = (pid3->size[0] << 21 | pid3->size[1] << 14 | size = (pid3->size[0] << 21 | pid3->size[1] << 14 |
pid3->size[2] << 7 | pid3->size[3]); pid3->size[2] << 7 | pid3->size[3]);
fp_size=size + sizeof(SCAN_ID3HEADER); fp_size=size + sizeof(SCAN_ID3HEADER);
first_check=1; first_check=1;
@ -877,7 +878,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
if(!scan_mp3_decode_mp3_frame((u_char*)frame_buffer,&fi)) { if(!scan_mp3_decode_mp3_frame((u_char*)frame_buffer,&fi)) {
found=1; found=1;
fp_size += index; fp_size += index;
} }
} else { } else {
DPRINTF(E_LOG,L_SCAN,"Could not read frame header: %s\n",file); DPRINTF(E_LOG,L_SCAN,"Could not read frame header: %s\n",file);
fclose(infile); fclose(infile);
@ -889,13 +890,13 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
} }
} }
} }
if(!found) { if(!found) {
index++; index++;
if (first_check) { if (first_check) {
/* if the header info was wrong about where the data started, /* if the header info was wrong about where the data started,
* then start a brute-force scan from the beginning of the file. * then start a brute-force scan from the beginning of the file.
* don't want to just scan forward, because we might have already * don't want to just scan forward, because we might have already
* missed the xing header * missed the xing header
*/ */
DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n"); DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n");
@ -923,7 +924,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
DPRINTF(E_DBG,L_SCAN," Layer: %d\n",fi.layer); DPRINTF(E_DBG,L_SCAN," Layer: %d\n",fi.layer);
DPRINTF(E_DBG,L_SCAN," Sample Rate: %d\n",fi.samplerate); DPRINTF(E_DBG,L_SCAN," Sample Rate: %d\n",fi.samplerate);
DPRINTF(E_DBG,L_SCAN," Bit Rate: %d\n",fi.bitrate); DPRINTF(E_DBG,L_SCAN," Bit Rate: %d\n",fi.bitrate);
/* now check for an XING header */ /* now check for an XING header */
if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) { if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) {
DPRINTF(E_DBG,L_SCAN,"Found Xing header\n"); DPRINTF(E_DBG,L_SCAN,"Found Xing header\n");
@ -931,7 +932,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
buffer[index+fi.xing_offset+4+5] << 16 | buffer[index+fi.xing_offset+4+5] << 16 |
buffer[index+fi.xing_offset+4+6] << 8 | buffer[index+fi.xing_offset+4+6] << 8 |
buffer[index+fi.xing_offset+4+7]; buffer[index+fi.xing_offset+4+7];
DPRINTF(E_DBG,L_SCAN,"Xing Flags: %02X\n",xing_flags); DPRINTF(E_DBG,L_SCAN,"Xing Flags: %02X\n",xing_flags);
if(xing_flags & 0x1) { if(xing_flags & 0x1) {
@ -943,13 +944,13 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
} }
} }
if((config.scan_type != 0) && if((conf_get_int("general","scan_type",0) != 0) &&
(fi.number_of_frames == 0) && (fi.number_of_frames == 0) &&
(!pmp3->song_length)) { (!pmp3->song_length)) {
/* We have no good estimate of song time, and we want more /* We have no good estimate of song time, and we want more
* aggressive scanning */ * aggressive scanning */
DPRINTF(E_DBG,L_SCAN,"Starting aggressive file length scan\n"); DPRINTF(E_DBG,L_SCAN,"Starting aggressive file length scan\n");
if(config.scan_type == 1) { if(conf_get_int("general","scan_type",0) == 1) {
/* get average bitrate */ /* get average bitrate */
scan_mp3_get_average_bitrate(infile, &fi); scan_mp3_get_average_bitrate(infile, &fi);
} else { } else {
@ -960,7 +961,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
pmp3->bitrate=fi.bitrate; pmp3->bitrate=fi.bitrate;
pmp3->samplerate=fi.samplerate; pmp3->samplerate=fi.samplerate;
/* guesstimate the file length */ /* guesstimate the file length */
if(!pmp3->song_length) { /* could have gotten it from the tag */ if(!pmp3->song_length) { /* could have gotten it from the tag */
/* DWB: use ms time instead of seconds, use doubles to /* DWB: use ms time instead of seconds, use doubles to

View File

@ -37,10 +37,11 @@
#ifndef WIN32 #ifndef WIN32
#include <netinet/in.h> /* htons and friends */ #include <netinet/in.h> /* htons and friends */
#include <dirent.h> /* why here? For osx 10.2, of course! */ #include <dirent.h> /* why here? For osx 10.2, of course! */
#endif #endif
#include <sys/stat.h> #include <sys/stat.h>
#include "conf.h"
#include "daapd.h" #include "daapd.h"
#include "db-generic.h" #include "db-generic.h"
#include "err.h" #include "err.h"
@ -60,16 +61,21 @@
* @returns 1 if should be converted. 0 if not * @returns 1 if should be converted. 0 if not
*/ */
int server_side_convert(char *codectype) { int server_side_convert(char *codectype) {
if ((!config.ssc_codectypes) || char ssc_codectypes[PATH_MAX];
(!config.ssc_codectypes[0]) || int size;
(!config.ssc_prog) ||
(!config.ssc_prog[0]) || size = sizeof(ssc_codectypes);
conf_get_string("general","ssc_codectypes","ogg,flac,wma,alac",
ssc_codectypes,&size);
if ((!conf_isset("general","ssc_codectypes")) ||
(!conf_isset("general","ssc_prog")) ||
(!codectype)) { (!codectype)) {
DPRINTF(E_DBG,L_SCAN,"Nope\n"); DPRINTF(E_DBG,L_SCAN,"Nope\n");
return 0; return 0;
} }
if(strcasestr(config.ssc_codectypes, codectype)) { if(strcasestr(ssc_codectypes, codectype)) {
return 1; return 1;
} }
@ -90,14 +96,23 @@ FILE *server_side_convert_open(char *path, off_t offset, unsigned long len_ms, c
char *metachars = "\"\\!(){}#*?$&<>`"; /* More?? */ char *metachars = "\"\\!(){}#*?$&<>`"; /* More?? */
char metacount = 0; char metacount = 0;
char *src,*dst; char *src,*dst;
char ssc_prog[PATH_MAX];
int size;
size = sizeof(ssc_prog);
conf_get_string("general","ssc_prog","",ssc_prog,&size);
if(ssc_prog[0] == '\0') { /* can't happen */
return NULL;
}
src=path; src=path;
while(*src) { while(*src) {
if(strchr(metachars,*src)) if(strchr(metachars,*src))
metacount+=5; metacount+=5;
src++; src++;
} }
if(metachars) { if(metachars) {
newpath = (char*)malloc(strlen(path) + metacount + 1); newpath = (char*)malloc(strlen(path) + metacount + 1);
if(!newpath) { if(!newpath) {
@ -105,7 +120,7 @@ FILE *server_side_convert_open(char *path, off_t offset, unsigned long len_ms, c
} }
src=path; src=path;
dst=newpath; dst=newpath;
while(*src) { while(*src) {
if(strchr(metachars,*src)) { if(strchr(metachars,*src)) {
*dst++='"'; *dst++='"';
@ -123,11 +138,11 @@ FILE *server_side_convert_open(char *path, off_t offset, unsigned long len_ms, c
} }
/* FIXME: is 64 enough? is there a better way to determine this? */ /* FIXME: is 64 enough? is there a better way to determine this? */
cmd=(char *)malloc(strlen(config.ssc_prog) + cmd=(char *)malloc(strlen(ssc_prog) +
strlen(path) + strlen(path) +
64); 64);
sprintf(cmd, "%s \"%s\" %ld %lu.%03lu \"%s\"", sprintf(cmd, "%s \"%s\" %ld %lu.%03lu \"%s\"",
config.ssc_prog, newpath, (long)offset, len_ms / 1000, ssc_prog, newpath, (long)offset, len_ms / 1000,
len_ms % 1000, (codectype && *codectype) ? codectype : "*"); len_ms % 1000, (codectype && *codectype) ? codectype : "*");
DPRINTF(E_INF,L_SCAN,"Executing %s\n",cmd); DPRINTF(E_INF,L_SCAN,"Executing %s\n",cmd);
f = popen(cmd, "r"); f = popen(cmd, "r");