mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-24 06:05:56 -05:00
add ini style config handling
This commit is contained in:
parent
47f6eeb8c5
commit
73636a5a4c
@ -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)
|
||||
|
19
src/conf.c
19
src/conf.c
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
479
src/configfile.c
479
src/configfile.c
@ -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.
|
||||
*
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
3
src/ll.c
3
src/ll.c
@ -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) {
|
||||
|
69
src/main.c
69
src/main.c
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
37
src/ssc.c
37
src/ssc.c
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user