mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2025-01-03 14:56:34 -05:00
Merge branch 'ph3-resourcematch'
This commit is contained in:
commit
cf093b11d7
@ -7,6 +7,7 @@ bin_PROGRAMS = icecast
|
||||
noinst_HEADERS = \
|
||||
icecasttypes.h \
|
||||
admin.h \
|
||||
resourcematch.h \
|
||||
main.h \
|
||||
cfgfile.h \
|
||||
logging.h \
|
||||
@ -69,6 +70,7 @@ icecast_SOURCES = \
|
||||
xslt.c \
|
||||
fserve.c \
|
||||
admin.c \
|
||||
resourcematch.c \
|
||||
md5.c \
|
||||
matchfile.c \
|
||||
tls.c \
|
||||
|
86
src/admin.c
86
src/admin.c
@ -118,37 +118,37 @@ static void command_updatemetadata (client_t *client, source_t *source, adm
|
||||
static void command_buildm3u (client_t *client, source_t *source, admin_format_t response);
|
||||
|
||||
static const admin_command_handler_t handlers[] = {
|
||||
{ "*", ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, NULL }, /* for ACL framework */
|
||||
{ FALLBACK_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_fallback },
|
||||
{ FALLBACK_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_fallback },
|
||||
{ METADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_metadata },
|
||||
{ METADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_metadata },
|
||||
{ SHOUTCAST_METADATA_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_shoutcast_metadata },
|
||||
{ LISTCLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_show_listeners },
|
||||
{ LISTCLIENTS_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_show_listeners },
|
||||
{ STATS_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats },
|
||||
{ STATS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats },
|
||||
{ "stats.xml", ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats },
|
||||
{ QUEUE_RELOAD_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_queue_reload },
|
||||
{ QUEUE_RELOAD_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_queue_reload },
|
||||
{ LISTMOUNTS_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts },
|
||||
{ LISTMOUNTS_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts },
|
||||
{ STREAMLIST_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts },
|
||||
{ STREAMLIST_PLAINTEXT_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_PLAINTEXT, command_list_mounts },
|
||||
{ STREAMLIST_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts },
|
||||
{ MOVECLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_move_clients },
|
||||
{ MOVECLIENTS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_move_clients },
|
||||
{ KILLCLIENT_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_client },
|
||||
{ KILLCLIENT_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_client },
|
||||
{ KILLSOURCE_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_source },
|
||||
{ KILLSOURCE_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_source },
|
||||
{ MANAGEAUTH_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_manageauth },
|
||||
{ MANAGEAUTH_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_manageauth },
|
||||
{ UPDATEMETADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_updatemetadata },
|
||||
{ UPDATEMETADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_updatemetadata },
|
||||
{ BUILDM3U_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_buildm3u },
|
||||
{ DEFAULT_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats },
|
||||
{ DEFAULT_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats }
|
||||
{ "*", ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, NULL, NULL}, /* for ACL framework */
|
||||
{ FALLBACK_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_fallback, NULL},
|
||||
{ FALLBACK_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_fallback, NULL},
|
||||
{ METADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_metadata, NULL},
|
||||
{ METADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_metadata, NULL},
|
||||
{ SHOUTCAST_METADATA_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_shoutcast_metadata, NULL},
|
||||
{ LISTCLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_show_listeners, NULL},
|
||||
{ LISTCLIENTS_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_show_listeners, NULL},
|
||||
{ STATS_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats, NULL},
|
||||
{ STATS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL},
|
||||
{ "stats.xml", ADMINTYPE_HYBRID, ADMIN_FORMAT_RAW, command_stats, NULL},
|
||||
{ QUEUE_RELOAD_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_queue_reload, NULL},
|
||||
{ QUEUE_RELOAD_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_queue_reload, NULL},
|
||||
{ LISTMOUNTS_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts, NULL},
|
||||
{ LISTMOUNTS_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts, NULL},
|
||||
{ STREAMLIST_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_list_mounts, NULL},
|
||||
{ STREAMLIST_PLAINTEXT_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_PLAINTEXT, command_list_mounts, NULL},
|
||||
{ STREAMLIST_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_list_mounts, NULL},
|
||||
{ MOVECLIENTS_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_move_clients, NULL},
|
||||
{ MOVECLIENTS_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_move_clients, NULL},
|
||||
{ KILLCLIENT_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_client, NULL},
|
||||
{ KILLCLIENT_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_client, NULL},
|
||||
{ KILLSOURCE_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_kill_source, NULL},
|
||||
{ KILLSOURCE_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_kill_source, NULL},
|
||||
{ MANAGEAUTH_RAW_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_RAW, command_manageauth, NULL},
|
||||
{ MANAGEAUTH_HTML_REQUEST, ADMINTYPE_GENERAL, ADMIN_FORMAT_HTML, command_manageauth, NULL},
|
||||
{ UPDATEMETADATA_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_updatemetadata, NULL},
|
||||
{ UPDATEMETADATA_HTML_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_HTML, command_updatemetadata, NULL},
|
||||
{ BUILDM3U_RAW_REQUEST, ADMINTYPE_MOUNT, ADMIN_FORMAT_RAW, command_buildm3u, NULL},
|
||||
{ DEFAULT_HTML_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL},
|
||||
{ DEFAULT_RAW_REQUEST, ADMINTYPE_HYBRID, ADMIN_FORMAT_HTML, command_stats, NULL}
|
||||
};
|
||||
|
||||
static admin_command_table_t command_tables[ADMIN_MAX_COMMAND_TABLES] = {
|
||||
@ -247,7 +247,7 @@ admin_command_id_t admin_get_command(const char *command)
|
||||
}
|
||||
|
||||
for (i = 0; i < table->length; i++)
|
||||
if (strcmp(table->handlers[i].route, suffix) == 0)
|
||||
if (resourcematch_match(table->handlers[i].route, suffix, NULL) == RESOURCEMATCH_MATCH)
|
||||
return admin_get_command_by_table_and_index(table, i);
|
||||
|
||||
return COMMAND_ERROR;
|
||||
@ -480,7 +480,7 @@ void admin_handle_request(client_t *client, const char *uri)
|
||||
handler = admin_get_handler(client->admin_command);
|
||||
|
||||
/* Check if admin command is valid */
|
||||
if (handler == NULL || handler->function == NULL) {
|
||||
if (handler == NULL || (handler->function == NULL && handler->function_with_parameters == NULL)) {
|
||||
ICECAST_LOG_ERROR("Error parsing command string or unrecognised command: %H",
|
||||
uri);
|
||||
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND);
|
||||
@ -545,7 +545,25 @@ void admin_handle_request(client_t *client, const char *uri)
|
||||
switch (client->parser->req_type) {
|
||||
case httpp_req_get:
|
||||
case httpp_req_post:
|
||||
handler->function(client, source, format);
|
||||
if (handler->function) {
|
||||
handler->function(client, source, format);
|
||||
} else {
|
||||
resourcematch_extract_t *extract = NULL;
|
||||
const char *suffix = strchr(uri, '/');
|
||||
|
||||
if (!suffix) {
|
||||
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND);
|
||||
} else {
|
||||
suffix++;
|
||||
|
||||
if (resourcematch_match(handler->route, suffix, &extract) == RESOURCEMATCH_MATCH) {
|
||||
handler->function_with_parameters(client, source, format, extract);
|
||||
resourcematch_extract_free(extract);
|
||||
} else {
|
||||
client_send_error_by_id(client, ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case httpp_req_options:
|
||||
client_send_204(client);
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "icecasttypes.h"
|
||||
#include "compat.h"
|
||||
#include "resourcematch.h"
|
||||
|
||||
/* types */
|
||||
#define ADMINTYPE_ERROR (-1)
|
||||
@ -31,12 +32,14 @@
|
||||
#define ADMIN_COMMAND_ANY ((admin_command_id_t)0) /* for ACL framework */
|
||||
|
||||
typedef void (*admin_request_function_ptr)(client_t * client, source_t * source, admin_format_t format);
|
||||
typedef void (*admin_request_function_with_parameters_ptr)(client_t * client, source_t * source, admin_format_t format, resourcematch_extract_t *parameters);
|
||||
|
||||
typedef struct admin_command_handler {
|
||||
const char *route;
|
||||
const int type;
|
||||
const int format;
|
||||
const admin_request_function_ptr function;
|
||||
const admin_request_function_with_parameters_ptr function_with_parameters;
|
||||
} admin_command_handler_t;
|
||||
|
||||
void admin_handle_request(client_t *client, const char *uri);
|
||||
|
191
src/resourcematch.c
Normal file
191
src/resourcematch.c
Normal file
@ -0,0 +1,191 @@
|
||||
/* 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 <lion@lion.leolix.org>,
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "resourcematch.h"
|
||||
|
||||
static size_t count_groups(const char *pattern)
|
||||
{
|
||||
size_t ret = 0;
|
||||
|
||||
while (*pattern) {
|
||||
for (; *pattern && *pattern != '%'; pattern++);
|
||||
|
||||
if (!*pattern) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
pattern++;
|
||||
|
||||
if (!*pattern)
|
||||
return ret;
|
||||
|
||||
if (*pattern != '%')
|
||||
ret++;
|
||||
|
||||
pattern++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static resourcematch_extract_t * allocate_extract(const char *pattern)
|
||||
{
|
||||
size_t groups = count_groups(pattern);
|
||||
resourcematch_extract_t *ret;
|
||||
|
||||
ret = calloc(1, sizeof(*ret));
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ret->groups = groups;
|
||||
ret->group = calloc(groups, sizeof(*ret->group));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void strip_common_prefix(const char **pattern, const char **string)
|
||||
{
|
||||
const char *p = *pattern;
|
||||
const char *s = *string;
|
||||
|
||||
for (; *p && *p != '%' && *p == *s; p++, s++);
|
||||
|
||||
*pattern = p;
|
||||
*string = s;
|
||||
}
|
||||
|
||||
static inline void setup_group(resourcematch_extract_t *extract, size_t idx, char type)
|
||||
{
|
||||
if (!extract)
|
||||
return;
|
||||
|
||||
extract->group[idx].type = type;
|
||||
extract->group[idx].raw = NULL;
|
||||
}
|
||||
|
||||
static inline resourcematch_result_t match_lli(const char **string, resourcematch_extract_t *extract, size_t idx, int base)
|
||||
{
|
||||
long long int ret;
|
||||
char *endptr;
|
||||
|
||||
errno = 0;
|
||||
ret = strtoll(*string, &endptr, base);
|
||||
if (errno != 0)
|
||||
return RESOURCEMATCH_ERROR;
|
||||
|
||||
if (extract) {
|
||||
extract->group[idx].result.lli = ret;
|
||||
}
|
||||
|
||||
*string = endptr;
|
||||
|
||||
return RESOURCEMATCH_MATCH;
|
||||
}
|
||||
|
||||
resourcematch_result_t resourcematch_match(const char *pattern, const char *string, resourcematch_extract_t **extract)
|
||||
{
|
||||
resourcematch_result_t ret;
|
||||
resourcematch_extract_t *matches = NULL;
|
||||
size_t idx = 0;
|
||||
|
||||
if (!pattern || !string)
|
||||
return RESOURCEMATCH_ERROR;
|
||||
|
||||
if (extract) {
|
||||
matches = allocate_extract(pattern);
|
||||
if (!matches)
|
||||
return RESOURCEMATCH_NOMATCH;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
strip_common_prefix(&pattern, &string);
|
||||
|
||||
if (!*pattern && !*string) {
|
||||
if (extract)
|
||||
*extract = matches;
|
||||
|
||||
return RESOURCEMATCH_MATCH;
|
||||
} else if (!*pattern || !*string) {
|
||||
if (extract)
|
||||
resourcematch_extract_free(matches);
|
||||
|
||||
return RESOURCEMATCH_NOMATCH;
|
||||
}
|
||||
|
||||
if (*pattern != '%') {
|
||||
if (extract)
|
||||
resourcematch_extract_free(matches);
|
||||
|
||||
return RESOURCEMATCH_NOMATCH;
|
||||
}
|
||||
|
||||
pattern++;
|
||||
|
||||
switch (*pattern) {
|
||||
case '%':
|
||||
if (*string == '%') {
|
||||
string++;
|
||||
} else {
|
||||
if (extract)
|
||||
resourcematch_extract_free(matches);
|
||||
|
||||
return RESOURCEMATCH_NOMATCH;
|
||||
}
|
||||
break;
|
||||
#define _test_int(type,base) \
|
||||
case (type): \
|
||||
setup_group(matches, idx, *pattern); \
|
||||
\
|
||||
ret = match_lli(&string, matches, idx, (base)); \
|
||||
if (ret != RESOURCEMATCH_MATCH) { \
|
||||
if (extract) \
|
||||
resourcematch_extract_free(matches); \
|
||||
\
|
||||
return ret; \
|
||||
} \
|
||||
idx++; \
|
||||
break;
|
||||
|
||||
_test_int('i', 0);
|
||||
_test_int('d', 10);
|
||||
_test_int('x', 16);
|
||||
_test_int('o', 8);
|
||||
|
||||
default:
|
||||
if (extract)
|
||||
resourcematch_extract_free(matches);
|
||||
|
||||
return RESOURCEMATCH_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
pattern++;
|
||||
}
|
||||
}
|
||||
|
||||
void resourcematch_extract_free(resourcematch_extract_t *extract)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!extract)
|
||||
return;
|
||||
|
||||
for (i = 0; i < extract->groups; i++) {
|
||||
free(extract->group[i].raw);
|
||||
}
|
||||
|
||||
free(extract->group);
|
||||
free(extract);
|
||||
}
|
37
src/resourcematch.h
Normal file
37
src/resourcematch.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* 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 <lion@lion.leolix.org>,
|
||||
*/
|
||||
|
||||
#ifndef __RESOURCEMATCH_H__
|
||||
#define __RESOURCEMATCH_H__
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef enum {
|
||||
RESOURCEMATCH_ERROR,
|
||||
RESOURCEMATCH_MATCH,
|
||||
RESOURCEMATCH_NOMATCH
|
||||
} resourcematch_result_t;
|
||||
|
||||
typedef struct {
|
||||
char type;
|
||||
char *raw;
|
||||
union {
|
||||
const char *string;
|
||||
long long int lli;
|
||||
} result;
|
||||
} resourcematch_group_t;
|
||||
|
||||
typedef struct {
|
||||
size_t groups;
|
||||
resourcematch_group_t *group;
|
||||
} resourcematch_extract_t;
|
||||
|
||||
resourcematch_result_t resourcematch_match(const char *pattern, const char *string, resourcematch_extract_t **extract);
|
||||
void resourcematch_extract_free(resourcematch_extract_t *extract);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user