From e311b4335435fbf7f5cb01e27918b5bb9c8b7435 Mon Sep 17 00:00:00 2001 From: Ron Pedde Date: Wed, 12 Nov 2003 06:23:01 +0000 Subject: [PATCH] move config web dispatcher to configfile.c, add more flexible config parsing --- src/configfile.c | 248 +++++++++++++++++++++++++++++++++++++++-------- src/configfile.h | 5 +- 2 files changed, 212 insertions(+), 41 deletions(-) diff --git a/src/configfile.c b/src/configfile.c index 94bfe4ae..ac02eeb1 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -19,18 +19,52 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include +#include #include #include #include #include +#include #include "configfile.h" #include "err.h" + +/* + * Forwards + */ +void config_emit_string(void *value,WS_CONNINFO *pwsc); +void config_emit_int(void *value,WS_CONNINFO *pwsc); + /* * Defines */ +#define CONFIG_TYPE_INT 0 +#define CONFIG_TYPE_STRING 1 + +typedef struct tag_configelement { + int config_element; + int required; + int changed; + int type; + char *name; + void *var; + void (*emit)(void *,WS_CONNINFO *); +} CONFIGELEMENT; + +CONFIGELEMENT config_elements[] = { + { 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_STRING,"admin_pw",(void*)&config.adminpassword,config_emit_string }, + { 1,1,0,CONFIG_TYPE_STRING,"mp3_dir",(void*)&config.mp3dir,config_emit_string }, + { -1,1,0,CONFIG_TYPE_STRING,NULL,NULL,NULL } +}; + #define MAX_LINE 1024 @@ -43,7 +77,7 @@ * * This function returns 0 on success, errorcode on failure */ -int config_read(char *file, CONFIG *pconfig) { +int config_read(char *file) { FILE *fin; char *buffer; int err; @@ -51,6 +85,8 @@ int config_read(char *file, CONFIG *pconfig) { char *comment; char path_buffer[PATH_MAX]; err=0; + CONFIGELEMENT *pce; + int handled; buffer=(char*)malloc(MAX_LINE); if(!buffer) @@ -63,9 +99,9 @@ int config_read(char *file, CONFIG *pconfig) { return -1; } - memset(pconfig,0,sizeof(CONFIG)); + memset((void*)&config,0,sizeof(CONFIG)); - pconfig->configfile=strdup(file); + config.configfile=strdup(file); while(fgets(buffer,MAX_LINE,fin)) { if(*buffer != '#') { @@ -81,21 +117,30 @@ int config_read(char *file, CONFIG *pconfig) { if(value[strlen(value)-1] == '\n') value[strlen(value)-1] = '\0'; - if(!strcasecmp(buffer,"web_root")) { - realpath(value,path_buffer); - pconfig->web_root=strdup(path_buffer); - DPRINTF(ERR_DEBUG,"Web root: %s\n",path_buffer); - } else if(!strcasecmp(buffer,"port")) { - pconfig->port=atoi(value); - DPRINTF(ERR_DEBUG,"Port: %d\n",pconfig->port); - } else if(!strcasecmp(buffer,"admin_password")) { - pconfig->adminpassword=strdup(value); - DPRINTF(ERR_DEBUG,"Admin pw: %s\n",value); - } else if(!strcasecmp(buffer,"mp3_dir")) { - pconfig->mp3dir=strdup(value); - DPRINTF(ERR_DEBUG,"MP3 Dir: %s\n",value); - } else { - DPRINTF(ERR_INFO,"Bad config directive: %s\n",buffer); + pce=config_elements; + handled=0; + while((!handled) && (pce->config_element != -1)) { + if((strcasecmp(buffer,pce->name)==0) && (pce->config_element)) { + /* valid config directive */ + handled=1; + pce->changed=1; + + switch(pce->type) { + case CONFIG_TYPE_STRING: + 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; } } } @@ -103,29 +148,18 @@ int config_read(char *file, CONFIG *pconfig) { fclose(fin); - if(!pconfig->web_root) { - fprintf(stderr,"Config: missing web_root entry\n"); - errno=EINVAL; - err=-1; - } - - if(!pconfig->adminpassword) { - fprintf(stderr,"Config: missing admin_password entry\n"); - errno=EINVAL; - err=-1; - } - - if(!pconfig->port) { - fprintf(stderr,"Config: missing port entry\n"); - errno=EINVAL; - err=-1; - } - - if(!pconfig->mp3dir) { - fprintf(stderr,"Config: missing mp3_dir entry\n"); - errno=EINVAL; - err=-1; + /* 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) { + fprintf(stderr,"Required config entry '%s' not specified\n",pce->name); + err=-1; + } + pce->changed=0; + pce++; } + return err; } @@ -139,3 +173,137 @@ int config_write(CONFIG *pconfig) { return 0; } +/* + * config_handler + * + * Handle serving pages from the admin-root + */ +void config_handler(WS_CONNINFO *pwsc) { + char path[PATH_MAX]; + char resolved_path[PATH_MAX]; + int file_fd; + struct stat sb; + char argbuffer[30]; + int in_arg; + char *argptr; + char next; + + pwsc->close=1; + ws_addresponseheader(pwsc,"Connection","close"); + + snprintf(path,PATH_MAX,"%s/%s",config.web_root,pwsc->uri); + if(!realpath(path,resolved_path)) { + pwsc->error=errno; + DPRINTF(ERR_WARN,"Cannot resolve %s\n",path); + ws_returnerror(pwsc,404,"Not found"); + return; + } + + /* this should really return a 302:Found */ + stat(resolved_path,&sb); + if(sb.st_mode & S_IFDIR) + strcat(resolved_path,"/index.html"); + + DPRINTF(ERR_DEBUG,"Thread %d: Preparing to serve %s\n", + pwsc->threadno, resolved_path); + + if(strncmp(resolved_path,config.web_root, + strlen(config.web_root))) { + pwsc->error=EINVAL; + DPRINTF(ERR_WARN,"Thread %d: Requested file %s out of root\n", + pwsc->threadno,resolved_path); + ws_returnerror(pwsc,403,"Forbidden"); + return; + } + + file_fd=r_open2(resolved_path,O_RDONLY); + if(file_fd == -1) { + pwsc->error=errno; + DPRINTF(ERR_WARN,"Thread %d: Error opening %s: %s\n", + pwsc->threadno,resolved_path,strerror(errno)); + ws_returnerror(pwsc,404,"Not found"); + return; + } + + if(strcasecmp(pwsc->uri,"/config-update.html")==0) { + /* we need to update stuff */ + argptr=ws_getvar(pwsc,"adminpw"); + if(argptr) { + if(config.adminpassword) + free(config.adminpassword); + config.adminpassword=strdup(argptr); + } + } + + ws_writefd(pwsc,"HTTP/1.1 200 OK\r\n"); + ws_emitheaders(pwsc); + + /* now throw out the file, with replacements */ + in_arg=0; + argptr=argbuffer; + + while(1) { + if(r_read(file_fd,&next,1) <= 0) + break; + + if(in_arg) { + if(next == '@') { + in_arg=0; + if(strcasecmp(argbuffer,"WEB_ROOT") == 0) { + ws_writefd(pwsc,"%s",config.web_root); + } else if (strcasecmp(argbuffer,"PORT") == 0) { + ws_writefd(pwsc,"%d",config.port); + } else if (strcasecmp(argbuffer,"ADMINPW") == 0) { + ws_writefd(pwsc,"%s",config.adminpassword); + } else if (strcasecmp(argbuffer,"RELEASE") == 0) { + ws_writefd(pwsc,"mt-daapd %s\n",VERSION); + } else if (strcasecmp(argbuffer,"MP3DIR") == 0) { + ws_writefd(pwsc,"%s",config.mp3dir); + } else { + ws_writefd(pwsc,"@ERR@"); + } + } else { + if((argptr - argbuffer) < (sizeof(argbuffer)-1)) + *argptr++ = next; + } + } else { + if(next == '@') { + argptr=argbuffer; + memset(argbuffer,0,sizeof(argbuffer)); + in_arg=1; + } else { + if(r_write(pwsc->fd,&next,1) == -1) + break; + } + } + } + + r_close(file_fd); + DPRINTF(ERR_DEBUG,"Thread %d: Served successfully\n",pwsc->threadno); + return; +} + +int config_auth(char *user, char *password) { + return !strcmp(password,config.adminpassword); +} + + +/* + * config_emit_string + * + * write a simple string value to the connection + */ +void config_emit_string(void *value,WS_CONNINFO *pwsc) { + ws_writefd(pwsc,"%s",value); +} + + +/* + * config_emit_int + * + * write a simple int value to the connection + */ +void config_emit_int(void *value,WS_CONNINFO *pwsc) { + ws_writefd(pwsc,"%d",value); +} + diff --git a/src/configfile.h b/src/configfile.h index 651d94f6..12362697 100644 --- a/src/configfile.h +++ b/src/configfile.h @@ -23,8 +23,11 @@ #define _CONFIGFILE_H_ #include "daapd.h" +#include "webserver.h" -extern int config_read(char *file, CONFIG *pconfig); +extern int config_read(char *file); extern int config_write(CONFIG *pconfig); +extern int config_auth(char *user, char *password); +extern void config_handler(WS_CONNINFO *pwsc); #endif /* _CONFIGFILE_H_ */