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 \
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 \
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 \
$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC) $(FLACSRC) \
$(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_string(char *section, char *key, char *value);
extern int conf_isset(char *section, char *key);
extern int conf_iswritable(void);
extern int conf_write(void);

View File

@ -48,6 +48,7 @@
# include <sys/wait.h>
#endif
#include "conf.h"
#include "configfile.h"
#include "db-generic.h"
#include "err.h"
@ -62,9 +63,7 @@
/*
* 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_int(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_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_readonly(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_flags(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 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);
/*
@ -114,6 +109,7 @@ typedef struct tag_configelement {
/** List of all valid config entries and web interface directives */
CONFIGELEMENT config_elements[] = {
/*
{ 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_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,"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,"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,"release",(void*)VERSION,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,"version",(void*)NULL,config_emit_version },
{ 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 },
{ -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.
*
@ -241,356 +239,8 @@ int config_existdir(char *path) {
errno=ENOTDIR;
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
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
* meta commands. This is what fills in the fields
@ -670,6 +320,13 @@ void config_handler(WS_CONNINFO *pwsc) {
int file_fd;
struct stat sb;
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");
@ -687,7 +344,7 @@ void config_handler(WS_CONNINFO *pwsc) {
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)) {
pwsc->error=errno;
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",
pwsc->threadno, resolved_path);
if(strncmp(resolved_path,config.web_root,
strlen(config.web_root))) {
if(strncmp(resolved_path,web_root,strlen(web_root))) {
pwsc->error=EINVAL;
DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n",
pwsc->threadno,resolved_path);
@ -736,32 +392,6 @@ void config_handler(WS_CONNINFO *pwsc) {
} else if (strcasecmp(pw,"rescan")==0) {
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
*/
int config_auth(char *user, char *password) {
if((!password)||(!config.adminpassword))
return 0;
return !strcmp(password,config.adminpassword);
char adminpassword[80];
int size = sizeof(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;
}
/**
* 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,
* 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);
}
/**
* 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
* 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;
}
/**
* 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
* 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
*/
void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg) {
if(config_file_is_readonly()) {
if(!conf_iswritable()) {
ws_writefd(pwsc,"readonly=\"readonly\"");
}
}
@ -1176,10 +772,17 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
char path[PATH_MAX];
int file_fd;
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);
snprintf(path,PATH_MAX,"%s/%s",config.web_root,arg);
snprintf(path,PATH_MAX,"%s/%s",web_root,arg);
if(!realpath(path,resolved_path)) {
pwsc->error=errno;
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",
pwsc->threadno, resolved_path);
if(strncmp(resolved_path,config.web_root,
strlen(config.web_root))) {
if(strncmp(resolved_path,web_root,strlen(web_root))) {
pwsc->error=EINVAL;
DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n",
pwsc->threadno,resolved_path);
@ -1322,17 +924,6 @@ void config_emit_version(WS_CONNINFO *pwsc, void *value, char *arg) {
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.
*

View File

@ -25,14 +25,11 @@
#include "daapd.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 void config_handler(WS_CONNINFO *pwsc);
extern void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...);
extern int config_get_session_count(void);
extern int config_get_next_session(void);
extern void config_close(void);
/** thread local storage */
typedef struct tag_scan_status {

View File

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

View File

@ -37,6 +37,7 @@
#include <netinet/in.h>
#endif
#include "conf.h"
#include "db-generic.h"
#include "configfile.h"
#include "err.h"
@ -111,14 +112,19 @@ typedef struct tag_output_info {
* \returns 1 if auth successful, 0 otherwise
*/
int daap_auth(char *username, char *password) {
char readpassword[40];
int size = sizeof(readpassword);
conf_get_string("general","password","",readpassword,&size);
if((password == NULL) &&
((config.readpassword == NULL) || (strlen(config.readpassword)==0)))
((readpassword == NULL) || (strlen(readpassword)==0)))
return 1;
if(password == NULL)
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);
/* 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)) {
fstat(img_fd, &sb);
img_size = sb.st_size;
@ -820,7 +826,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
if(!offset)
config.stats.songs_served++; /* FIXME: remove stat races */
if((config.artfilename) &&
if((conf_isset("general","art_filenaem")) &&
(!offset) &&
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
@ -1341,8 +1347,12 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
unsigned char *current = dbinfo_response;
int namelen;
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_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,"mlit",44 + namelen);
current += db_dmap_add_int(current,"miid",1); /* 12 */
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + namelen */
current += db_dmap_add_string(current,"minm",servername); /* 8 + namelen */
db_get_song_count(NULL,&count);
current += db_dmap_add_int(current,"mimc",count); /* 12 */
db_get_playlist_count(NULL,&count);
@ -1431,8 +1441,12 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
char *client_version;
int mpro = 2 << 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)) {
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,"apro",apro); /* 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 */
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,"msix",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) {
unsigned char *block, *current;
int len;
len = 12 + 8 + 8 + (int) strlen(error);
block = (unsigned char *)malloc(len);
if(!block)
DPRINTF(E_FATAL,L_DAAP,"Malloc error\n");
current = block;
current += db_dmap_add_container(current,container,len - 8);
current += db_dmap_add_int(current,"mstt",500);
current += db_dmap_add_string(current,"msts",error);
dispatch_output_start(pwsc,pqi,len);
dispatch_output_write(pwsc,pqi,block,len);
dispatch_output_end(pwsc,pqi);
free(block);
pwsc->close=1;
}

View File

@ -37,6 +37,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include "conf.h"
#include "configfile.h"
#include "err.h"
#include "restart.h"
@ -85,17 +86,26 @@ int fcopyblock(FILE *fromfp, int tofd, size_t size);
/*
* 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) {
char buffer[PATH_MAX];
char *path_end;
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));
path_end = strrchr(buffer,'/');
strcpy(path_end+1,config.artfilename);
strcpy(path_end+1,artfilename);
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);
return fd;
@ -104,16 +114,16 @@ int da_get_image_fd(char *filename) {
/*
* get_current_tag_info
*
* 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
* 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
* attach to the song
*/
int *da_get_current_tag_info(int file_fd) {
unsigned char buffer[10];
int *tag_info;
tag_info = (int *) calloc(2,sizeof(int));
r_read(file_fd,buffer,10);
if (strncmp((char*)buffer,"ID3", 3) == 0 ) {
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[1] = ( tag_size & 0x1FC000 ) >> 14;
buffer[0] = ( tag_size & 0xFE00000 ) >> 21;
r_write(out_fd,buffer,4);
if (tag_info[0] == 3) {
r_write(out_fd,"APIC\0",5);
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;
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. */
cp = strrchr(config.artfilename, '.');
cp = strrchr(artfilename, '.');
if (cp) {
if (!strcasecmp(cp, ".jpeg") || !strcasecmp(cp, ".jpg")) {
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[0] = ( atom_length >> 24 ) & 0xFF;
r_write(out_fd, buffer, 4);
r_write(out_fd, "covr", 4);
/* 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");
stco_atom_pos = scan_aac_drilltoatom(aac_fp,
stco_atom_pos = scan_aac_drilltoatom(aac_fp,
"moov:trak:mdia:minf:stbl:stco",
&atom_length);
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 (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. */
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 *current;
if(!pl)
return NULL;
current = pl->itemlist.next;
while(current) {
if(pl->flags & LL_FLAG_HONORCASE) {

View File

@ -69,6 +69,7 @@
#include <sys/wait.h>
#endif
#include "conf.h"
#include "configfile.h"
#include "dispatch.h"
#include "err.h"
@ -170,6 +171,16 @@ int main(int argc, char *argv[]) {
int force_non_root=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;
char *perr;
@ -246,7 +257,7 @@ int main(int argc, char *argv[]) {
config.stats.start_time=start_time=(int)time(NULL);
config.stop=0;
if(config_read(configfile)) {
if(conf_read(configfile) != CONF_E_SUCCESS) {
fprintf(stderr,"Error reading config file (%s)\n",configfile);
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());
if(!foreground) {
if(config.logfile) {
err_setdest(config.logfile,LOGDEST_LOGFILE);
size = PATH_MAX;
if(conf_get_string("general","logfile",NULL,logfile,&size)) {
err_setdest(logfile,LOGDEST_LOGFILE);
} else {
err_setdest("mt-daapd",LOGDEST_SYSLOG);
}
@ -264,7 +276,9 @@ int main(int argc, char *argv[]) {
#ifndef WITHOUT_MDNS
if(config.use_mdns) {
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));
}
}
@ -277,10 +291,16 @@ int main(int argc, char *argv[]) {
}
/* 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)
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",perr);
if(err) {
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error: db_open %s/%s: %s\n",
db_type,db_parms,perr);
}
/* Initialize the database before starting */
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));
}
size = sizeof(mp3_dir);
conf_get_string("general","mp3_dir","/mnt/mp3",mp3_dir,&size);
if(!skip_initial) {
DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan of %s\n",config.mp3dir);
if(scan_init(config.mp3dir)) {
DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan of %s\n",mp3_dir);
if(scan_init(mp3_dir)) {
DPRINTF(E_FATAL,L_MAIN|L_SCAN,"Error scanning MP3 files: %s\n",strerror(errno));
}
}
/* start up the web server */
ws_config.web_root=config.web_root;
ws_config.port=config.port;
size = sizeof(web_root);
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",
config.web_root, config.port);
ws_config.web_root, ws_config.port);
config.server=ws_start(&ws_config);
if(!config.server) {
@ -319,8 +344,12 @@ int main(int argc, char *argv[]) {
#ifndef WITHOUT_MDNS
if(config.use_mdns) { /* register services */
DPRINTF(E_LOG,L_MAIN|L_REND,"Registering rendezvous names\n");
rend_register(config.servername,"_daap._tcp",config.port,config.iface);
rend_register(config.servername,"_http._tcp",config.port,config.iface);
size = sizeof(servername);
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
@ -331,8 +360,10 @@ int main(int argc, char *argv[]) {
end_time-start_time);
while(!config.stop) {
if((config.rescan_interval) && (rescan_counter > config.rescan_interval)) {
if((config.always_scan) || (config_get_session_count())) {
if((conf_get_int("general","rescan_interval",0) &&
(rescan_counter > conf_get_int("general","rescan_interval",0)))) {
if((conf_get_int("general","always_scan",0)) ||
(config_get_session_count())) {
config.reload=1;
} else {
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);
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");
config.stop=1;
}
@ -378,7 +411,7 @@ int main(int argc, char *argv[]) {
ws_stop(config.server);
*/
config_close();
conf_close();
DPRINTF(E_LOG,L_MAIN|L_DB,"Closing database\n");
db_deinit();

View File

@ -46,6 +46,7 @@
#endif
#include <sys/stat.h>
#include "conf.h"
#include "daapd.h"
#include "db-generic.h"
#include "err.h"
@ -261,6 +262,7 @@ int scan_init(char *path) {
* @returns 1 if it is a compilation path, 0 otherwise
*/
int scan_is_compdir(char *path) {
#if 0
int current=0;
if(!config.complist)
@ -271,7 +273,7 @@ int scan_is_compdir(char *path) {
return 1;
current++;
}
#endif
return 0;
}
@ -294,6 +296,11 @@ int scan_path(char *path) {
MP3FILE *pmp3;
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) {
DPRINTF(E_WARN,L_SCAN,"opendir: %s\n",strerror(errno));
return -1;
@ -339,13 +346,13 @@ int scan_path(char *path) {
/* process the file */
if(strlen(pde->d_name) > 4) {
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 */
scan_add_playlistlist(mp3_path);
} else if((strcasecmp(".xml",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0)) {
scan_add_playlistlist(mp3_path);
} 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 */
modified_time=(int) sb.st_mtime;
pmp3=db_fetch_path(NULL,mp3_path,0);

View File

@ -33,6 +33,8 @@
#include <stdlib.h>
#include <stdio.h>
#include <syslog.h>
#include <wait.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
@ -44,6 +46,7 @@
#include <sys/time.h>
#include <sys/resource.h>
#include "conf.h"
#include "err.h"
#include "daapd.h"
#include "os-unix.h"
@ -71,13 +74,17 @@ char *_os_pidfile = PIDFILE;
/**
* this initializes the platform... sets up signal handlers, forks to the
* background, etc
*
*
* @param foreground whether to run in fg or fork to bg
* @returns TRUE on success, FALSE otherwise
*/
int os_init(int foreground) {
int pid_fd;
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 */
if(!foreground) {
@ -94,7 +101,7 @@ int os_init(int foreground) {
}
// 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));
}
@ -113,7 +120,7 @@ int os_init(int foreground) {
fprintf(pid_fp,"%d\n",_os_signal_pid);
fclose(pid_fp);
}
return TRUE;
}
@ -129,7 +136,7 @@ void os_deinit(void) {
/**
* start syslogging
*
*
* @returns TRUE on success, FALSE otherwise
*/
int os_opensyslog(void) {
@ -140,7 +147,7 @@ int os_opensyslog(void) {
/**
* stop syslogging
*
*
* @returns TRUE on success, FALSE otherwise
*/
int os_closesyslog(void) {
@ -150,7 +157,7 @@ int os_closesyslog(void) {
/**
* log a syslog message
*
*
* @param level log level (1-9: 1=fatal, 9=debug)
* @param msg message to log to the syslog
* @returns TRUE on success, FALSE otherwise
@ -258,7 +265,7 @@ int os_drop_privs(char *user) {
if(atoi(user)) {
pw=getpwuid((uid_t)atoi(user)); /* doh! */
} else {
pw=getpwnam(config.runas);
pw=getpwnam(user);
}
if(pw) {
@ -376,4 +383,4 @@ int _os_start_signal_handler(pthread_t *handler_tid) {
void os_set_pidfile(char *file) {
_os_pidfile = file;
}

View File

@ -31,6 +31,7 @@
#include <string.h>
#include <time.h>
#include "conf.h"
#include "daapd.h"
#include "err.h"
#include "mp3-scanner.h"
@ -198,7 +199,7 @@ char *scan_winamp_genre[] = {
"Chanson",
"Opera",
"Chamber Music",
"Sonata", // 105
"Sonata", // 105
"Symphony",
"Booty Bass",
"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
* @returns 1 if number, 0 otherwise
@ -305,7 +306,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
}
pid3tag=id3_file_tag(pid3file);
if(!pid3tag) {
err=errno;
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)) &&
(id3_field_getnstrings(&pid3frame->fields[1])))
(id3_field_getnstrings(&pid3frame->fields[1])))
have_text=1;
if(have_text) {
@ -345,7 +346,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
* it's just plain wrong.
*/
have_utf8=1;
if(config.latin1_tags) {
if(conf_get_int("general","latin1_tags",0)) {
utf8_text=(char *)id3_ucs4_latin1duplicate(native_text);
} else {
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]))) {
genre=atoi((char*)&pmp3->genre[1]);
got_numeric_genre=1;
}
}
if(got_numeric_genre) {
if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN))
@ -450,7 +451,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
*
* iTunes_CDDB_IDs
* iTunNORM
*
*
* If other apps stuff crap into comment fields, then we'll ignore them
* 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.
*
* @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))
layer_index = 4;
break;
case 2:
case 2:
pfi->version = 2.0;
sample_index=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;
return -1;
}
pfi->bitrate = scan_br_table[layer_index][bitrate_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;
else
pfi->stereo = 1;
@ -647,7 +648,7 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
/* now, find the first frame */
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;
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");
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 */
fseek(infile,pos + index + fi.frame_length,SEEK_SET);
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))
found=1;
}
if(!found)
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");
return;
}
bitrate_total += fi.bitrate;
frame_count++;
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
* 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
* 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.
*
* @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;
DPRINTF(E_DBG,L_SCAN,"Starting frame count\n");
fseek(infile,0,SEEK_END);
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) {
/* found an ID3 header... */
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]);
fp_size=size + sizeof(SCAN_ID3HEADER);
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)) {
found=1;
fp_size += index;
}
}
} else {
DPRINTF(E_LOG,L_SCAN,"Could not read frame header: %s\n",file);
fclose(infile);
@ -889,13 +890,13 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
}
}
}
if(!found) {
index++;
if (first_check) {
/* if the header info was wrong about where the data started,
* 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
*/
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," Sample Rate: %d\n",fi.samplerate);
DPRINTF(E_DBG,L_SCAN," Bit Rate: %d\n",fi.bitrate);
/* now check for an XING header */
if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) {
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+6] << 8 |
buffer[index+fi.xing_offset+4+7];
DPRINTF(E_DBG,L_SCAN,"Xing Flags: %02X\n",xing_flags);
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) &&
(!pmp3->song_length)) {
/* We have no good estimate of song time, and we want more
* aggressive scanning */
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 */
scan_mp3_get_average_bitrate(infile, &fi);
} else {
@ -960,7 +961,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
pmp3->bitrate=fi.bitrate;
pmp3->samplerate=fi.samplerate;
/* guesstimate the file length */
if(!pmp3->song_length) { /* could have gotten it from the tag */
/* DWB: use ms time instead of seconds, use doubles to

View File

@ -37,10 +37,11 @@
#ifndef WIN32
#include <netinet/in.h> /* htons and friends */
#include <dirent.h> /* why here? For osx 10.2, of course! */
#endif
#endif
#include <sys/stat.h>
#include "conf.h"
#include "daapd.h"
#include "db-generic.h"
#include "err.h"
@ -60,16 +61,21 @@
* @returns 1 if should be converted. 0 if not
*/
int server_side_convert(char *codectype) {
if ((!config.ssc_codectypes) ||
(!config.ssc_codectypes[0]) ||
(!config.ssc_prog) ||
(!config.ssc_prog[0]) ||
char ssc_codectypes[PATH_MAX];
int size;
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)) {
DPRINTF(E_DBG,L_SCAN,"Nope\n");
return 0;
}
if(strcasestr(config.ssc_codectypes, codectype)) {
if(strcasestr(ssc_codectypes, codectype)) {
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 metacount = 0;
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;
while(*src) {
if(strchr(metachars,*src))
metacount+=5;
src++;
}
if(metachars) {
newpath = (char*)malloc(strlen(path) + metacount + 1);
if(!newpath) {
@ -105,7 +120,7 @@ FILE *server_side_convert_open(char *path, off_t offset, unsigned long len_ms, c
}
src=path;
dst=newpath;
while(*src) {
if(strchr(metachars,*src)) {
*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? */
cmd=(char *)malloc(strlen(config.ssc_prog) +
cmd=(char *)malloc(strlen(ssc_prog) +
strlen(path) +
64);
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 : "*");
DPRINTF(E_INF,L_SCAN,"Executing %s\n",cmd);
f = popen(cmd, "r");