mirror of
https://github.com/owntone/owntone-server.git
synced 2025-04-08 21:55:52 -04: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 \
|
rxml.c rxml.h redblack.c redblack.h scan-mp3.c scan-mp4.c \
|
||||||
scan-xml.c scan-wma.c scan-aac.c scan-aac.h scan-wav.c scan-url.c \
|
scan-xml.c scan-wma.c scan-aac.c scan-aac.h scan-wav.c scan-url.c \
|
||||||
smart-parser.c smart-parser.h xml-rpc.c xml-rpc.h \
|
smart-parser.c smart-parser.h xml-rpc.c xml-rpc.h \
|
||||||
os.h strptime.c strptime.h \
|
os.h strptime.c strptime.h ll.c ll.h conf.c conf.h \
|
||||||
strtok_r.c strtok_r.h os-unix.h os-unix.c os.h \
|
strtok_r.c strtok_r.h os-unix.h os-unix.c os.h \
|
||||||
$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC) $(FLACSRC) \
|
$(PRENDSRC) $(ORENDSRC) $(HRENDSRC) $(OGGVORBISSRC) $(FLACSRC) \
|
||||||
$(MUSEPACKSRC) $(SQLITEDB) $(SQLITE3DB) $(SQLDB)
|
$(MUSEPACKSRC) $(SQLITEDB) $(SQLITE3DB) $(SQLDB)
|
||||||
|
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_int(char *section, char *key, int value);
|
||||||
extern int conf_set_string(char *section, char *key, char *value);
|
extern int conf_set_string(char *section, char *key, char *value);
|
||||||
|
|
||||||
|
extern int conf_isset(char *section, char *key);
|
||||||
extern int conf_iswritable(void);
|
extern int conf_iswritable(void);
|
||||||
extern int conf_write(void);
|
extern int conf_write(void);
|
||||||
|
|
||||||
|
479
src/configfile.c
479
src/configfile.c
@ -48,6 +48,7 @@
|
|||||||
# include <sys/wait.h>
|
# include <sys/wait.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
#include "configfile.h"
|
#include "configfile.h"
|
||||||
#include "db-generic.h"
|
#include "db-generic.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
@ -62,9 +63,7 @@
|
|||||||
/*
|
/*
|
||||||
* Forwards
|
* Forwards
|
||||||
*/
|
*/
|
||||||
static void config_emit_string(WS_CONNINFO *pwsc, void *value, char *arg);
|
|
||||||
static void config_emit_literal(WS_CONNINFO *pwsc, void *value, char *arg);
|
static void config_emit_literal(WS_CONNINFO *pwsc, void *value, char *arg);
|
||||||
static void config_emit_int(WS_CONNINFO *pwsc, void *value, char *arg);
|
|
||||||
static void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg);
|
static void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg);
|
||||||
static void config_emit_threadstatus(WS_CONNINFO *pwsc, void *value, char *arg);
|
static void config_emit_threadstatus(WS_CONNINFO *pwsc, void *value, char *arg);
|
||||||
static void config_emit_ispage(WS_CONNINFO *pwsc, void *value, char *arg);
|
static void config_emit_ispage(WS_CONNINFO *pwsc, void *value, char *arg);
|
||||||
@ -73,14 +72,10 @@ static void config_emit_service_status(WS_CONNINFO *pwsc, void *value, char *arg
|
|||||||
static void config_emit_user(WS_CONNINFO *pwsc, void *value, char *arg);
|
static void config_emit_user(WS_CONNINFO *pwsc, void *value, char *arg);
|
||||||
static void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg);
|
static void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg);
|
||||||
static void config_emit_version(WS_CONNINFO *pwsc, void *value, char *arg);
|
static void config_emit_version(WS_CONNINFO *pwsc, void *value, char *arg);
|
||||||
static void config_emit_debuglevel(WS_CONNINFO *pwsc, void *value, char *arg);
|
|
||||||
static void config_emit_system(WS_CONNINFO *pwsc, void *value, char *arg);
|
static void config_emit_system(WS_CONNINFO *pwsc, void *value, char *arg);
|
||||||
static void config_emit_flags(WS_CONNINFO *pwsc, void *value, char *arg);
|
static void config_emit_flags(WS_CONNINFO *pwsc, void *value, char *arg);
|
||||||
static void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg);
|
static void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg);
|
||||||
static void config_subst_stream(WS_CONNINFO *pwsc, int fd_src);
|
static void config_subst_stream(WS_CONNINFO *pwsc, int fd_src);
|
||||||
static int config_file_is_readonly(void);
|
|
||||||
static int config_existdir(char *path);
|
|
||||||
static int config_makedir(char *path);
|
|
||||||
static void config_content_type(WS_CONNINFO *pwsc, char *path);
|
static void config_content_type(WS_CONNINFO *pwsc, char *path);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -114,6 +109,7 @@ typedef struct tag_configelement {
|
|||||||
|
|
||||||
/** List of all valid config entries and web interface directives */
|
/** List of all valid config entries and web interface directives */
|
||||||
CONFIGELEMENT config_elements[] = {
|
CONFIGELEMENT config_elements[] = {
|
||||||
|
/*
|
||||||
{ 1,1,0,CONFIG_TYPE_STRING,"runas",(void*)&config.runas,config_emit_string },
|
{ 1,1,0,CONFIG_TYPE_STRING,"runas",(void*)&config.runas,config_emit_string },
|
||||||
{ 1,1,0,CONFIG_TYPE_STRING,"web_root",(void*)&config.web_root,config_emit_string },
|
{ 1,1,0,CONFIG_TYPE_STRING,"web_root",(void*)&config.web_root,config_emit_string },
|
||||||
{ 1,1,0,CONFIG_TYPE_INT,"port",(void*)&config.port,config_emit_int },
|
{ 1,1,0,CONFIG_TYPE_INT,"port",(void*)&config.port,config_emit_int },
|
||||||
@ -138,6 +134,8 @@ CONFIGELEMENT config_elements[] = {
|
|||||||
{ 1,0,0,CONFIG_TYPE_STRING,"password",(void*)&config.readpassword, config_emit_string },
|
{ 1,0,0,CONFIG_TYPE_STRING,"password",(void*)&config.readpassword, config_emit_string },
|
||||||
{ 1,0,0,CONFIG_TYPE_STRING,"compdirs",(void*)&config.compdirs, config_emit_string },
|
{ 1,0,0,CONFIG_TYPE_STRING,"compdirs",(void*)&config.compdirs, config_emit_string },
|
||||||
{ 1,0,0,CONFIG_TYPE_STRING,"logfile",(void*)&config.logfile, config_emit_string },
|
{ 1,0,0,CONFIG_TYPE_STRING,"logfile",(void*)&config.logfile, config_emit_string },
|
||||||
|
{ 1,0,0,CONFIG_TYPE_STRING,"art_filename",(void*)&config.artfilename,config_emit_string },
|
||||||
|
*/
|
||||||
{ 0,0,0,CONFIG_TYPE_SPECIAL,"host",(void*)NULL,config_emit_host },
|
{ 0,0,0,CONFIG_TYPE_SPECIAL,"host",(void*)NULL,config_emit_host },
|
||||||
{ 0,0,0,CONFIG_TYPE_SPECIAL,"release",(void*)VERSION,config_emit_literal },
|
{ 0,0,0,CONFIG_TYPE_SPECIAL,"release",(void*)VERSION,config_emit_literal },
|
||||||
{ 0,0,0,CONFIG_TYPE_SPECIAL,"package",(void*)PACKAGE,config_emit_literal },
|
{ 0,0,0,CONFIG_TYPE_SPECIAL,"package",(void*)PACKAGE,config_emit_literal },
|
||||||
@ -150,7 +148,6 @@ CONFIGELEMENT config_elements[] = {
|
|||||||
{ 0,0,0,CONFIG_TYPE_SPECIAL,"readonly",(void*)NULL,config_emit_readonly },
|
{ 0,0,0,CONFIG_TYPE_SPECIAL,"readonly",(void*)NULL,config_emit_readonly },
|
||||||
{ 0,0,0,CONFIG_TYPE_SPECIAL,"version",(void*)NULL,config_emit_version },
|
{ 0,0,0,CONFIG_TYPE_SPECIAL,"version",(void*)NULL,config_emit_version },
|
||||||
{ 0,0,0,CONFIG_TYPE_SPECIAL,"system",(void*)NULL,config_emit_system },
|
{ 0,0,0,CONFIG_TYPE_SPECIAL,"system",(void*)NULL,config_emit_system },
|
||||||
{ 1,0,0,CONFIG_TYPE_STRING,"art_filename",(void*)&config.artfilename,config_emit_string },
|
|
||||||
{ 0,0,0,CONFIG_TYPE_SPECIAL,"flags",(void*)NULL,config_emit_flags },
|
{ 0,0,0,CONFIG_TYPE_SPECIAL,"flags",(void*)NULL,config_emit_flags },
|
||||||
{ -1,1,0,CONFIG_TYPE_STRING,NULL,NULL,NULL }
|
{ -1,1,0,CONFIG_TYPE_STRING,NULL,NULL,NULL }
|
||||||
};
|
};
|
||||||
@ -177,6 +174,7 @@ void config_content_type(WS_CONNINFO *pwsc, char *path) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
/**
|
/**
|
||||||
* Try and create a directory, including parents.
|
* Try and create a directory, including parents.
|
||||||
*
|
*
|
||||||
@ -241,356 +239,8 @@ int config_existdir(char *path) {
|
|||||||
errno=ENOTDIR;
|
errno=ENOTDIR;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the specified config file, padding the config structure
|
|
||||||
* appropriately.
|
|
||||||
*
|
|
||||||
* \param file config file to process
|
|
||||||
* \returns 0 on success, -1 otherwise with errno set
|
|
||||||
*/
|
|
||||||
int config_read(char *file) {
|
|
||||||
FILE *fin;
|
|
||||||
char *buffer;
|
|
||||||
int err=0;
|
|
||||||
char *value;
|
|
||||||
char *comment;
|
|
||||||
char path_buffer[PATH_MAX];
|
|
||||||
CONFIGELEMENT *pce;
|
|
||||||
int handled;
|
|
||||||
char *term;
|
|
||||||
int compterms=0,currentterm;
|
|
||||||
char *term_begin, *term_end;
|
|
||||||
char *compdirs;
|
|
||||||
|
|
||||||
buffer=(char*)malloc(MAX_LINE+1);
|
|
||||||
if(!buffer)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if((fin=fopen(file,"r")) == NULL) {
|
|
||||||
err=errno;
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
if(ENOENT == err) {
|
|
||||||
DPRINTF(E_LOG,L_CONF,"Whoops! Can't find the config file! If you are running this for the first\n");
|
|
||||||
DPRINTF(E_LOG,L_CONF,"time, then perhaps you've forgotten to copy the sample config in the \n");
|
|
||||||
DPRINTF(E_LOG,L_CONF,"contrib directory into /etc/mt-daapd.conf. Just a suggestion...\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
errno=err;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef NSLU2
|
|
||||||
config.always_scan=0;
|
|
||||||
#else
|
|
||||||
config.always_scan=1;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
config.configfile=strdup(file);
|
|
||||||
config.web_root=NULL;
|
|
||||||
config.adminpassword=NULL;
|
|
||||||
config.readpassword=NULL;
|
|
||||||
config.iface=NULL;
|
|
||||||
config.mp3dir=NULL;
|
|
||||||
config.playlist=NULL;
|
|
||||||
config.runas=NULL;
|
|
||||||
config.artfilename=NULL;
|
|
||||||
config.logfile=NULL;
|
|
||||||
config.rescan_interval=0;
|
|
||||||
config.process_m3u=0;
|
|
||||||
config.scan_type=0;
|
|
||||||
config.compress=0;
|
|
||||||
config.latin1_tags=0;
|
|
||||||
config.compdirs=NULL;
|
|
||||||
config.complist = NULL;
|
|
||||||
|
|
||||||
/* DWB: use alloced space so it can be freed without errors */
|
|
||||||
config.extensions=strdup(".mp3");
|
|
||||||
config.ssc_codectypes=strdup("");
|
|
||||||
config.ssc_prog=strdup("");
|
|
||||||
|
|
||||||
/* DWB: use alloced space so it can be freed without errors */
|
|
||||||
config.servername=strdup("mt-daapd " VERSION);
|
|
||||||
|
|
||||||
while(fgets(buffer,MAX_LINE,fin)) {
|
|
||||||
buffer[MAX_LINE] = '\0';
|
|
||||||
|
|
||||||
comment=strchr(buffer,'#');
|
|
||||||
if(comment)
|
|
||||||
*comment = '\0';
|
|
||||||
|
|
||||||
while(strlen(buffer) && (strchr("\n\r ",buffer[strlen(buffer)-1])))
|
|
||||||
buffer[strlen(buffer)-1] = '\0';
|
|
||||||
|
|
||||||
term=buffer;
|
|
||||||
|
|
||||||
while((*term=='\t') || (*term==' '))
|
|
||||||
term++;
|
|
||||||
|
|
||||||
value=term;
|
|
||||||
|
|
||||||
strsep(&value,"\t ");
|
|
||||||
if((value) && (term) && (strlen(term))) {
|
|
||||||
while(strlen(value) && (strchr("\t ",*value)))
|
|
||||||
value++;
|
|
||||||
|
|
||||||
pce=config_elements;
|
|
||||||
handled=0;
|
|
||||||
while((!handled) && (pce->config_element != -1)) {
|
|
||||||
if((strcasecmp(term,pce->name)==0) && (pce->config_element)) {
|
|
||||||
/* valid config directive */
|
|
||||||
handled=1;
|
|
||||||
pce->changed=1;
|
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_CONF,"Read %s: %s\n",pce->name,value);
|
|
||||||
|
|
||||||
switch(pce->type) {
|
|
||||||
case CONFIG_TYPE_STRING:
|
|
||||||
/* DWB: free space to prevent small leak */
|
|
||||||
if(*((char **)(pce->var)))
|
|
||||||
free(*((char **)(pce->var)));
|
|
||||||
*((char **)(pce->var)) = (void*)strdup(value);
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_INT:
|
|
||||||
*((int*)(pce->var)) = atoi(value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pce++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!handled) {
|
|
||||||
fprintf(stderr,"Invalid config directive: %s\n",buffer);
|
|
||||||
fclose(fin);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fin);
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
/* check to see if all required elements are satisfied */
|
|
||||||
pce=config_elements;
|
|
||||||
err=0;
|
|
||||||
while((pce->config_element != -1)) {
|
|
||||||
if(pce->required && pce->config_element && !pce->changed) {
|
|
||||||
DPRINTF(E_LOG,L_CONF,"Required config entry '%s' not specified\n",pce->name);
|
|
||||||
err=-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* too much spam on startup
|
|
||||||
if((pce->config_element) && (pce->changed)) {
|
|
||||||
switch(pce->type) {
|
|
||||||
case CONFIG_TYPE_STRING:
|
|
||||||
DPRINTF(E_INF,"%s: %s\n",pce->name,*((char**)pce->var));
|
|
||||||
break;
|
|
||||||
case CONFIG_TYPE_INT:
|
|
||||||
DPRINTF(E_INF,"%s: %d\n",pce->name,*((int*)pce->var));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
pce->changed=0;
|
|
||||||
pce++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the directory components to realpaths */
|
|
||||||
realpath(config.web_root,path_buffer);
|
|
||||||
free(config.web_root);
|
|
||||||
config.web_root=strdup(path_buffer);
|
|
||||||
|
|
||||||
realpath(config.mp3dir,path_buffer);
|
|
||||||
free(config.mp3dir);
|
|
||||||
config.mp3dir=strdup(path_buffer);
|
|
||||||
|
|
||||||
if(config.dbdir) {
|
|
||||||
DPRINTF(E_LOG,L_MISC,"You are using db_dir rather than "
|
|
||||||
"db_type/db_parms. This will stop working at "
|
|
||||||
"some point. Please fix your config\n");
|
|
||||||
realpath(config.dbdir,path_buffer);
|
|
||||||
free(config.dbdir);
|
|
||||||
config.dbdir=strdup(path_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* sanity check the paths */
|
|
||||||
sprintf(path_buffer,"%s/index.html",config.web_root);
|
|
||||||
if((fin=fopen(path_buffer,"r")) == NULL) {
|
|
||||||
err=-1;
|
|
||||||
DPRINTF(E_LOG,L_CONF,"Invalid web_root\n");
|
|
||||||
|
|
||||||
/* check for the common error */
|
|
||||||
if(strcasecmp(config.web_root,"/usr/share/mt-daapd/admin-root") == 0) {
|
|
||||||
/* see if /usr/local is any better */
|
|
||||||
if((fin=fopen("/usr/local/share/mt-daapd/admin-root","r")) != NULL) {
|
|
||||||
fclose(fin);
|
|
||||||
DPRINTF(E_LOG,L_CONF,"Should it be /usr/local/share/mt-daapd/admin-root?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fclose(fin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* should really check the mp3 path */
|
|
||||||
if(!config_existdir(config.mp3dir)) {
|
|
||||||
DPRINTF(E_LOG,L_CONF,"Bad mp3 directory (%s): %s\n",config.mp3dir,strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((config.dbdir)&&(!config_existdir(config.dbdir))) {
|
|
||||||
/* try to make it */
|
|
||||||
if(config_makedir(config.dbdir)) {
|
|
||||||
DPRINTF(E_LOG,L_CONF,"Database dir %s does not exist, cannot create: %s\n",
|
|
||||||
config.dbdir,strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* must have zlib 1.2 or better for gzip encoding support */
|
|
||||||
if(!strncmp(ZLIB_VERSION,"0.",2) ||
|
|
||||||
!strncmp(ZLIB_VERSION,"1.0",3) ||
|
|
||||||
!strncmp(ZLIB_VERSION,"1.1",3)) {
|
|
||||||
if(config.compress) {
|
|
||||||
config.compress=0;
|
|
||||||
DPRINTF(E_LOG,L_CONF,"Must have zlib > 1.2.0 to use gzip content encoding. You have %s. Disabling.\n",ZLIB_VERSION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See how many compilation dirs we have */
|
|
||||||
compterms=0;
|
|
||||||
term_begin=config.compdirs;
|
|
||||||
while(term_begin) {
|
|
||||||
compterms++;
|
|
||||||
term_begin=strchr(term_begin,',');
|
|
||||||
if(term_begin)
|
|
||||||
term_begin++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now allocate comp dirs */
|
|
||||||
if(compterms) {
|
|
||||||
config.complist=(char**)malloc((compterms+1) * sizeof(char*));
|
|
||||||
if(!config.complist)
|
|
||||||
DPRINTF(E_FATAL,L_MISC,"Alloc error.\n");
|
|
||||||
|
|
||||||
currentterm=0;
|
|
||||||
|
|
||||||
term_begin=config.compdirs;
|
|
||||||
while(*term_begin && *term_begin ==' ')
|
|
||||||
term_begin++;
|
|
||||||
|
|
||||||
compdirs = strdup(term_begin);
|
|
||||||
term_begin = term_end = compdirs;
|
|
||||||
|
|
||||||
while(term_end) {
|
|
||||||
term_end = strchr(term_begin,',');
|
|
||||||
while((*term_begin)&&(*term_begin == ' '))
|
|
||||||
term_begin++;
|
|
||||||
|
|
||||||
if(term_end)
|
|
||||||
*term_end='\0';
|
|
||||||
|
|
||||||
while(strlen(term_begin) && term_begin[strlen(term_begin)-1]==' ')
|
|
||||||
term_begin[strlen(term_begin)-1] = '\0';
|
|
||||||
|
|
||||||
if(strlen(term_begin)) {
|
|
||||||
config.complist[currentterm++] = term_begin;
|
|
||||||
}
|
|
||||||
|
|
||||||
term_begin = term_end + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.complist[currentterm] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* free up any memory used
|
|
||||||
*/
|
|
||||||
void config_close(void) {
|
|
||||||
CONFIGELEMENT *pce;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if(config.complist) {
|
|
||||||
if(config.complist[0])
|
|
||||||
free(config.complist[0]);
|
|
||||||
free(config.complist);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(config.configfile);
|
|
||||||
pce=config_elements;
|
|
||||||
err=0;
|
|
||||||
while((pce->config_element != -1)) {
|
|
||||||
if((pce->config_element) &&
|
|
||||||
(pce->type == CONFIG_TYPE_STRING) &&
|
|
||||||
(*((char**)pce->var))) {
|
|
||||||
DPRINTF(E_DBG,L_CONF,"Freeing %s\n",pce->name);
|
|
||||||
free(*((char**)pce->var));
|
|
||||||
}
|
|
||||||
pce++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write the config specified in the web post to a file. This
|
|
||||||
* doesn't change the running config, just updates the config
|
|
||||||
* file.
|
|
||||||
*
|
|
||||||
* \param pwsc the connection struct from the config-update url
|
|
||||||
*/
|
|
||||||
int config_write(WS_CONNINFO *pwsc) {
|
|
||||||
FILE *configfile;
|
|
||||||
char ctime_buf[27];
|
|
||||||
time_t now;
|
|
||||||
|
|
||||||
configfile=fopen(config.configfile,"w");
|
|
||||||
if(!configfile)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
now=time(NULL);
|
|
||||||
ctime_r(&now,ctime_buf);
|
|
||||||
fprintf(configfile,"#\n# mt-daapd.conf\n#\n");
|
|
||||||
fprintf(configfile,"# Edited: %s",ctime_buf);
|
|
||||||
fprintf(configfile,"# By: %s\n",ws_getvar(pwsc,"HTTP_USER"));
|
|
||||||
fprintf(configfile,"#\n");
|
|
||||||
|
|
||||||
fprintf(configfile,"web_root\t%s\n",ws_getvar(pwsc,"web_root"));
|
|
||||||
fprintf(configfile,"port\t\t%s\n",ws_getvar(pwsc,"port"));
|
|
||||||
fprintf(configfile,"admin_pw\t%s\n",ws_getvar(pwsc,"admin_pw"));
|
|
||||||
fprintf(configfile,"mp3_dir\t\t%s\n",ws_getvar(pwsc,"mp3_dir"));
|
|
||||||
fprintf(configfile,"servername\t%s\n",ws_getvar(pwsc,"servername"));
|
|
||||||
fprintf(configfile,"runas\t\t%s\n",ws_getvar(pwsc,"runas"));
|
|
||||||
fprintf(configfile,"playlist\t%s\n",ws_getvar(pwsc,"playlist"));
|
|
||||||
if(ws_getvar(pwsc,"password") && strlen(ws_getvar(pwsc,"password")))
|
|
||||||
fprintf(configfile,"password\t%s\n",ws_getvar(pwsc,"password"));
|
|
||||||
fprintf(configfile,"extensions\t%s\n",ws_getvar(pwsc,"extensions"));
|
|
||||||
fprintf(configfile,"ssc_codectypes\t%s\n",ws_getvar(pwsc,"ssc_codectypes"));
|
|
||||||
fprintf(configfile,"ssc_prog\t%s\n",ws_getvar(pwsc,"ssc_prog"));
|
|
||||||
fprintf(configfile,"db_dir\t\t%s\n",ws_getvar(pwsc,"db_dir"));
|
|
||||||
fprintf(configfile,"rescan_interval\t%s\n",ws_getvar(pwsc,"rescan_interval"));
|
|
||||||
fprintf(configfile,"scan_type\t%s\n",ws_getvar(pwsc,"scan_type"));
|
|
||||||
if(ws_getvar(pwsc,"always_scan") && strlen(ws_getvar(pwsc,"always_scan")))
|
|
||||||
fprintf(configfile,"always_scan\t%s\n",ws_getvar(pwsc,"always_scan"));
|
|
||||||
if(ws_getvar(pwsc,"art_filename") && strlen(ws_getvar(pwsc,"art_filename")))
|
|
||||||
fprintf(configfile,"art_filename\t%s\n",ws_getvar(pwsc,"art_filename"));
|
|
||||||
if(ws_getvar(pwsc,"logfile") && strlen(ws_getvar(pwsc,"logfile")))
|
|
||||||
fprintf(configfile,"logfile\t\t%s\n",ws_getvar(pwsc,"logfile"));
|
|
||||||
fprintf(configfile,"process_m3u\t%s\n",ws_getvar(pwsc,"process_m3u"));
|
|
||||||
fprintf(configfile,"compress\t%s\n",ws_getvar(pwsc,"compress"));
|
|
||||||
|
|
||||||
fprintf(configfile,"debuglevel\t%s\n",ws_getvar(pwsc,"debuglevel"));
|
|
||||||
fprintf(configfile,"compdirs\t%s\n",ws_getvar(pwsc,"compdirs"));
|
|
||||||
|
|
||||||
fclose(configfile);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* walk through a stream doing substitution on the
|
* walk through a stream doing substitution on the
|
||||||
* meta commands. This is what fills in the fields
|
* meta commands. This is what fills in the fields
|
||||||
@ -670,6 +320,13 @@ void config_handler(WS_CONNINFO *pwsc) {
|
|||||||
int file_fd;
|
int file_fd;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
char *pw;
|
char *pw;
|
||||||
|
char web_root[PATH_MAX];
|
||||||
|
int size;
|
||||||
|
|
||||||
|
size = PATH_MAX;
|
||||||
|
if(!conf_get_string("general","web_root","",web_root,&size))
|
||||||
|
DPRINTF(E_FATAL,L_CONF,"No web root!\n"); /* shouldnt' happen */
|
||||||
|
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_CONF|L_WS,"Entering config_handler\n");
|
DPRINTF(E_DBG,L_CONF|L_WS,"Entering config_handler\n");
|
||||||
|
|
||||||
@ -687,7 +344,7 @@ void config_handler(WS_CONNINFO *pwsc) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(path,PATH_MAX,"%s/%s",config.web_root,pwsc->uri);
|
snprintf(path,PATH_MAX,"%s/%s",web_root,pwsc->uri);
|
||||||
if(!realpath(path,resolved_path)) {
|
if(!realpath(path,resolved_path)) {
|
||||||
pwsc->error=errno;
|
pwsc->error=errno;
|
||||||
DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path);
|
DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path);
|
||||||
@ -704,8 +361,7 @@ void config_handler(WS_CONNINFO *pwsc) {
|
|||||||
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n",
|
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n",
|
||||||
pwsc->threadno, resolved_path);
|
pwsc->threadno, resolved_path);
|
||||||
|
|
||||||
if(strncmp(resolved_path,config.web_root,
|
if(strncmp(resolved_path,web_root,strlen(web_root))) {
|
||||||
strlen(config.web_root))) {
|
|
||||||
pwsc->error=EINVAL;
|
pwsc->error=EINVAL;
|
||||||
DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n",
|
DPRINTF(E_WARN,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n",
|
||||||
pwsc->threadno,resolved_path);
|
pwsc->threadno,resolved_path);
|
||||||
@ -736,32 +392,6 @@ void config_handler(WS_CONNINFO *pwsc) {
|
|||||||
} else if (strcasecmp(pw,"rescan")==0) {
|
} else if (strcasecmp(pw,"rescan")==0) {
|
||||||
config.reload=1;
|
config.reload=1;
|
||||||
}
|
}
|
||||||
} else if (ws_getvar(pwsc,"web_root") != NULL) {
|
|
||||||
/* Make sure we got here from a post, and then
|
|
||||||
* we need to update stuff */
|
|
||||||
pw=ws_getvar(pwsc,"admin_pw");
|
|
||||||
if(pw) {
|
|
||||||
if(config.adminpassword)
|
|
||||||
free(config.adminpassword);
|
|
||||||
config.adminpassword=strdup(pw);
|
|
||||||
}
|
|
||||||
|
|
||||||
pw=ws_getvar(pwsc,"password");
|
|
||||||
if(pw) {
|
|
||||||
if(config.readpassword)
|
|
||||||
free(config.readpassword);
|
|
||||||
config.readpassword=strdup(pw);
|
|
||||||
}
|
|
||||||
|
|
||||||
pw=ws_getvar(pwsc,"rescan_interval");
|
|
||||||
if(pw) {
|
|
||||||
config.rescan_interval=atoi(pw);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!config_file_is_readonly()) {
|
|
||||||
DPRINTF(E_INF,L_CONF|L_WS,"Updating config file\n");
|
|
||||||
config_write(pwsc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -789,9 +419,17 @@ void config_handler(WS_CONNINFO *pwsc) {
|
|||||||
* \param password password passed in the auth request
|
* \param password password passed in the auth request
|
||||||
*/
|
*/
|
||||||
int config_auth(char *user, char *password) {
|
int config_auth(char *user, char *password) {
|
||||||
if((!password)||(!config.adminpassword))
|
char adminpassword[80];
|
||||||
return 0;
|
int size = sizeof(adminpassword);
|
||||||
return !strcmp(password,config.adminpassword);
|
|
||||||
|
if(!conf_get_string("general","admin_pw","",adminpassword,&size)) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!password)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return !strcmp(password,adminpassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -822,18 +460,6 @@ void config_emit_host(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to emit a string configuration value to an admin page
|
|
||||||
*
|
|
||||||
* \param pwsc web connection
|
|
||||||
* \param value the variable that was requested
|
|
||||||
* \param arg any args passwd with the meta command
|
|
||||||
*/
|
|
||||||
void config_emit_string(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|
||||||
if(*((char**)value))
|
|
||||||
ws_writefd(pwsc,"%s",*((char**)value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit a string to the admin page. This is an actual string,
|
* Emit a string to the admin page. This is an actual string,
|
||||||
* not a pointer to a string pointer, like emit_string.
|
* not a pointer to a string pointer, like emit_string.
|
||||||
@ -846,18 +472,6 @@ void config_emit_literal(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
ws_writefd(pwsc,"%s",(char*)value);
|
ws_writefd(pwsc,"%s",(char*)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emit an integer valut to the web page.
|
|
||||||
*
|
|
||||||
* \param pwsc web connection
|
|
||||||
* \param value the variable that was requested
|
|
||||||
* \param arg any args passwd with the meta command
|
|
||||||
*/
|
|
||||||
void config_emit_int(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|
||||||
ws_writefd(pwsc,"%d",*((int*)value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dumps the status and control block to the web page. This is the
|
* Dumps the status and control block to the web page. This is the
|
||||||
* page that offers the user the ability to rescan, or stop the
|
* page that offers the user the ability to rescan, or stop the
|
||||||
@ -1130,24 +744,6 @@ void config_emit_user(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check to see if the config file is read only
|
|
||||||
*
|
|
||||||
* \returns 1 if the file is readonly, 0 otherwise
|
|
||||||
*/
|
|
||||||
int config_file_is_readonly(void) {
|
|
||||||
FILE *fin;
|
|
||||||
|
|
||||||
fin=fopen(config.configfile,"r+");
|
|
||||||
if(!fin) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fin);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* implement the READONLY command. This is really a hack so
|
* implement the READONLY command. This is really a hack so
|
||||||
* that the html config form isn't editable if the config file
|
* that the html config form isn't editable if the config file
|
||||||
@ -1158,7 +754,7 @@ int config_file_is_readonly(void) {
|
|||||||
* \param arg any args passwd with the meta command. Unused
|
* \param arg any args passwd with the meta command. Unused
|
||||||
*/
|
*/
|
||||||
void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg) {
|
void config_emit_readonly(WS_CONNINFO *pwsc, void *value, char *arg) {
|
||||||
if(config_file_is_readonly()) {
|
if(!conf_iswritable()) {
|
||||||
ws_writefd(pwsc,"readonly=\"readonly\"");
|
ws_writefd(pwsc,"readonly=\"readonly\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1176,10 +772,17 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
char path[PATH_MAX];
|
char path[PATH_MAX];
|
||||||
int file_fd;
|
int file_fd;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
|
char web_root[PATH_MAX];
|
||||||
|
int size;
|
||||||
|
|
||||||
|
size = sizeof(web_root);
|
||||||
|
if(!conf_get_string("general","web_root","/tmp",web_root,&size)) {
|
||||||
|
DPRINTF(E_FATAL,L_WS,"No web root!\n");
|
||||||
|
}
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_CONF|L_WS,"Preparing to include %s\n",arg);
|
DPRINTF(E_DBG,L_CONF|L_WS,"Preparing to include %s\n",arg);
|
||||||
|
|
||||||
snprintf(path,PATH_MAX,"%s/%s",config.web_root,arg);
|
snprintf(path,PATH_MAX,"%s/%s",web_root,arg);
|
||||||
if(!realpath(path,resolved_path)) {
|
if(!realpath(path,resolved_path)) {
|
||||||
pwsc->error=errno;
|
pwsc->error=errno;
|
||||||
DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path);
|
DPRINTF(E_WARN,L_CONF|L_WS,"Cannot resolve %s\n",path);
|
||||||
@ -1198,8 +801,7 @@ void config_emit_include(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n",
|
DPRINTF(E_DBG,L_CONF|L_WS,"Thread %d: Preparing to serve %s\n",
|
||||||
pwsc->threadno, resolved_path);
|
pwsc->threadno, resolved_path);
|
||||||
|
|
||||||
if(strncmp(resolved_path,config.web_root,
|
if(strncmp(resolved_path,web_root,strlen(web_root))) {
|
||||||
strlen(config.web_root))) {
|
|
||||||
pwsc->error=EINVAL;
|
pwsc->error=EINVAL;
|
||||||
DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n",
|
DPRINTF(E_LOG,L_CONF|L_WS,"Thread %d: Requested file %s out of root\n",
|
||||||
pwsc->threadno,resolved_path);
|
pwsc->threadno,resolved_path);
|
||||||
@ -1322,17 +924,6 @@ void config_emit_version(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|||||||
ws_writefd(pwsc,"%s",VERSION);
|
ws_writefd(pwsc,"%s",VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* implement the DEBUGLEVEL command
|
|
||||||
*
|
|
||||||
* @param pwsc web connection
|
|
||||||
* @param value the variable that was requested. Unused.
|
|
||||||
* @param arg any args passed with the meta command. Unused.
|
|
||||||
*/
|
|
||||||
void config_emit_debuglevel(WS_CONNINFO *pwsc, void *value, char *arg) {
|
|
||||||
ws_writefd(pwsc,"%d",err_getlevel());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* implement the SYSTEM command.
|
* implement the SYSTEM command.
|
||||||
*
|
*
|
||||||
|
@ -25,14 +25,11 @@
|
|||||||
#include "daapd.h"
|
#include "daapd.h"
|
||||||
#include "webserver.h"
|
#include "webserver.h"
|
||||||
|
|
||||||
extern int config_read(char *file);
|
|
||||||
extern int config_write(WS_CONNINFO *pwsc);
|
|
||||||
extern int config_auth(char *user, char *password);
|
extern int config_auth(char *user, char *password);
|
||||||
extern void config_handler(WS_CONNINFO *pwsc);
|
extern void config_handler(WS_CONNINFO *pwsc);
|
||||||
extern void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...);
|
extern void config_set_status(WS_CONNINFO *pwsc, int session, char *fmt, ...);
|
||||||
extern int config_get_session_count(void);
|
extern int config_get_session_count(void);
|
||||||
extern int config_get_next_session(void);
|
extern int config_get_next_session(void);
|
||||||
extern void config_close(void);
|
|
||||||
|
|
||||||
/** thread local storage */
|
/** thread local storage */
|
||||||
typedef struct tag_scan_status {
|
typedef struct tag_scan_status {
|
||||||
|
@ -50,6 +50,8 @@ typedef struct tag_config {
|
|||||||
int use_mdns; /**< Should we do rendezvous advertisements? */
|
int use_mdns; /**< Should we do rendezvous advertisements? */
|
||||||
int stop; /**< Time to exit? */
|
int stop; /**< Time to exit? */
|
||||||
int reload; /**< Time to reload and/or rescan the database? */
|
int reload; /**< Time to reload and/or rescan the database? */
|
||||||
|
|
||||||
|
#if 0
|
||||||
char *configfile; /**< path to config file */
|
char *configfile; /**< path to config file */
|
||||||
char *web_root; /**< path to the dir containing the web files */
|
char *web_root; /**< path to the dir containing the web files */
|
||||||
char *iface; /**< interface to advertise on */
|
char *iface; /**< interface to advertise on */
|
||||||
@ -76,6 +78,8 @@ typedef struct tag_config {
|
|||||||
char *dbtype; /**< db backend type */
|
char *dbtype; /**< db backend type */
|
||||||
char *dbparms; /**< parameters for the db backend */
|
char *dbparms; /**< parameters for the db backend */
|
||||||
char **complist; /**< list of compilation directories */
|
char **complist; /**< list of compilation directories */
|
||||||
|
#endif
|
||||||
|
|
||||||
STATS stats; /**< Stats structure (see above) */
|
STATS stats; /**< Stats structure (see above) */
|
||||||
WSHANDLE server; /**< webserver handle */
|
WSHANDLE server; /**< webserver handle */
|
||||||
} CONFIG;
|
} CONFIG;
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
#include "db-generic.h"
|
#include "db-generic.h"
|
||||||
#include "configfile.h"
|
#include "configfile.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
@ -111,14 +112,19 @@ typedef struct tag_output_info {
|
|||||||
* \returns 1 if auth successful, 0 otherwise
|
* \returns 1 if auth successful, 0 otherwise
|
||||||
*/
|
*/
|
||||||
int daap_auth(char *username, char *password) {
|
int daap_auth(char *username, char *password) {
|
||||||
|
char readpassword[40];
|
||||||
|
int size = sizeof(readpassword);
|
||||||
|
|
||||||
|
conf_get_string("general","password","",readpassword,&size);
|
||||||
|
|
||||||
if((password == NULL) &&
|
if((password == NULL) &&
|
||||||
((config.readpassword == NULL) || (strlen(config.readpassword)==0)))
|
((readpassword == NULL) || (strlen(readpassword)==0)))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if(password == NULL)
|
if(password == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return !strcasecmp(password,config.readpassword);
|
return !strcasecmp(password,readpassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -770,7 +776,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
lseek(file_fd,0,SEEK_SET);
|
lseek(file_fd,0,SEEK_SET);
|
||||||
|
|
||||||
/* Re-adjust content length for cover art */
|
/* Re-adjust content length for cover art */
|
||||||
if((config.artfilename) &&
|
if((conf_isset("general","art_filename")) &&
|
||||||
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
|
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
|
||||||
fstat(img_fd, &sb);
|
fstat(img_fd, &sb);
|
||||||
img_size = sb.st_size;
|
img_size = sb.st_size;
|
||||||
@ -820,7 +826,7 @@ void dispatch_stream(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
if(!offset)
|
if(!offset)
|
||||||
config.stats.songs_served++; /* FIXME: remove stat races */
|
config.stats.songs_served++; /* FIXME: remove stat races */
|
||||||
|
|
||||||
if((config.artfilename) &&
|
if((conf_isset("general","art_filenaem")) &&
|
||||||
(!offset) &&
|
(!offset) &&
|
||||||
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
|
((img_fd=da_get_image_fd(pmp3->path)) != -1)) {
|
||||||
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
|
if (strncasecmp(pmp3->type,"mp3",4) ==0) {
|
||||||
@ -1341,8 +1347,12 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
unsigned char *current = dbinfo_response;
|
unsigned char *current = dbinfo_response;
|
||||||
int namelen;
|
int namelen;
|
||||||
int count;
|
int count;
|
||||||
|
char servername[80];
|
||||||
|
int size;
|
||||||
|
|
||||||
namelen=(int) strlen(config.servername);
|
conf_get_string("general","servername","mt-daapd",servername,&size);
|
||||||
|
|
||||||
|
namelen=(int) strlen(servername);
|
||||||
|
|
||||||
current += db_dmap_add_container(current,"avdb",105 + namelen);
|
current += db_dmap_add_container(current,"avdb",105 + namelen);
|
||||||
current += db_dmap_add_int(current,"mstt",200); /* 12 */
|
current += db_dmap_add_int(current,"mstt",200); /* 12 */
|
||||||
@ -1352,7 +1362,7 @@ void dispatch_dbinfo(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
current += db_dmap_add_container(current,"mlcl",52 + namelen);
|
current += db_dmap_add_container(current,"mlcl",52 + namelen);
|
||||||
current += db_dmap_add_container(current,"mlit",44 + namelen);
|
current += db_dmap_add_container(current,"mlit",44 + namelen);
|
||||||
current += db_dmap_add_int(current,"miid",1); /* 12 */
|
current += db_dmap_add_int(current,"miid",1); /* 12 */
|
||||||
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + namelen */
|
current += db_dmap_add_string(current,"minm",servername); /* 8 + namelen */
|
||||||
db_get_song_count(NULL,&count);
|
db_get_song_count(NULL,&count);
|
||||||
current += db_dmap_add_int(current,"mimc",count); /* 12 */
|
current += db_dmap_add_int(current,"mimc",count); /* 12 */
|
||||||
db_get_playlist_count(NULL,&count);
|
db_get_playlist_count(NULL,&count);
|
||||||
@ -1431,8 +1441,12 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
char *client_version;
|
char *client_version;
|
||||||
int mpro = 2 << 16;
|
int mpro = 2 << 16;
|
||||||
int apro = 3 << 16;
|
int apro = 3 << 16;
|
||||||
|
char servername[80];
|
||||||
|
int size;
|
||||||
|
|
||||||
int actual_length=130 + (int) strlen(config.servername);
|
conf_get_string("general","servername","mt-daapd",servername,&size);
|
||||||
|
|
||||||
|
int actual_length=130 + (int) strlen(servername);
|
||||||
|
|
||||||
if(actual_length > sizeof(server_info)) {
|
if(actual_length > sizeof(server_info)) {
|
||||||
DPRINTF(E_FATAL,L_DAAP,"Server name too long.\n");
|
DPRINTF(E_FATAL,L_DAAP,"Server name too long.\n");
|
||||||
@ -1456,10 +1470,10 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
current += db_dmap_add_int(current,"mpro",mpro); /* 12 */
|
current += db_dmap_add_int(current,"mpro",mpro); /* 12 */
|
||||||
current += db_dmap_add_int(current,"apro",apro); /* 12 */
|
current += db_dmap_add_int(current,"apro",apro); /* 12 */
|
||||||
current += db_dmap_add_int(current,"mstm",1800); /* 12 */
|
current += db_dmap_add_int(current,"mstm",1800); /* 12 */
|
||||||
current += db_dmap_add_string(current,"minm",config.servername); /* 8 + strlen(name) */
|
current += db_dmap_add_string(current,"minm",servername); /* 8 + strlen(name) */
|
||||||
|
|
||||||
current += db_dmap_add_char(current,"msau", /* 9 */
|
current += db_dmap_add_char(current,"msau", /* 9 */
|
||||||
config.readpassword != NULL ? 2 : 0);
|
conf_isset("general","password") ? 2 : 0);
|
||||||
current += db_dmap_add_char(current,"msex",0); /* 9 */
|
current += db_dmap_add_char(current,"msex",0); /* 9 */
|
||||||
current += db_dmap_add_char(current,"msix",0); /* 9 */
|
current += db_dmap_add_char(current,"msix",0); /* 9 */
|
||||||
current += db_dmap_add_char(current,"msbr",0); /* 9 */
|
current += db_dmap_add_char(current,"msbr",0); /* 9 */
|
||||||
@ -1481,24 +1495,24 @@ void dispatch_server_info(WS_CONNINFO *pwsc, DBQUERYINFO *pqi) {
|
|||||||
void dispatch_error(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, char *container, char *error) {
|
void dispatch_error(WS_CONNINFO *pwsc, DBQUERYINFO *pqi, char *container, char *error) {
|
||||||
unsigned char *block, *current;
|
unsigned char *block, *current;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
len = 12 + 8 + 8 + (int) strlen(error);
|
len = 12 + 8 + 8 + (int) strlen(error);
|
||||||
block = (unsigned char *)malloc(len);
|
block = (unsigned char *)malloc(len);
|
||||||
|
|
||||||
if(!block)
|
if(!block)
|
||||||
DPRINTF(E_FATAL,L_DAAP,"Malloc error\n");
|
DPRINTF(E_FATAL,L_DAAP,"Malloc error\n");
|
||||||
|
|
||||||
current = block;
|
current = block;
|
||||||
current += db_dmap_add_container(current,container,len - 8);
|
current += db_dmap_add_container(current,container,len - 8);
|
||||||
current += db_dmap_add_int(current,"mstt",500);
|
current += db_dmap_add_int(current,"mstt",500);
|
||||||
current += db_dmap_add_string(current,"msts",error);
|
current += db_dmap_add_string(current,"msts",error);
|
||||||
|
|
||||||
dispatch_output_start(pwsc,pqi,len);
|
dispatch_output_start(pwsc,pqi,len);
|
||||||
dispatch_output_write(pwsc,pqi,block,len);
|
dispatch_output_write(pwsc,pqi,block,len);
|
||||||
dispatch_output_end(pwsc,pqi);
|
dispatch_output_end(pwsc,pqi);
|
||||||
|
|
||||||
free(block);
|
free(block);
|
||||||
|
|
||||||
pwsc->close=1;
|
pwsc->close=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
#include "configfile.h"
|
#include "configfile.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
#include "restart.h"
|
#include "restart.h"
|
||||||
@ -85,17 +86,26 @@ int fcopyblock(FILE *fromfp, int tofd, size_t size);
|
|||||||
/*
|
/*
|
||||||
* get_image_fd
|
* get_image_fd
|
||||||
*
|
*
|
||||||
* Get a file descriptor for a piece of cover art.
|
* Get a file descriptor for a piece of cover art.
|
||||||
*/
|
*/
|
||||||
int da_get_image_fd(char *filename) {
|
int da_get_image_fd(char *filename) {
|
||||||
char buffer[PATH_MAX];
|
char buffer[PATH_MAX];
|
||||||
char *path_end;
|
char *path_end;
|
||||||
int fd;
|
int fd;
|
||||||
|
char artfilename[PATH_MAX];
|
||||||
|
int size;
|
||||||
|
|
||||||
|
size = sizeof(artfilename);
|
||||||
|
if(!conf_get_string("general","art_filename","_folderOpenImage.jpg",
|
||||||
|
artfilename,&size)) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
strncpy(buffer,filename,sizeof(buffer));
|
strncpy(buffer,filename,sizeof(buffer));
|
||||||
path_end = strrchr(buffer,'/');
|
path_end = strrchr(buffer,'/');
|
||||||
strcpy(path_end+1,config.artfilename);
|
strcpy(path_end+1,artfilename);
|
||||||
fd = open(buffer,O_RDONLY);
|
fd = open(buffer,O_RDONLY);
|
||||||
if(fd != -1)
|
if(fd != -1)
|
||||||
DPRINTF(E_INF,L_ART,"Found image file %s (fd %d)\n",buffer,fd);
|
DPRINTF(E_INF,L_ART,"Found image file %s (fd %d)\n",buffer,fd);
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
@ -104,16 +114,16 @@ int da_get_image_fd(char *filename) {
|
|||||||
/*
|
/*
|
||||||
* get_current_tag_info
|
* get_current_tag_info
|
||||||
*
|
*
|
||||||
* Get the current tag from the file. We need to do this to determine whether we
|
* Get the current tag from the file. We need to do this to determine whether we
|
||||||
* are dealing with an id3v2.2 or id3v2.3 tag so we can decide which artwork to
|
* are dealing with an id3v2.2 or id3v2.3 tag so we can decide which artwork to
|
||||||
* attach to the song
|
* attach to the song
|
||||||
*/
|
*/
|
||||||
int *da_get_current_tag_info(int file_fd) {
|
int *da_get_current_tag_info(int file_fd) {
|
||||||
unsigned char buffer[10];
|
unsigned char buffer[10];
|
||||||
int *tag_info;
|
int *tag_info;
|
||||||
|
|
||||||
tag_info = (int *) calloc(2,sizeof(int));
|
tag_info = (int *) calloc(2,sizeof(int));
|
||||||
|
|
||||||
r_read(file_fd,buffer,10);
|
r_read(file_fd,buffer,10);
|
||||||
if (strncmp((char*)buffer,"ID3", 3) == 0 ) {
|
if (strncmp((char*)buffer,"ID3", 3) == 0 ) {
|
||||||
tag_info[0] = buffer[3];
|
tag_info[0] = buffer[3];
|
||||||
@ -176,9 +186,9 @@ int da_attach_image(int img_fd, int out_fd, int mp3_fd, int offset)
|
|||||||
buffer[2] = ( tag_size & 0x3F80 ) >> 7;
|
buffer[2] = ( tag_size & 0x3F80 ) >> 7;
|
||||||
buffer[1] = ( tag_size & 0x1FC000 ) >> 14;
|
buffer[1] = ( tag_size & 0x1FC000 ) >> 14;
|
||||||
buffer[0] = ( tag_size & 0xFE00000 ) >> 21;
|
buffer[0] = ( tag_size & 0xFE00000 ) >> 21;
|
||||||
|
|
||||||
r_write(out_fd,buffer,4);
|
r_write(out_fd,buffer,4);
|
||||||
|
|
||||||
if (tag_info[0] == 3) {
|
if (tag_info[0] == 3) {
|
||||||
r_write(out_fd,"APIC\0",5);
|
r_write(out_fd,"APIC\0",5);
|
||||||
img_size = id3v3_image_size(img_size);
|
img_size = id3v3_image_size(img_size);
|
||||||
@ -288,8 +298,17 @@ off_t da_aac_insert_covr_atom(off_t extra_size, int out_fd, FILE *aac_fp,
|
|||||||
char *cp;
|
char *cp;
|
||||||
unsigned char img_type_flag = 0;
|
unsigned char img_type_flag = 0;
|
||||||
|
|
||||||
|
char artfilename[PATH_MAX];
|
||||||
|
int size;
|
||||||
|
|
||||||
|
size = sizeof(artfilename);
|
||||||
|
if(!conf_get_string("general","art_filename","_folderOpenImage.jpg",
|
||||||
|
artfilename,&size)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Figure out image file type since this needs to be encoded in the atom. */
|
/* Figure out image file type since this needs to be encoded in the atom. */
|
||||||
cp = strrchr(config.artfilename, '.');
|
cp = strrchr(artfilename, '.');
|
||||||
if (cp) {
|
if (cp) {
|
||||||
if (!strcasecmp(cp, ".jpeg") || !strcasecmp(cp, ".jpg")) {
|
if (!strcasecmp(cp, ".jpeg") || !strcasecmp(cp, ".jpg")) {
|
||||||
img_type_flag = 0x0d;
|
img_type_flag = 0x0d;
|
||||||
@ -381,7 +400,7 @@ off_t da_aac_insert_covr_atom(off_t extra_size, int out_fd, FILE *aac_fp,
|
|||||||
buffer[1] = ( atom_length >> 16 ) & 0xFF;
|
buffer[1] = ( atom_length >> 16 ) & 0xFF;
|
||||||
buffer[0] = ( atom_length >> 24 ) & 0xFF;
|
buffer[0] = ( atom_length >> 24 ) & 0xFF;
|
||||||
r_write(out_fd, buffer, 4);
|
r_write(out_fd, buffer, 4);
|
||||||
|
|
||||||
r_write(out_fd, "covr", 4);
|
r_write(out_fd, "covr", 4);
|
||||||
|
|
||||||
/* Write out 'data' atom */
|
/* Write out 'data' atom */
|
||||||
@ -476,7 +495,7 @@ off_t da_aac_attach_image(int img_fd, int out_fd, int aac_fd, int offset)
|
|||||||
|
|
||||||
aac_fp = fdopen(dup(aac_fd), "r");
|
aac_fp = fdopen(dup(aac_fd), "r");
|
||||||
|
|
||||||
stco_atom_pos = scan_aac_drilltoatom(aac_fp,
|
stco_atom_pos = scan_aac_drilltoatom(aac_fp,
|
||||||
"moov:trak:mdia:minf:stbl:stco",
|
"moov:trak:mdia:minf:stbl:stco",
|
||||||
&atom_length);
|
&atom_length);
|
||||||
ilst_atom_pos = scan_aac_drilltoatom(aac_fp, "moov:udta:meta:ilst",
|
ilst_atom_pos = scan_aac_drilltoatom(aac_fp, "moov:udta:meta:ilst",
|
||||||
@ -485,7 +504,7 @@ off_t da_aac_attach_image(int img_fd, int out_fd, int aac_fd, int offset)
|
|||||||
|
|
||||||
if (last_pos != -1) {
|
if (last_pos != -1) {
|
||||||
if (offset >= last_pos) {
|
if (offset >= last_pos) {
|
||||||
/* Offset is in the actual music data so don't bother processing
|
/* Offset is in the actual music data so don't bother processing
|
||||||
meta data. */
|
meta data. */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
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 *ll_fetch_item(LL *pl, char *key) {
|
||||||
LL_ITEM *current;
|
LL_ITEM *current;
|
||||||
|
|
||||||
|
if(!pl)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
current = pl->itemlist.next;
|
current = pl->itemlist.next;
|
||||||
while(current) {
|
while(current) {
|
||||||
if(pl->flags & LL_FLAG_HONORCASE) {
|
if(pl->flags & LL_FLAG_HONORCASE) {
|
||||||
|
69
src/main.c
69
src/main.c
@ -69,6 +69,7 @@
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
#include "configfile.h"
|
#include "configfile.h"
|
||||||
#include "dispatch.h"
|
#include "dispatch.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
@ -170,6 +171,16 @@ int main(int argc, char *argv[]) {
|
|||||||
int force_non_root=0;
|
int force_non_root=0;
|
||||||
int skip_initial=0;
|
int skip_initial=0;
|
||||||
|
|
||||||
|
int size;
|
||||||
|
char logfile[PATH_MAX];
|
||||||
|
char db_type[40];
|
||||||
|
char db_parms[PATH_MAX];
|
||||||
|
char mp3_dir[PATH_MAX];
|
||||||
|
char web_root[PATH_MAX];
|
||||||
|
char runas[40];
|
||||||
|
char servername[PATH_MAX];
|
||||||
|
char iface[20];
|
||||||
|
|
||||||
int err;
|
int err;
|
||||||
char *perr;
|
char *perr;
|
||||||
|
|
||||||
@ -246,7 +257,7 @@ int main(int argc, char *argv[]) {
|
|||||||
config.stats.start_time=start_time=(int)time(NULL);
|
config.stats.start_time=start_time=(int)time(NULL);
|
||||||
config.stop=0;
|
config.stop=0;
|
||||||
|
|
||||||
if(config_read(configfile)) {
|
if(conf_read(configfile) != CONF_E_SUCCESS) {
|
||||||
fprintf(stderr,"Error reading config file (%s)\n",configfile);
|
fprintf(stderr,"Error reading config file (%s)\n",configfile);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
@ -254,8 +265,9 @@ int main(int argc, char *argv[]) {
|
|||||||
DPRINTF(E_LOG,L_MAIN,"Starting with debuglevel %d\n",err_getlevel());
|
DPRINTF(E_LOG,L_MAIN,"Starting with debuglevel %d\n",err_getlevel());
|
||||||
|
|
||||||
if(!foreground) {
|
if(!foreground) {
|
||||||
if(config.logfile) {
|
size = PATH_MAX;
|
||||||
err_setdest(config.logfile,LOGDEST_LOGFILE);
|
if(conf_get_string("general","logfile",NULL,logfile,&size)) {
|
||||||
|
err_setdest(logfile,LOGDEST_LOGFILE);
|
||||||
} else {
|
} else {
|
||||||
err_setdest("mt-daapd",LOGDEST_SYSLOG);
|
err_setdest("mt-daapd",LOGDEST_SYSLOG);
|
||||||
}
|
}
|
||||||
@ -264,7 +276,9 @@ int main(int argc, char *argv[]) {
|
|||||||
#ifndef WITHOUT_MDNS
|
#ifndef WITHOUT_MDNS
|
||||||
if(config.use_mdns) {
|
if(config.use_mdns) {
|
||||||
DPRINTF(E_LOG,L_MAIN,"Starting rendezvous daemon\n");
|
DPRINTF(E_LOG,L_MAIN,"Starting rendezvous daemon\n");
|
||||||
if(rend_init(config.runas)) {
|
size = sizeof(runas);
|
||||||
|
conf_get_string("general","runas","nobody",runas,&size);
|
||||||
|
if(rend_init(runas)) {
|
||||||
DPRINTF(E_FATAL,L_MAIN|L_REND,"Error in rend_init: %s\n",strerror(errno));
|
DPRINTF(E_FATAL,L_MAIN|L_REND,"Error in rend_init: %s\n",strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -277,10 +291,16 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* this will require that the db be readable by the runas user */
|
/* this will require that the db be readable by the runas user */
|
||||||
err=db_open(&perr,config.dbtype,config.dbparms);
|
size = sizeof(db_type);
|
||||||
|
conf_get_string("general","db_type","sqlite",db_type,&size);
|
||||||
|
size = sizeof(db_parms);
|
||||||
|
conf_get_string("general","db_parms","/var/cache/mt-daapd",db_parms,&size);
|
||||||
|
err=db_open(&perr,db_type,db_parms);
|
||||||
|
|
||||||
if(err)
|
if(err) {
|
||||||
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_open: %s\n",perr);
|
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error: db_open %s/%s: %s\n",
|
||||||
|
db_type,db_parms,perr);
|
||||||
|
}
|
||||||
|
|
||||||
/* Initialize the database before starting */
|
/* Initialize the database before starting */
|
||||||
DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n");
|
DPRINTF(E_LOG,L_MAIN|L_DB,"Initializing database\n");
|
||||||
@ -288,19 +308,24 @@ int main(int argc, char *argv[]) {
|
|||||||
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_init: %s\n",strerror(errno));
|
DPRINTF(E_FATAL,L_MAIN|L_DB,"Error in db_init: %s\n",strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size = sizeof(mp3_dir);
|
||||||
|
conf_get_string("general","mp3_dir","/mnt/mp3",mp3_dir,&size);
|
||||||
if(!skip_initial) {
|
if(!skip_initial) {
|
||||||
DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan of %s\n",config.mp3dir);
|
DPRINTF(E_LOG,L_MAIN|L_SCAN,"Starting mp3 scan of %s\n",mp3_dir);
|
||||||
if(scan_init(config.mp3dir)) {
|
|
||||||
|
if(scan_init(mp3_dir)) {
|
||||||
DPRINTF(E_FATAL,L_MAIN|L_SCAN,"Error scanning MP3 files: %s\n",strerror(errno));
|
DPRINTF(E_FATAL,L_MAIN|L_SCAN,"Error scanning MP3 files: %s\n",strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* start up the web server */
|
/* start up the web server */
|
||||||
ws_config.web_root=config.web_root;
|
size = sizeof(web_root);
|
||||||
ws_config.port=config.port;
|
conf_get_string("general","web_root",NULL,web_root,&size);
|
||||||
|
ws_config.web_root=web_root;
|
||||||
|
ws_config.port=conf_get_int("general","port",3689);
|
||||||
|
|
||||||
DPRINTF(E_LOG,L_MAIN|L_WS,"Starting web server from %s on port %d\n",
|
DPRINTF(E_LOG,L_MAIN|L_WS,"Starting web server from %s on port %d\n",
|
||||||
config.web_root, config.port);
|
ws_config.web_root, ws_config.port);
|
||||||
|
|
||||||
config.server=ws_start(&ws_config);
|
config.server=ws_start(&ws_config);
|
||||||
if(!config.server) {
|
if(!config.server) {
|
||||||
@ -319,8 +344,12 @@ int main(int argc, char *argv[]) {
|
|||||||
#ifndef WITHOUT_MDNS
|
#ifndef WITHOUT_MDNS
|
||||||
if(config.use_mdns) { /* register services */
|
if(config.use_mdns) { /* register services */
|
||||||
DPRINTF(E_LOG,L_MAIN|L_REND,"Registering rendezvous names\n");
|
DPRINTF(E_LOG,L_MAIN|L_REND,"Registering rendezvous names\n");
|
||||||
rend_register(config.servername,"_daap._tcp",config.port,config.iface);
|
size = sizeof(servername);
|
||||||
rend_register(config.servername,"_http._tcp",config.port,config.iface);
|
conf_get_string("general","servername","mt-daapd",servername,&size);
|
||||||
|
size = sizeof(iface);
|
||||||
|
conf_get_string("general","interface","",iface,&size);
|
||||||
|
rend_register(servername,"_daap._tcp",ws_config.port,iface);
|
||||||
|
rend_register(servername,"_http._tcp",ws_config.port,iface);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -331,8 +360,10 @@ int main(int argc, char *argv[]) {
|
|||||||
end_time-start_time);
|
end_time-start_time);
|
||||||
|
|
||||||
while(!config.stop) {
|
while(!config.stop) {
|
||||||
if((config.rescan_interval) && (rescan_counter > config.rescan_interval)) {
|
if((conf_get_int("general","rescan_interval",0) &&
|
||||||
if((config.always_scan) || (config_get_session_count())) {
|
(rescan_counter > conf_get_int("general","rescan_interval",0)))) {
|
||||||
|
if((conf_get_int("general","always_scan",0)) ||
|
||||||
|
(config_get_session_count())) {
|
||||||
config.reload=1;
|
config.reload=1;
|
||||||
} else {
|
} else {
|
||||||
DPRINTF(E_DBG,L_MAIN|L_SCAN|L_DB,"Skipped bground scan... no users\n");
|
DPRINTF(E_DBG,L_MAIN|L_SCAN|L_DB,"Skipped bground scan... no users\n");
|
||||||
@ -345,7 +376,9 @@ int main(int argc, char *argv[]) {
|
|||||||
start_time=(int) time(NULL);
|
start_time=(int) time(NULL);
|
||||||
|
|
||||||
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n");
|
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Rescanning database\n");
|
||||||
if(scan_init(config.mp3dir)) {
|
size = PATH_MAX;
|
||||||
|
conf_get_string("general","mp3_dir","/mnt/mp3",mp3_dir,&size);
|
||||||
|
if(scan_init(mp3_dir)) {
|
||||||
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Error rescanning... exiting\n");
|
DPRINTF(E_LOG,L_MAIN|L_DB|L_SCAN,"Error rescanning... exiting\n");
|
||||||
config.stop=1;
|
config.stop=1;
|
||||||
}
|
}
|
||||||
@ -378,7 +411,7 @@ int main(int argc, char *argv[]) {
|
|||||||
ws_stop(config.server);
|
ws_stop(config.server);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
config_close();
|
conf_close();
|
||||||
|
|
||||||
DPRINTF(E_LOG,L_MAIN|L_DB,"Closing database\n");
|
DPRINTF(E_LOG,L_MAIN|L_DB,"Closing database\n");
|
||||||
db_deinit();
|
db_deinit();
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
#include "daapd.h"
|
#include "daapd.h"
|
||||||
#include "db-generic.h"
|
#include "db-generic.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
@ -261,6 +262,7 @@ int scan_init(char *path) {
|
|||||||
* @returns 1 if it is a compilation path, 0 otherwise
|
* @returns 1 if it is a compilation path, 0 otherwise
|
||||||
*/
|
*/
|
||||||
int scan_is_compdir(char *path) {
|
int scan_is_compdir(char *path) {
|
||||||
|
#if 0
|
||||||
int current=0;
|
int current=0;
|
||||||
|
|
||||||
if(!config.complist)
|
if(!config.complist)
|
||||||
@ -271,7 +273,7 @@ int scan_is_compdir(char *path) {
|
|||||||
return 1;
|
return 1;
|
||||||
current++;
|
current++;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,6 +296,11 @@ int scan_path(char *path) {
|
|||||||
MP3FILE *pmp3;
|
MP3FILE *pmp3;
|
||||||
int is_compdir;
|
int is_compdir;
|
||||||
|
|
||||||
|
char extensions[PATH_MAX];
|
||||||
|
int size = sizeof(extensions);
|
||||||
|
|
||||||
|
conf_get_string("general","extensions",".mp3,.m4a,.m4p",extensions,&size);
|
||||||
|
|
||||||
if((current_dir=opendir(path)) == NULL) {
|
if((current_dir=opendir(path)) == NULL) {
|
||||||
DPRINTF(E_WARN,L_SCAN,"opendir: %s\n",strerror(errno));
|
DPRINTF(E_WARN,L_SCAN,"opendir: %s\n",strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
@ -339,13 +346,13 @@ int scan_path(char *path) {
|
|||||||
/* process the file */
|
/* process the file */
|
||||||
if(strlen(pde->d_name) > 4) {
|
if(strlen(pde->d_name) > 4) {
|
||||||
if((strcasecmp(".m3u",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0) &&
|
if((strcasecmp(".m3u",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0) &&
|
||||||
config.process_m3u){
|
conf_get_int("general","process_m3u",0)){
|
||||||
/* we found an m3u file */
|
/* we found an m3u file */
|
||||||
scan_add_playlistlist(mp3_path);
|
scan_add_playlistlist(mp3_path);
|
||||||
} else if((strcasecmp(".xml",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0)) {
|
} else if((strcasecmp(".xml",(char*)&pde->d_name[strlen(pde->d_name) - 4]) == 0)) {
|
||||||
scan_add_playlistlist(mp3_path);
|
scan_add_playlistlist(mp3_path);
|
||||||
} else if (((ext = strrchr(pde->d_name, '.')) != NULL) &&
|
} else if (((ext = strrchr(pde->d_name, '.')) != NULL) &&
|
||||||
(strcasestr(config.extensions, ext))) {
|
(strcasestr(extensions, ext))) {
|
||||||
/* only scan if it's been changed, or empty db */
|
/* only scan if it's been changed, or empty db */
|
||||||
modified_time=(int) sb.st_mtime;
|
modified_time=(int) sb.st_mtime;
|
||||||
pmp3=db_fetch_path(NULL,mp3_path,0);
|
pmp3=db_fetch_path(NULL,mp3_path,0);
|
||||||
|
@ -33,6 +33,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
#include <wait.h>
|
||||||
|
|
||||||
#ifdef HAVE_UNISTD_H
|
#ifdef HAVE_UNISTD_H
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
@ -44,6 +46,7 @@
|
|||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
#include "daapd.h"
|
#include "daapd.h"
|
||||||
#include "os-unix.h"
|
#include "os-unix.h"
|
||||||
@ -71,13 +74,17 @@ char *_os_pidfile = PIDFILE;
|
|||||||
/**
|
/**
|
||||||
* this initializes the platform... sets up signal handlers, forks to the
|
* this initializes the platform... sets up signal handlers, forks to the
|
||||||
* background, etc
|
* background, etc
|
||||||
*
|
*
|
||||||
* @param foreground whether to run in fg or fork to bg
|
* @param foreground whether to run in fg or fork to bg
|
||||||
* @returns TRUE on success, FALSE otherwise
|
* @returns TRUE on success, FALSE otherwise
|
||||||
*/
|
*/
|
||||||
int os_init(int foreground) {
|
int os_init(int foreground) {
|
||||||
int pid_fd;
|
int pid_fd;
|
||||||
FILE *pid_fp=NULL;
|
FILE *pid_fp=NULL;
|
||||||
|
char runas[80];
|
||||||
|
int size = sizeof(runas);
|
||||||
|
|
||||||
|
conf_get_string("general","runas","nobody",runas,&size);
|
||||||
|
|
||||||
/* open the pidfile, so it can be written once we detach */
|
/* open the pidfile, so it can be written once we detach */
|
||||||
if(!foreground) {
|
if(!foreground) {
|
||||||
@ -94,7 +101,7 @@ int os_init(int foreground) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Drop privs here
|
// Drop privs here
|
||||||
if(os_drop_privs(config.runas)) {
|
if(os_drop_privs(runas)) {
|
||||||
DPRINTF(E_FATAL,L_MAIN,"Error in drop_privs: %s\n",strerror(errno));
|
DPRINTF(E_FATAL,L_MAIN,"Error in drop_privs: %s\n",strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +120,7 @@ int os_init(int foreground) {
|
|||||||
fprintf(pid_fp,"%d\n",_os_signal_pid);
|
fprintf(pid_fp,"%d\n",_os_signal_pid);
|
||||||
fclose(pid_fp);
|
fclose(pid_fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +136,7 @@ void os_deinit(void) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* start syslogging
|
* start syslogging
|
||||||
*
|
*
|
||||||
* @returns TRUE on success, FALSE otherwise
|
* @returns TRUE on success, FALSE otherwise
|
||||||
*/
|
*/
|
||||||
int os_opensyslog(void) {
|
int os_opensyslog(void) {
|
||||||
@ -140,7 +147,7 @@ int os_opensyslog(void) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* stop syslogging
|
* stop syslogging
|
||||||
*
|
*
|
||||||
* @returns TRUE on success, FALSE otherwise
|
* @returns TRUE on success, FALSE otherwise
|
||||||
*/
|
*/
|
||||||
int os_closesyslog(void) {
|
int os_closesyslog(void) {
|
||||||
@ -150,7 +157,7 @@ int os_closesyslog(void) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* log a syslog message
|
* log a syslog message
|
||||||
*
|
*
|
||||||
* @param level log level (1-9: 1=fatal, 9=debug)
|
* @param level log level (1-9: 1=fatal, 9=debug)
|
||||||
* @param msg message to log to the syslog
|
* @param msg message to log to the syslog
|
||||||
* @returns TRUE on success, FALSE otherwise
|
* @returns TRUE on success, FALSE otherwise
|
||||||
@ -258,7 +265,7 @@ int os_drop_privs(char *user) {
|
|||||||
if(atoi(user)) {
|
if(atoi(user)) {
|
||||||
pw=getpwuid((uid_t)atoi(user)); /* doh! */
|
pw=getpwuid((uid_t)atoi(user)); /* doh! */
|
||||||
} else {
|
} else {
|
||||||
pw=getpwnam(config.runas);
|
pw=getpwnam(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pw) {
|
if(pw) {
|
||||||
@ -376,4 +383,4 @@ int _os_start_signal_handler(pthread_t *handler_tid) {
|
|||||||
void os_set_pidfile(char *file) {
|
void os_set_pidfile(char *file) {
|
||||||
_os_pidfile = file;
|
_os_pidfile = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
#include "daapd.h"
|
#include "daapd.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
#include "mp3-scanner.h"
|
#include "mp3-scanner.h"
|
||||||
@ -198,7 +199,7 @@ char *scan_winamp_genre[] = {
|
|||||||
"Chanson",
|
"Chanson",
|
||||||
"Opera",
|
"Opera",
|
||||||
"Chamber Music",
|
"Chamber Music",
|
||||||
"Sonata", // 105
|
"Sonata", // 105
|
||||||
"Symphony",
|
"Symphony",
|
||||||
"Booty Bass",
|
"Booty Bass",
|
||||||
"Primus",
|
"Primus",
|
||||||
@ -267,7 +268,7 @@ int scan_get_mp3info(char *filename, MP3FILE *pmp3) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* decide if a string is numeric or not...
|
* decide if a string is numeric or not...
|
||||||
*
|
*
|
||||||
* @param str string to evaluate
|
* @param str string to evaluate
|
||||||
* @returns 1 if number, 0 otherwise
|
* @returns 1 if number, 0 otherwise
|
||||||
@ -305,7 +306,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pid3tag=id3_file_tag(pid3file);
|
pid3tag=id3_file_tag(pid3file);
|
||||||
|
|
||||||
if(!pid3tag) {
|
if(!pid3tag) {
|
||||||
err=errno;
|
err=errno;
|
||||||
id3_file_close(pid3file);
|
id3_file_close(pid3file);
|
||||||
@ -332,7 +333,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(((pid3frame->id[0] == 'T')||(strcmp(pid3frame->id,"COMM")==0)) &&
|
if(((pid3frame->id[0] == 'T')||(strcmp(pid3frame->id,"COMM")==0)) &&
|
||||||
(id3_field_getnstrings(&pid3frame->fields[1])))
|
(id3_field_getnstrings(&pid3frame->fields[1])))
|
||||||
have_text=1;
|
have_text=1;
|
||||||
|
|
||||||
if(have_text) {
|
if(have_text) {
|
||||||
@ -345,7 +346,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
|
|||||||
* it's just plain wrong.
|
* it's just plain wrong.
|
||||||
*/
|
*/
|
||||||
have_utf8=1;
|
have_utf8=1;
|
||||||
if(config.latin1_tags) {
|
if(conf_get_int("general","latin1_tags",0)) {
|
||||||
utf8_text=(char *)id3_ucs4_latin1duplicate(native_text);
|
utf8_text=(char *)id3_ucs4_latin1duplicate(native_text);
|
||||||
} else {
|
} else {
|
||||||
utf8_text=(char *)id3_ucs4_utf8duplicate(native_text);
|
utf8_text=(char *)id3_ucs4_utf8duplicate(native_text);
|
||||||
@ -394,7 +395,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
|
|||||||
} else if ((pmp3->genre[0] == '(') && (isdigit(pmp3->genre[1]))) {
|
} else if ((pmp3->genre[0] == '(') && (isdigit(pmp3->genre[1]))) {
|
||||||
genre=atoi((char*)&pmp3->genre[1]);
|
genre=atoi((char*)&pmp3->genre[1]);
|
||||||
got_numeric_genre=1;
|
got_numeric_genre=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(got_numeric_genre) {
|
if(got_numeric_genre) {
|
||||||
if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN))
|
if((genre < 0) || (genre > WINAMP_GENRE_UNKNOWN))
|
||||||
@ -450,7 +451,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
|
|||||||
*
|
*
|
||||||
* iTunes_CDDB_IDs
|
* iTunes_CDDB_IDs
|
||||||
* iTunNORM
|
* iTunNORM
|
||||||
*
|
*
|
||||||
* If other apps stuff crap into comment fields, then we'll ignore them
|
* If other apps stuff crap into comment fields, then we'll ignore them
|
||||||
* here.
|
* here.
|
||||||
*/
|
*/
|
||||||
@ -487,7 +488,7 @@ int scan_mp3_get_mp3tags(char *file, MP3FILE *pmp3) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode an mp3 frame header. Determine layer, bitrate,
|
* Decode an mp3 frame header. Determine layer, bitrate,
|
||||||
* samplerate, etc, and fill in the passed structure.
|
* samplerate, etc, and fill in the passed structure.
|
||||||
*
|
*
|
||||||
* @param frame 4 byte mp3 frame header
|
* @param frame 4 byte mp3 frame header
|
||||||
@ -521,7 +522,7 @@ int scan_mp3_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi) {
|
|||||||
if((pfi->layer == 2) || (pfi->layer == 3))
|
if((pfi->layer == 2) || (pfi->layer == 3))
|
||||||
layer_index = 4;
|
layer_index = 4;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
pfi->version = 2.0;
|
pfi->version = 2.0;
|
||||||
sample_index=1;
|
sample_index=1;
|
||||||
if(pfi->layer == 1)
|
if(pfi->layer == 1)
|
||||||
@ -573,11 +574,11 @@ int scan_mp3_decode_mp3_frame(unsigned char *frame, SCAN_FRAMEINFO *pfi) {
|
|||||||
pfi->is_valid=0;
|
pfi->is_valid=0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pfi->bitrate = scan_br_table[layer_index][bitrate_index];
|
pfi->bitrate = scan_br_table[layer_index][bitrate_index];
|
||||||
pfi->samplerate = scan_sample_table[sample_index][samplerate_index];
|
pfi->samplerate = scan_sample_table[sample_index][samplerate_index];
|
||||||
|
|
||||||
if((frame[3] & 0xC0 >> 6) == 3)
|
if((frame[3] & 0xC0 >> 6) == 3)
|
||||||
pfi->stereo = 0;
|
pfi->stereo = 0;
|
||||||
else
|
else
|
||||||
pfi->stereo = 1;
|
pfi->stereo = 1;
|
||||||
@ -647,7 +648,7 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
|
|||||||
|
|
||||||
/* now, find the first frame */
|
/* now, find the first frame */
|
||||||
fseek(infile,pos,SEEK_SET);
|
fseek(infile,pos,SEEK_SET);
|
||||||
if(fread(frame_buffer,1,sizeof(frame_buffer),infile) != sizeof(frame_buffer))
|
if(fread(frame_buffer,1,sizeof(frame_buffer),infile) != sizeof(frame_buffer))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while(!found) {
|
while(!found) {
|
||||||
@ -658,8 +659,8 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
|
|||||||
DPRINTF(E_DBG,L_SCAN,"Could not find frame... quitting\n");
|
DPRINTF(E_DBG,L_SCAN,"Could not find frame... quitting\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!scan_mp3_decode_mp3_frame(&frame_buffer[index],&fi)) {
|
if(!scan_mp3_decode_mp3_frame(&frame_buffer[index],&fi)) {
|
||||||
/* see if next frame is valid */
|
/* see if next frame is valid */
|
||||||
fseek(infile,pos + index + fi.frame_length,SEEK_SET);
|
fseek(infile,pos + index + fi.frame_length,SEEK_SET);
|
||||||
if(fread(header,1,sizeof(header),infile) != sizeof(header)) {
|
if(fread(header,1,sizeof(header),infile) != sizeof(header)) {
|
||||||
@ -670,7 +671,7 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
|
|||||||
if(!scan_mp3_decode_mp3_frame(header,&fi))
|
if(!scan_mp3_decode_mp3_frame(header,&fi))
|
||||||
found=1;
|
found=1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!found)
|
if(!found)
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
@ -688,7 +689,7 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
|
|||||||
DPRINTF(E_DBG,L_SCAN,"Invalid frame header while averaging\n");
|
DPRINTF(E_DBG,L_SCAN,"Invalid frame header while averaging\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bitrate_total += fi.bitrate;
|
bitrate_total += fi.bitrate;
|
||||||
frame_count++;
|
frame_count++;
|
||||||
pos += fi.frame_length;
|
pos += fi.frame_length;
|
||||||
@ -705,7 +706,7 @@ void scan_mp3_get_average_bitrate(FILE *infile, SCAN_FRAMEINFO *pfi) {
|
|||||||
* do a full frame-by-frame scan of the file, counting frames
|
* do a full frame-by-frame scan of the file, counting frames
|
||||||
* as we go to try and get a more accurate song length estimate.
|
* as we go to try and get a more accurate song length estimate.
|
||||||
* If the song turns out to be CBR, then we'll not set the frame
|
* If the song turns out to be CBR, then we'll not set the frame
|
||||||
* length. Instead we'll use the file size estimate, since it is
|
* length. Instead we'll use the file size estimate, since it is
|
||||||
* more consistent with iTunes.
|
* more consistent with iTunes.
|
||||||
*
|
*
|
||||||
* @param infile file to scan for frame count
|
* @param infile file to scan for frame count
|
||||||
@ -722,7 +723,7 @@ void scan_mp3_get_frame_count(FILE *infile, SCAN_FRAMEINFO *pfi) {
|
|||||||
int last_bitrate=0;
|
int last_bitrate=0;
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_SCAN,"Starting frame count\n");
|
DPRINTF(E_DBG,L_SCAN,"Starting frame count\n");
|
||||||
|
|
||||||
fseek(infile,0,SEEK_END);
|
fseek(infile,0,SEEK_END);
|
||||||
file_size=ftell(infile);
|
file_size=ftell(infile);
|
||||||
|
|
||||||
@ -821,7 +822,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
if(strncmp((char*)pid3->id,"ID3",3)==0) {
|
if(strncmp((char*)pid3->id,"ID3",3)==0) {
|
||||||
/* found an ID3 header... */
|
/* found an ID3 header... */
|
||||||
DPRINTF(E_DBG,L_SCAN,"Found ID3 header\n");
|
DPRINTF(E_DBG,L_SCAN,"Found ID3 header\n");
|
||||||
size = (pid3->size[0] << 21 | pid3->size[1] << 14 |
|
size = (pid3->size[0] << 21 | pid3->size[1] << 14 |
|
||||||
pid3->size[2] << 7 | pid3->size[3]);
|
pid3->size[2] << 7 | pid3->size[3]);
|
||||||
fp_size=size + sizeof(SCAN_ID3HEADER);
|
fp_size=size + sizeof(SCAN_ID3HEADER);
|
||||||
first_check=1;
|
first_check=1;
|
||||||
@ -877,7 +878,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
if(!scan_mp3_decode_mp3_frame((u_char*)frame_buffer,&fi)) {
|
if(!scan_mp3_decode_mp3_frame((u_char*)frame_buffer,&fi)) {
|
||||||
found=1;
|
found=1;
|
||||||
fp_size += index;
|
fp_size += index;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DPRINTF(E_LOG,L_SCAN,"Could not read frame header: %s\n",file);
|
DPRINTF(E_LOG,L_SCAN,"Could not read frame header: %s\n",file);
|
||||||
fclose(infile);
|
fclose(infile);
|
||||||
@ -889,13 +890,13 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!found) {
|
if(!found) {
|
||||||
index++;
|
index++;
|
||||||
if (first_check) {
|
if (first_check) {
|
||||||
/* if the header info was wrong about where the data started,
|
/* if the header info was wrong about where the data started,
|
||||||
* then start a brute-force scan from the beginning of the file.
|
* then start a brute-force scan from the beginning of the file.
|
||||||
* don't want to just scan forward, because we might have already
|
* don't want to just scan forward, because we might have already
|
||||||
* missed the xing header
|
* missed the xing header
|
||||||
*/
|
*/
|
||||||
DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n");
|
DPRINTF(E_DBG,L_SCAN,"Bad header... dropping back for full frame search\n");
|
||||||
@ -923,7 +924,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
DPRINTF(E_DBG,L_SCAN," Layer: %d\n",fi.layer);
|
DPRINTF(E_DBG,L_SCAN," Layer: %d\n",fi.layer);
|
||||||
DPRINTF(E_DBG,L_SCAN," Sample Rate: %d\n",fi.samplerate);
|
DPRINTF(E_DBG,L_SCAN," Sample Rate: %d\n",fi.samplerate);
|
||||||
DPRINTF(E_DBG,L_SCAN," Bit Rate: %d\n",fi.bitrate);
|
DPRINTF(E_DBG,L_SCAN," Bit Rate: %d\n",fi.bitrate);
|
||||||
|
|
||||||
/* now check for an XING header */
|
/* now check for an XING header */
|
||||||
if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) {
|
if(strncasecmp((char*)&buffer[index+fi.xing_offset+4],"XING",4) == 0) {
|
||||||
DPRINTF(E_DBG,L_SCAN,"Found Xing header\n");
|
DPRINTF(E_DBG,L_SCAN,"Found Xing header\n");
|
||||||
@ -931,7 +932,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
buffer[index+fi.xing_offset+4+5] << 16 |
|
buffer[index+fi.xing_offset+4+5] << 16 |
|
||||||
buffer[index+fi.xing_offset+4+6] << 8 |
|
buffer[index+fi.xing_offset+4+6] << 8 |
|
||||||
buffer[index+fi.xing_offset+4+7];
|
buffer[index+fi.xing_offset+4+7];
|
||||||
|
|
||||||
DPRINTF(E_DBG,L_SCAN,"Xing Flags: %02X\n",xing_flags);
|
DPRINTF(E_DBG,L_SCAN,"Xing Flags: %02X\n",xing_flags);
|
||||||
|
|
||||||
if(xing_flags & 0x1) {
|
if(xing_flags & 0x1) {
|
||||||
@ -943,13 +944,13 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if((config.scan_type != 0) &&
|
if((conf_get_int("general","scan_type",0) != 0) &&
|
||||||
(fi.number_of_frames == 0) &&
|
(fi.number_of_frames == 0) &&
|
||||||
(!pmp3->song_length)) {
|
(!pmp3->song_length)) {
|
||||||
/* We have no good estimate of song time, and we want more
|
/* We have no good estimate of song time, and we want more
|
||||||
* aggressive scanning */
|
* aggressive scanning */
|
||||||
DPRINTF(E_DBG,L_SCAN,"Starting aggressive file length scan\n");
|
DPRINTF(E_DBG,L_SCAN,"Starting aggressive file length scan\n");
|
||||||
if(config.scan_type == 1) {
|
if(conf_get_int("general","scan_type",0) == 1) {
|
||||||
/* get average bitrate */
|
/* get average bitrate */
|
||||||
scan_mp3_get_average_bitrate(infile, &fi);
|
scan_mp3_get_average_bitrate(infile, &fi);
|
||||||
} else {
|
} else {
|
||||||
@ -960,7 +961,7 @@ int scan_mp3_get_mp3fileinfo(char *file, MP3FILE *pmp3) {
|
|||||||
|
|
||||||
pmp3->bitrate=fi.bitrate;
|
pmp3->bitrate=fi.bitrate;
|
||||||
pmp3->samplerate=fi.samplerate;
|
pmp3->samplerate=fi.samplerate;
|
||||||
|
|
||||||
/* guesstimate the file length */
|
/* guesstimate the file length */
|
||||||
if(!pmp3->song_length) { /* could have gotten it from the tag */
|
if(!pmp3->song_length) { /* could have gotten it from the tag */
|
||||||
/* DWB: use ms time instead of seconds, use doubles to
|
/* DWB: use ms time instead of seconds, use doubles to
|
||||||
|
37
src/ssc.c
37
src/ssc.c
@ -37,10 +37,11 @@
|
|||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
#include <netinet/in.h> /* htons and friends */
|
#include <netinet/in.h> /* htons and friends */
|
||||||
#include <dirent.h> /* why here? For osx 10.2, of course! */
|
#include <dirent.h> /* why here? For osx 10.2, of course! */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
#include "daapd.h"
|
#include "daapd.h"
|
||||||
#include "db-generic.h"
|
#include "db-generic.h"
|
||||||
#include "err.h"
|
#include "err.h"
|
||||||
@ -60,16 +61,21 @@
|
|||||||
* @returns 1 if should be converted. 0 if not
|
* @returns 1 if should be converted. 0 if not
|
||||||
*/
|
*/
|
||||||
int server_side_convert(char *codectype) {
|
int server_side_convert(char *codectype) {
|
||||||
if ((!config.ssc_codectypes) ||
|
char ssc_codectypes[PATH_MAX];
|
||||||
(!config.ssc_codectypes[0]) ||
|
int size;
|
||||||
(!config.ssc_prog) ||
|
|
||||||
(!config.ssc_prog[0]) ||
|
size = sizeof(ssc_codectypes);
|
||||||
|
conf_get_string("general","ssc_codectypes","ogg,flac,wma,alac",
|
||||||
|
ssc_codectypes,&size);
|
||||||
|
|
||||||
|
if ((!conf_isset("general","ssc_codectypes")) ||
|
||||||
|
(!conf_isset("general","ssc_prog")) ||
|
||||||
(!codectype)) {
|
(!codectype)) {
|
||||||
DPRINTF(E_DBG,L_SCAN,"Nope\n");
|
DPRINTF(E_DBG,L_SCAN,"Nope\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strcasestr(config.ssc_codectypes, codectype)) {
|
if(strcasestr(ssc_codectypes, codectype)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,14 +96,23 @@ FILE *server_side_convert_open(char *path, off_t offset, unsigned long len_ms, c
|
|||||||
char *metachars = "\"\\!(){}#*?$&<>`"; /* More?? */
|
char *metachars = "\"\\!(){}#*?$&<>`"; /* More?? */
|
||||||
char metacount = 0;
|
char metacount = 0;
|
||||||
char *src,*dst;
|
char *src,*dst;
|
||||||
|
char ssc_prog[PATH_MAX];
|
||||||
|
int size;
|
||||||
|
|
||||||
|
size = sizeof(ssc_prog);
|
||||||
|
conf_get_string("general","ssc_prog","",ssc_prog,&size);
|
||||||
|
|
||||||
|
if(ssc_prog[0] == '\0') { /* can't happen */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
src=path;
|
src=path;
|
||||||
while(*src) {
|
while(*src) {
|
||||||
if(strchr(metachars,*src))
|
if(strchr(metachars,*src))
|
||||||
metacount+=5;
|
metacount+=5;
|
||||||
src++;
|
src++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(metachars) {
|
if(metachars) {
|
||||||
newpath = (char*)malloc(strlen(path) + metacount + 1);
|
newpath = (char*)malloc(strlen(path) + metacount + 1);
|
||||||
if(!newpath) {
|
if(!newpath) {
|
||||||
@ -105,7 +120,7 @@ FILE *server_side_convert_open(char *path, off_t offset, unsigned long len_ms, c
|
|||||||
}
|
}
|
||||||
src=path;
|
src=path;
|
||||||
dst=newpath;
|
dst=newpath;
|
||||||
|
|
||||||
while(*src) {
|
while(*src) {
|
||||||
if(strchr(metachars,*src)) {
|
if(strchr(metachars,*src)) {
|
||||||
*dst++='"';
|
*dst++='"';
|
||||||
@ -123,11 +138,11 @@ FILE *server_side_convert_open(char *path, off_t offset, unsigned long len_ms, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: is 64 enough? is there a better way to determine this? */
|
/* FIXME: is 64 enough? is there a better way to determine this? */
|
||||||
cmd=(char *)malloc(strlen(config.ssc_prog) +
|
cmd=(char *)malloc(strlen(ssc_prog) +
|
||||||
strlen(path) +
|
strlen(path) +
|
||||||
64);
|
64);
|
||||||
sprintf(cmd, "%s \"%s\" %ld %lu.%03lu \"%s\"",
|
sprintf(cmd, "%s \"%s\" %ld %lu.%03lu \"%s\"",
|
||||||
config.ssc_prog, newpath, (long)offset, len_ms / 1000,
|
ssc_prog, newpath, (long)offset, len_ms / 1000,
|
||||||
len_ms % 1000, (codectype && *codectype) ? codectype : "*");
|
len_ms % 1000, (codectype && *codectype) ? codectype : "*");
|
||||||
DPRINTF(E_INF,L_SCAN,"Executing %s\n",cmd);
|
DPRINTF(E_INF,L_SCAN,"Executing %s\n",cmd);
|
||||||
f = popen(cmd, "r");
|
f = popen(cmd, "r");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user