diff --git a/src/main.c b/src/main.c index 5a79dac1..ad5a8599 100644 --- a/src/main.c +++ b/src/main.c @@ -109,7 +109,7 @@ CONFIG config; /**< Main configuration structure, as read from configfile */ */ static void usage(char *program); static void txt_add(char *txtrecord, char *fmt, ...); - +static void main_handler(WS_CONNINFO *pwsc); /** * build a dns text string @@ -133,6 +133,18 @@ void txt_add(char *txtrecord, char *fmt, ...) { strcpy(end+1,buff); } +void main_handler(WS_CONNINFO *pwsc) { + if(plugin_url_candispatch(pwsc)) { + DPRINTF(E_DBG,L_MAIN,"Dispatching %s to plugin\n",pwsc->uri); + plugin_url_handle(pwsc); + return; + } + + DPRINTF(E_DBG,L_MAIN,"Dispatching %s to config handler\n",pwsc->uri); + config_handler(pwsc); +} + + /** * Print usage information to stdout @@ -391,7 +403,7 @@ int main(int argc, char *argv[]) { DPRINTF(E_FATAL,L_MAIN|L_WS,"Error staring web server: %s\n",strerror(errno)); } - ws_registerhandler(config.server, "^.*$",config_handler,config_auth,1); + ws_registerhandler(config.server, "^.*$",main_handler,config_auth,1); ws_registerhandler(config.server, "^/server-info$",daap_handler,NULL,0); ws_registerhandler(config.server, "^/content-codes$",daap_handler,daap_auth,0); /* iTunes 6.0.4+? */ ws_registerhandler(config.server,"^/login$",daap_handler,daap_auth,0); diff --git a/src/os-unix.c b/src/os-unix.c index 29e57f96..95113f64 100644 --- a/src/os-unix.c +++ b/src/os-unix.c @@ -428,12 +428,22 @@ int _os_start_signal_handler(pthread_t *handler_tid) { * * @param */ -void *os_loadlib(char *path) { - return dlopen(path,RTLD_LAZY); +void *os_loadlib(char **pe, char *path) { + void *retval; + + if(!(retval = dlopen(path,RTLD_NOW))) + *pe = strdup(dlerror()); + + return retval; } -void *os_libfunc(void *handle, char *function) { - return dlsym(handle,function); +void *os_libfunc(char **pe, void *handle, char *function) { + void *retval; + + if(!(retval = dlsym(handle,function))) + *pe = strdup(dlerror()); + + return retval; } int *os_unload(void *handle) { diff --git a/src/os.h b/src/os.h index 86e2191f..b12082c3 100644 --- a/src/os.h +++ b/src/os.h @@ -34,8 +34,8 @@ extern int os_syslog(int level, char *msg); extern int os_chown(char *path, char *user); /* library loading functions */ -extern void *os_loadlib(char *path); -extern void *os_libfunc(void *handle, char *function); +extern void *os_loadlib(char **pe, char *path); +extern void *os_libfunc(char **pe, void *handle, char *function); extern void *os_unload(void *handle); #ifdef WIN32 diff --git a/src/plugin.c b/src/plugin.c index 86ca10ac..d875c7c4 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -33,16 +34,21 @@ #include "err.h" #include "os.h" #include "plugin.h" +#include "xml-rpc.h" +#include "webserver.h" typedef struct tag_pluginentry { void *phandle; int type; char *versionstring; + regex_t regex; + void *functions; struct tag_pluginentry *next; } PLUGIN_ENTRY; /* Globals */ -static PLUGIN_ENTRY _plugin_list = { NULL, 0, NULL, NULL }; +static PLUGIN_ENTRY _plugin_list; +static int _plugin_initialized = 0; static pthread_mutex_t _plugin_mutex = PTHREAD_MUTEX_INITIALIZER; static char* _plugin_error_list[] = { @@ -51,7 +57,6 @@ static char* _plugin_error_list[] = { "Plugin missing required export: plugin_type/plugin_ver" }; - /* Forwards */ void _plugin_lock(void); void _plugin_unlock(void); @@ -114,14 +119,15 @@ int _plugin_error(char **pe, int error, ...) { int plugin_load(char **pe, char *path) { PLUGIN_ENTRY *ppi; void *phandle; - int (*type_func)(void); - char* (*ver_func)(void); + PLUGIN_INFO *(*info_func)(void); + PLUGIN_INFO *pinfo; DPRINTF(E_DBG,L_PLUG,"Attempting to load plugin %s\n",path); - phandle = os_loadlib(path); + phandle = os_loadlib(pe, path); if(!phandle) { - return _plugin_error(pe,PLUGIN_E_NOLOAD,strerror(errno)); + DPRINTF(E_INF,L_PLUG,"Couldn't get lib handle for %s\n",path); + return PLUGIN_E_NOLOAD; } ppi = (PLUGIN_ENTRY*)malloc(sizeof(PLUGIN_ENTRY)); @@ -129,19 +135,32 @@ int plugin_load(char **pe, char *path) { ppi->phandle = phandle; - type_func = (int(*)(void)) os_libfunc(phandle,"plugin_type"); - ver_func = (char*(*)(void)) os_libfunc(phandle,"plugin_ver"); - - if((type_func == NULL) || (ver_func == NULL)) - return _plugin_error(pe,PLUGIN_E_BADFUNCS); - - ppi->type = type_func(); - ppi->versionstring = ver_func(); + info_func = (PLUGIN_INFO*(*)(void)) os_libfunc(pe, phandle,"plugin_info"); + if(info_func == NULL) { + DPRINTF(E_INF,L_PLUG,"Couldn't get info_func for %s\n",path); + return PLUGIN_E_BADFUNCS; + } + pinfo = info_func(); + + ppi->type = pinfo->type; + ppi->versionstring = pinfo->server; + if(ppi->type == PLUGIN_OUTPUT) { + /* build the regex */ + if(regcomp(&ppi->regex,pinfo->url,REG_EXTENDED | REG_NOSUB)) { + DPRINTF(E_LOG,L_PLUG,"Bad regex in %s: %s\n",path,pinfo->url); + } + } + ppi->functions = pinfo->handler_functions; + DPRINTF(E_INF,L_PLUG,"Loaded plugin %s (%s)\n",path,ppi->versionstring); _plugin_lock(); - + if(!_plugin_initialized) { + _plugin_initialized = 1; + memset((void*)&_plugin_list,0,sizeof(_plugin_list)); + } + ppi->next = _plugin_list.next; _plugin_list.next = ppi; @@ -149,4 +168,100 @@ int plugin_load(char **pe, char *path) { return PLUGIN_E_SUCCESS; } - + +/** + * check to see if we want to dispatch a particular url + * + * @param pwsc the connection info (including uri) to check + * @returns TRUE if we want to handle it + */ +int plugin_url_candispatch(WS_CONNINFO *pwsc) { + PLUGIN_ENTRY *ppi; + + _plugin_lock(); + ppi = _plugin_list.next; + while(ppi) { + if(ppi->type == PLUGIN_OUTPUT) { + if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) { + /* we have a winner */ + _plugin_unlock(); + return TRUE; + } + ppi = ppi->next; + } + } + _plugin_unlock(); + return FALSE; +} + + +/** + * actually DISPATCH the hander we said we wanted + * + * @param pwsc the connection info (including uri) to check + * @returns TRUE if we want to handle it + */ +void plugin_url_handle(WS_CONNINFO *pwsc) { + PLUGIN_ENTRY *ppi; + void (*disp_fn)(WS_CONNINFO *pwsc); + + _plugin_lock(); + ppi = _plugin_list.next; + while(ppi) { + if(ppi->type == PLUGIN_OUTPUT) { + if(!regexec(&ppi->regex,pwsc->uri,0,NULL,0)) { + /* we have a winner */ + DPRINTF(E_DBG,L_PLUG,"Dispatching %s to %s\n", pwsc->uri, + ppi->versionstring); + + /* so functions must be a tag_plugin_output_fn */ + disp_fn=(((PLUGIN_OUTPUT_FN*)ppi->functions)->handler); + disp_fn(pwsc); + + ws_returnerror(pwsc,404,"Wtf!"); + _plugin_unlock(); + return; + } + ppi = ppi->next; + } + } + + /* should 500 here or something */ + ws_returnerror(pwsc, 500, "Can't find plugin handler"); + _plugin_unlock(); + return; +} + +/* plugin wrappers for utility functions & stuff + * + * these functions need to be wrapped so we can maintain a stable + * interface to older plugins even if we get newer functions or apis + * upstream... it's a binary compatibility layer. + */ +XMLSTRUCT *pi_xml_init(WS_CONNINFO *pwsc, int emit_header) { + return xml_init(pwsc, emit_header); +} + +void pi_xml_push(XMLSTRUCT *pxml, char *term) { + return xml_push(pxml, term); +} + +void pi_xml_pop(XMLSTRUCT *pxml) { + return xml_pop(pxml); +} + +/* FIXME: 256? */ +void pi_xml_output(XMLSTRUCT *pxml, char *section, char *fmt, ...) { + char buf[256]; + va_list ap; + + va_start(ap,fmt); + vsnprintf(buf,sizeof(buf),fmt,ap); + va_end(ap); + + xml_output(pxml,section,"%s",buf); +} + +void pi_xml_deinit(XMLSTRUCT *pxml) { + return xml_deinit(pxml); +} diff --git a/src/plugin.h b/src/plugin.h index 1672c8cb..a2fce0b9 100644 --- a/src/plugin.h +++ b/src/plugin.h @@ -22,11 +22,38 @@ #ifndef _PLUGIN_H_ #define _PLUGIN_H_ +#include "webserver.h" + extern int plugin_load(char **pe, char *path); +/* Interfaces for web */ +extern int plugin_url_candispatch(WS_CONNINFO *pwsc); +extern void plugin_url_handle(WS_CONNINFO *pwsc); + #define PLUGIN_E_SUCCESS 0 #define PLUGIN_E_NOLOAD 1 #define PLUGIN_E_BADFUNCS 2 +#define PLUGIN_OUTPUT 0 +#define PLUGIN_SCANNER 1 +#define PLUGIN_DATABASE 2 +#define PLUGIN_OTHER 3 + +#define PLUGIN_VERSION 1 + + +typedef struct tag_plugin_output_fn { + void(*handler)(WS_CONNINFO *pwsc); +} PLUGIN_OUTPUT_FN; + +/* version 1 plugin info */ +typedef struct tag_plugin_info { + int version; + int type; + char *server; + char *url; /* for output plugins */ + void *handler_functions; +} PLUGIN_INFO; + #endif /* _PLUGIN_H_ */ diff --git a/src/webserver.c b/src/webserver.c index e64dd5a8..3ea1bcb6 100644 --- a/src/webserver.c +++ b/src/webserver.c @@ -1268,10 +1268,6 @@ char *ws_urldecode(char *string, int space_as_plus) { while(*src) { switch(*src) { - /* DWB - space gets converted to %20, not +, this definitely breaks compatibility with iTunes */ - /* But the browsers encode space as plus, so when using the web interface, - * anything with a plus is broken. This will end up having to be sniffed - * by remote agent */ case '+': if(space_as_plus) { *dst++=' '; diff --git a/src/xml-rpc.c b/src/xml-rpc.c index a435dcd0..ff7b8c5c 100644 --- a/src/xml-rpc.c +++ b/src/xml-rpc.c @@ -165,6 +165,8 @@ void xml_pop(XMLSTRUCT *pxml) { pxml->stack_level--; } +/* FIXME: Fixed at 256? And can't I get an expandable sprintf/cat? */ + /** * output a string */