diff --git a/src/Makefile.am b/src/Makefile.am index e3a3d9d8..fae29c14 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -30,6 +30,7 @@ noinst_HEADERS = \ matchfile.h \ tls.h \ refobject.h \ + module.h \ event.h \ event_log.h \ event_exec.h \ @@ -70,6 +71,7 @@ icecast_SOURCES = \ matchfile.c \ tls.c \ refobject.c \ + module.c \ format.c \ format_ogg.c \ format_mp3.c \ diff --git a/src/cfgfile.c b/src/cfgfile.c index 0b6c7b27..f78095ee 100644 --- a/src/cfgfile.c +++ b/src/cfgfile.c @@ -562,6 +562,8 @@ static void config_clear_resource(resource_t *resource) xmlFree(resource->destination); xmlFree(resource->bind_address); xmlFree(resource->vhost); + xmlFree(resource->module); + xmlFree(resource->handler); free(resource); resource = nextresource; } @@ -1948,6 +1950,9 @@ static void _parse_resource(xmlDocPtr doc, resource->vhost = (char *)xmlGetProp(node, XMLSTR("vhost")); + resource->module = (char *)xmlGetProp(node, XMLSTR("module")); + resource->handler = (char *)xmlGetProp(node, XMLSTR("handler")); + temp = (char *)xmlGetProp(node, XMLSTR("omode")); if (temp) { resource->omode = config_str_to_omode(temp); diff --git a/src/cfgfile.h b/src/cfgfile.h index 9705bbb7..25fbfbbb 100644 --- a/src/cfgfile.h +++ b/src/cfgfile.h @@ -139,6 +139,8 @@ typedef struct _resource { int port; char *bind_address; char *vhost; + char *module; + char *handler; operation_mode omode; unsigned int flags; struct _resource *next; diff --git a/src/client.c b/src/client.c index d80edcdf..44ae457e 100644 --- a/src/client.c +++ b/src/client.c @@ -29,6 +29,7 @@ #include "common/httpp/httpp.h" #include "global.h" +#include "refobject.h" #include "cfgfile.h" #include "connection.h" #include "refbuf.h" @@ -171,6 +172,8 @@ void client_destroy(client_t *client) if (client->free_client_data) client->free_client_data(client); + refobject_unref(client->handler_module); + free(client->handler_function); free(client->username); free(client->password); free(client->role); diff --git a/src/client.h b/src/client.h index aa9318b0..d87a2465 100644 --- a/src/client.h +++ b/src/client.h @@ -25,6 +25,7 @@ #include "icecasttypes.h" #include "errors.h" #include "refbuf.h" +#include "module.h" #define CLIENT_DEFAULT_ADMIN_FORMAT ADMIN_FORMAT_TRANSFORMED @@ -82,6 +83,10 @@ struct _client_tag { /* active ACL, set as soon as the client is authenticated */ acl_t *acl; + /* Handler module and function */ + module_t *handler_module; + char *handler_function; + /* is client getting intro data */ long intro_offset; diff --git a/src/connection.c b/src/connection.c index 50b79aeb..352a31cd 100644 --- a/src/connection.c +++ b/src/connection.c @@ -43,6 +43,7 @@ #include "cfgfile.h" #include "global.h" #include "util.h" +#include "refobject.h" #include "refbuf.h" #include "client.h" #include "errors.h" @@ -1135,6 +1136,28 @@ static int _handle_resources(client_t *client, char **uri) } if (resource->omode != OMODE_DEFAULT) client->mode = resource->omode; + + if (resource->module) { + module_t *module = module_container_get_module(global.modulecontainer, resource->module); + + if (module != NULL) { + refobject_unref(client->handler_module); + client->handler_module = module; + } else { + ICECAST_LOG_ERROR("Module used in alias not found: %s", resource->module); + } + } + + if (resource->handler) { + char *func = strdup(resource->handler); + if (func) { + free(client->handler_function); + client->handler_function = func; + } else { + ICECAST_LOG_ERROR("Can not allocate memory."); + } + } + ICECAST_LOG_DEBUG("resource has made %s into %s", *uri, new_uri); break; } @@ -1193,6 +1216,17 @@ static void _handle_authed_client(client_t *client, void *uri, auth_result resul return; } + if (client->handler_module && client->handler_function) { + const module_client_handler_t *handler = module_get_client_handler(client->handler_module, client->handler_function); + if (handler) { + handler->cb(client->handler_module, client, uri); + free(uri); + return; + } else { + ICECAST_LOG_ERROR("No such handler function in module: %s", client->handler_function); + } + } + switch (client->parser->req_type) { case httpp_req_source: case httpp_req_put: diff --git a/src/global.c b/src/global.c index 81d6dedb..3b61081a 100644 --- a/src/global.c +++ b/src/global.c @@ -21,6 +21,8 @@ #include "common/avl/avl.h" #include "global.h" +#include "refobject.h" +#include "module.h" #include "source.h" ice_global_t global; @@ -36,12 +38,14 @@ void global_initialize(void) global.clients = 0; global.sources = 0; global.source_tree = avl_tree_new(source_compare_sources, NULL); + global.modulecontainer = module_container_new(); thread_mutex_create(&_global_mutex); } void global_shutdown(void) { thread_mutex_destroy(&_global_mutex); + refobject_unref(global.modulecontainer); avl_tree_free(global.source_tree, NULL); } diff --git a/src/global.h b/src/global.h index c7e6633a..bc02de8d 100644 --- a/src/global.h +++ b/src/global.h @@ -42,6 +42,8 @@ typedef struct ice_global_tag /* relays retrieved from master */ relay_server *master_relays; + module_container_t *modulecontainer; + cond_t shutdown_cond; } ice_global_t; diff --git a/src/icecasttypes.h b/src/icecasttypes.h index 0474e0f3..36b198a4 100644 --- a/src/icecasttypes.h +++ b/src/icecasttypes.h @@ -92,6 +92,12 @@ typedef enum { typedef struct _relay_server relay_server; +/* ---[ module.[ch] ]--- */ + +typedef struct module_tag module_t; + +typedef struct module_container_tag module_container_t; + /* ---[ refobject.[ch] ]--- */ typedef struct refobject_base_tag refobject_base_t; @@ -99,6 +105,8 @@ typedef struct refobject_base_tag refobject_base_t; #ifdef HAVE_TYPE_ATTRIBUTE_TRANSPARENT_UNION typedef union __attribute__ ((__transparent_union__)) { refobject_base_t *refobject_base; + module_t *module; + module_container_t *module_container; } refobject_t; #else typedef void * refobject_t; diff --git a/src/main.c b/src/main.c index ce8af228..74277778 100644 --- a/src/main.c +++ b/src/main.c @@ -122,12 +122,12 @@ static void initialize_subsystems(void) { log_initialize(); thread_initialize(); + global_initialize(); sock_initialize(); resolver_initialize(); config_initialize(); tls_initialize(); connection_initialize(); - global_initialize(); refbuf_initialize(); xslt_initialize(); @@ -146,12 +146,12 @@ static void shutdown_subsystems(void) yp_shutdown(); stats_shutdown(); - global_shutdown(); connection_shutdown(); tls_shutdown(); config_shutdown(); resolver_shutdown(); sock_shutdown(); + global_shutdown(); thread_shutdown(); #ifdef HAVE_CURL diff --git a/src/module.c b/src/module.c new file mode 100644 index 00000000..1e68b4ef --- /dev/null +++ b/src/module.c @@ -0,0 +1,184 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2018, Philipp "ph3-der-loewe" Schafft , + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "common/thread/thread.h" +#include "common/avl/avl.h" + +#include "refobject.h" +#include "module.h" + +struct module_tag { + refobject_base_t __base; + mutex_t lock; + const module_client_handler_t *client_handlers; + size_t client_handlers_len; + module_setup_handler_t freecb; + void *userdata; +}; + + +struct module_container_tag { + refobject_base_t __base; + mutex_t lock; + avl_tree *module; +}; + +static int compare_refobject_t_name(void *arg, void *a, void *b) +{ + return strcmp(refobject_get_name(a), refobject_get_name(b)); +} + +static void __module_container_free(refobject_t self, void **userdata) +{ + module_container_t *cont = REFOBJECT_TO_TYPE(self, module_container_t *); + thread_mutex_destroy(&(cont->lock)); + avl_tree_free(cont->module, (avl_free_key_fun_type)refobject_unref); +} + +module_container_t * module_container_new(void) +{ + module_container_t *ret = REFOBJECT_TO_TYPE(refobject_new(sizeof(module_container_t), __module_container_free, NULL, NULL, NULL), module_container_t *); + + if (!ret) + return NULL; + + thread_mutex_create(&(ret->lock)); + + ret->module = avl_tree_new(compare_refobject_t_name, NULL); + + return ret; +} + +int module_container_add_module(module_container_t *self, module_t *module) +{ + if (!self) + return -1; + + if (refobject_ref(module) != 0) + return -1; + + thread_mutex_lock(&(self->lock)); + avl_insert(self->module, module); + thread_mutex_unlock(&(self->lock)); + + return 0; +} + +int module_container_delete_module(module_container_t *self, const char *name) +{ + module_t *module; + + if (!self || !name) + return -1; + + module = module_container_get_module(self, name); + if (!module) + return -1; + + thread_mutex_lock(&(self->lock)); + avl_delete(self->module, module, (avl_free_key_fun_type)refobject_unref); + thread_mutex_unlock(&(self->lock)); + + refobject_unref(module); + + return 0; +} + +module_t * module_container_get_module(module_container_t *self, const char *name) +{ + refobject_t search; + module_t *ret; + + if (!self || !name) + return NULL; + + search = refobject_new(sizeof(refobject_base_t), NULL, NULL, name, NULL); + + if (avl_get_by_key(self->module, REFOBJECT_TO_TYPE(search, void *), (void**)&ret) != 0) { + ret = NULL; + } + + refobject_unref(search); + refobject_ref(ret); + + return ret; +} + +static void __module_free(refobject_t self, void **userdata) +{ + module_t *mod = REFOBJECT_TO_TYPE(self, module_t *); + + if (mod->freecb) + mod->freecb(mod, &(mod->userdata)); + + if (mod->userdata) + free(mod->userdata); + + thread_mutex_destroy(&(mod->lock)); +} + +module_t * module_new(const char *name, module_setup_handler_t newcb, module_setup_handler_t freecb, void *userdata) +{ + module_t *ret = REFOBJECT_TO_TYPE(refobject_new(sizeof(module_t), __module_free, NULL, name, NULL), module_t *); + + if (!ret) + return NULL; + + thread_mutex_create(&(ret->lock)); + + ret->userdata = userdata; + ret->freecb = freecb; + + if (newcb) { + if (newcb(ret, &(ret->userdata)) != 0) { + refobject_unref(ret); + return NULL; + } + } + + return ret; +} + +const module_client_handler_t * module_get_client_handler(module_t *self, const char *name) +{ + size_t i; + + if (!self || !name) + return NULL; + + thread_mutex_lock(&(self->lock)); + for (i = 0; i < self->client_handlers_len; i++) { + if (self->client_handlers[i].name && strcmp(self->client_handlers[i].name, name) == 0) { + thread_mutex_unlock(&(self->lock)); + return &(self->client_handlers[i]); + } + } + thread_mutex_unlock(&(self->lock)); + + return NULL; +} + +int module_add_client_handler(module_t *self, const module_client_handler_t *handlers, size_t len) +{ + if (!self) + return -1; + + thread_mutex_lock(&(self->lock)); + self->client_handlers = handlers; + self->client_handlers_len = len; + thread_mutex_unlock(&(self->lock)); + + return 0; +} diff --git a/src/module.h b/src/module.h new file mode 100644 index 00000000..8d2ad781 --- /dev/null +++ b/src/module.h @@ -0,0 +1,33 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2018, Philipp "ph3-der-loewe" Schafft , + */ + +#ifndef __MODULE_H__ +#define __MODULE_H__ + +#include "icecasttypes.h" + +typedef void (*module_client_handler_function_t)(module_t *self, client_t *client, const char *uri); +typedef int (*module_setup_handler_t)(module_t *self, void **userdata); + +typedef struct { + const char *name; + module_client_handler_function_t cb; +} module_client_handler_t; + +module_container_t * module_container_new(void); +int module_container_add_module(module_container_t *self, module_t *module); +int module_container_delete_module(module_container_t *self, const char *name); +module_t * module_container_get_module(module_container_t *self, const char *name); + +module_t * module_new(const char *name, module_setup_handler_t newcb, module_setup_handler_t freecb, void *userdata); + +/* Note: Those functions are not really thread safe as (module_client_handler_t) is not thread safe. This is by design. */ +const module_client_handler_t * module_get_client_handler(module_t *self, const char *name); +int module_add_client_handler(module_t *self, const module_client_handler_t *handlers, size_t len); + +#endif