diff --git a/src/Makefile.am b/src/Makefile.am index d7548649..2ee0f0af 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -41,6 +41,7 @@ noinst_HEADERS = \ reportxml_helper.h \ json.h \ xml2json.h \ + valuefile.h \ string_renderer.h \ listensocket.h \ fastevent.h \ @@ -96,6 +97,7 @@ icecast_SOURCES = \ reportxml_helper.c \ json.c \ xml2json.c \ + valuefile.c \ string_renderer.c \ listensocket.c \ fastevent.c \ diff --git a/src/errors.c b/src/errors.c index 41dd64b8..8fbb5026 100644 --- a/src/errors.c +++ b/src/errors.c @@ -16,132 +16,134 @@ #include "logging.h" #define CATMODULE "errors" +#define BASE_DEF(x) .id = (x), .symbol = # x + // cut -d' ' -f2 x | while read x; do printf " {.id = %-60s .http_status = xxx,\n .message = \"\"},\n" "$x",; done static const icecast_error_t __errors[] = { - {.id = ICECAST_ERROR_ADMIN_DEST_NOT_RUNNING, .http_status = 400, + {BASE_DEF(ICECAST_ERROR_ADMIN_DEST_NOT_RUNNING), .http_status = 400, .uuid = "52735a81-16fe-4d7e-9984-5aed8a941055", .message = "Destination not running"}, - {.id = ICECAST_ERROR_ADMIN_METADAT_BADCALL, .http_status = 400, + {BASE_DEF(ICECAST_ERROR_ADMIN_METADAT_BADCALL), .http_status = 400, .uuid = "85d33e67-5c4e-4511-b4fa-3ca69ccd03de", .message = "illegal metadata call"}, - {.id = ICECAST_ERROR_ADMIN_METADAT_NO_SUCH_ACTION, .http_status = 501, + {BASE_DEF(ICECAST_ERROR_ADMIN_METADAT_NO_SUCH_ACTION), .http_status = 501, .uuid = "14f4d814-98d9-468c-8a0b-ba5e74c9d771", .message = "No such action"}, - {.id = ICECAST_ERROR_ADMIN_MISSING_PARAMETER, .http_status = 400, + {BASE_DEF(ICECAST_ERROR_ADMIN_MISSING_PARAMETER), .http_status = 400, .uuid = "cb11dc71-6149-454c-8d4e-47a3af26b03a", .message = "Missing parameter"}, - {.id = ICECAST_ERROR_ADMIN_missing_parameter, .http_status = 400, + {BASE_DEF(ICECAST_ERROR_ADMIN_missing_parameter), .http_status = 400, .uuid = "8be9ef0a-2b32-450c-aec9-a414ca0c074c", .message = "missing parameter"}, - {.id = ICECAST_ERROR_ADMIN_MOUNT_NOT_ACCEPT_URL_UPDATES, .http_status = 422, + {BASE_DEF(ICECAST_ERROR_ADMIN_MOUNT_NOT_ACCEPT_URL_UPDATES), .http_status = 422, .uuid = "3bed51bb-a10f-4af3-9965-4e67181de7d6", .message = "mountpoint will not accept URL updates"}, - {.id = ICECAST_ERROR_ADMIN_NO_SUCH_DESTINATION, .http_status = 404, + {BASE_DEF(ICECAST_ERROR_ADMIN_NO_SUCH_DESTINATION), .http_status = 404, .uuid = "c5f1ee06-46a0-4697-9f01-6e9fc333d555", .message = "No such destination"}, - {.id = ICECAST_ERROR_ADMIN_ROLEMGN_ADD_NOSYS, .http_status = 501, + {BASE_DEF(ICECAST_ERROR_ADMIN_ROLEMGN_ADD_NOSYS), .http_status = 501, .uuid = "7e1a8426-2ae1-4a6b-bfd9-59d8f8153021", .message = "Adding users to role not supported by role"}, - {.id = ICECAST_ERROR_ADMIN_ROLEMGN_DELETE_NOSYS, .http_status = 501, + {BASE_DEF(ICECAST_ERROR_ADMIN_ROLEMGN_DELETE_NOSYS), .http_status = 501, .uuid = "367fbad1-389e-4292-bba8-c97984e616cc", .message = "Deleting users from role not supported by role"}, - {.id = ICECAST_ERROR_ADMIN_ROLEMGN_ROLE_NOT_FOUND, .http_status = 404, + {BASE_DEF(ICECAST_ERROR_ADMIN_ROLEMGN_ROLE_NOT_FOUND), .http_status = 404, .uuid = "59fe9c81-8c34-49ff-800f-7ec42ea498be", .message = "Role not found"}, - {.id = ICECAST_ERROR_ADMIN_SOURCE_DOES_NOT_EXIST, .http_status = 404, + {BASE_DEF(ICECAST_ERROR_ADMIN_SOURCE_DOES_NOT_EXIST), .http_status = 404, .uuid = "2f51a026-02e4-4fe4-bf9d-cc16557b3b65", .message = "Source does not exist"}, - {.id = ICECAST_ERROR_ADMIN_SOURCE_IS_NOT_AVAILABLE, .http_status = 400, + {BASE_DEF(ICECAST_ERROR_ADMIN_SOURCE_IS_NOT_AVAILABLE), .http_status = 400, .uuid = "00b9d977-f41d-455f-820f-6d457dffb246", .message = "Source is not available"}, - {.id = ICECAST_ERROR_ADMIN_SUPPLIED_MOUNTPOINTS_ARE_IDENTICAL, .http_status = 400, + {BASE_DEF(ICECAST_ERROR_ADMIN_SUPPLIED_MOUNTPOINTS_ARE_IDENTICAL), .http_status = 400, .uuid = "4be9a010-7a3f-44e4-b74d-3c6d9c4f7236", .message = "supplied mountpoints are identical"}, - {.id = ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND, .http_status = 400, + {BASE_DEF(ICECAST_ERROR_ADMIN_UNRECOGNISED_COMMAND), .http_status = 400, .uuid = "811bddac-5be5-4580-9cde-7b849e66dfe5", .message = "unrecognised command"}, - {.id = ICECAST_ERROR_AUTH_BUSY, .http_status = 503, + {BASE_DEF(ICECAST_ERROR_AUTH_BUSY), .http_status = 503, .uuid = "26708754-8f98-4191-81d1-7fb7246200d6", .message = "busy, please try again later"}, - {.id = ICECAST_ERROR_CON_CONTENT_TYPE_NOSYS, .http_status = 415, + {BASE_DEF(ICECAST_ERROR_CON_CONTENT_TYPE_NOSYS), .http_status = 415, .uuid = "f684ad3c-513b-4d87-9a66-424788bc6adb", .message = "Content-type not supported"}, - {.id = ICECAST_ERROR_CON_INTERNAL_FORMAT_ALLOC_ERROR, .http_status = 500, + {BASE_DEF(ICECAST_ERROR_CON_INTERNAL_FORMAT_ALLOC_ERROR), .http_status = 500, .uuid = "47a4b11b-5d2a-46e2-8948-942e7b0af3e6", .message = "internal format allocation problem"}, - {.id = ICECAST_ERROR_CON_MISSING_PASS_PARAMETER, .http_status = 400 /* XXX */, + {BASE_DEF(ICECAST_ERROR_CON_MISSING_PASS_PARAMETER), .http_status = 400 /* XXX */, .uuid = "b59c3a05-e2b1-4a14-8798-bbe1ae46603b", .message = "missing pass parameter"}, - {.id = ICECAST_ERROR_CON_MOUNT_IN_USE, .http_status = 409, + {BASE_DEF(ICECAST_ERROR_CON_MOUNT_IN_USE), .http_status = 409, .uuid = "c5724467-5f85-48c7-b45a-915c3150c292", .message = "Mountpoint in use"}, - {.id = ICECAST_ERROR_CON_MOUNTPOINT_NOT_STARTING_WITH_SLASH, .http_status = 400, + {BASE_DEF(ICECAST_ERROR_CON_MOUNTPOINT_NOT_STARTING_WITH_SLASH), .http_status = 400, .uuid = "1ae45ead-40fc-4de2-b56f-e54d3247f2ee", .message = "source mountpoint not starting with /"}, - {.id = ICECAST_ERROR_CON_NO_CONTENT_TYPE_GIVEN, .http_status = 400, + {BASE_DEF(ICECAST_ERROR_CON_NO_CONTENT_TYPE_GIVEN), .http_status = 400, .uuid = "2cd86778-ac30-49e7-a108-26d627a7923b", .message = "No Content-type given"}, - {.id = ICECAST_ERROR_CON_PER_CRED_CLIENT_LIMIT, .http_status = 429, + {BASE_DEF(ICECAST_ERROR_CON_PER_CRED_CLIENT_LIMIT), .http_status = 429, .uuid = "9c72c1ec-f638-4d33-a077-6acbbff25317", .message = "Reached limit of concurrent connections on those credentials"}, - {.id = ICECAST_ERROR_CON_SOURCE_CLIENT_LIMIT, .http_status = 503, + {BASE_DEF(ICECAST_ERROR_CON_SOURCE_CLIENT_LIMIT), .http_status = 503, .uuid = "c770182d-c854-422a-a8e5-7142689234a3", .message = "too many sources connected"}, - {.id = ICECAST_ERROR_CON_UNIMPLEMENTED, .http_status = 501, + {BASE_DEF(ICECAST_ERROR_CON_UNIMPLEMENTED), .http_status = 501, .uuid = "58ce6cb4-72b4-49da-8ad2-feaf775bc61e", .message = "Unimplemented"}, - {.id = ICECAST_ERROR_CON_UNKNOWN_REQUEST, .http_status = 405, + {BASE_DEF(ICECAST_ERROR_CON_UNKNOWN_REQUEST), .http_status = 405, .uuid = "78f590cc-8812-40d5-a4ef-17344ab75b35", .message = "unknown request"}, - {.id = ICECAST_ERROR_CON_UPGRADE_ERROR, .http_status = 400 /* XXX */, + {BASE_DEF(ICECAST_ERROR_CON_UPGRADE_ERROR), .http_status = 400 /* XXX */, .uuid = "ec16f654-f262-415f-ab91-95703ae33704", .message = "Can not upgrade protocol"}, - {.id = ICECAST_ERROR_CON_MOUNT_NO_FOR_DIRECT_ACCESS, .http_status = 400 /* XXX */, + {BASE_DEF(ICECAST_ERROR_CON_MOUNT_NO_FOR_DIRECT_ACCESS), .http_status = 400 /* XXX */, .uuid = "652548c6-2a7d-4c73-a1c5-e53759032bd1", .message = "Mountpoint is not available for direct access"}, - {.id = ICECAST_ERROR_FSERV_FILE_NOT_FOUND, .http_status = 404, + {BASE_DEF(ICECAST_ERROR_FSERV_FILE_NOT_FOUND), .http_status = 404, .uuid = "18c32b43-0d8e-469d-b434-10133cdd06ad", .message = "The file you requested could not be found"}, - {.id = ICECAST_ERROR_FSERV_FILE_NOT_READABLE, .http_status = 404 /* XXX */, + {BASE_DEF(ICECAST_ERROR_FSERV_FILE_NOT_READABLE), .http_status = 404 /* XXX */, .uuid = "c883d55d-fb41-4f4c-8800-563f5542f51d", .message = "File not readable"}, - {.id = ICECAST_ERROR_FSERV_REQUEST_RANGE_NOT_SATISFIABLE, .http_status = 416, + {BASE_DEF(ICECAST_ERROR_FSERV_REQUEST_RANGE_NOT_SATISFIABLE), .http_status = 416, .uuid = "5874cc51-770b-42b5-82d2-737b2b406b30", .message = "Request Range Not Satisfiable"}, - {.id = ICECAST_ERROR_GEN_BUFFER_REALLOC, .http_status = 500, + {BASE_DEF(ICECAST_ERROR_GEN_BUFFER_REALLOC), .http_status = 500, .uuid = "cda8203e-f237-4090-8d43-544efdd6295c", .message = "Buffer reallocation failed."}, - {.id = ICECAST_ERROR_GEN_CLIENT_LIMIT, .http_status = 503, + {BASE_DEF(ICECAST_ERROR_GEN_CLIENT_LIMIT), .http_status = 503, .uuid = "87fd3e61-6702-4473-b506-f616d27a142f", .message = "Icecast connection limit reached"}, - {.id = ICECAST_ERROR_GEN_CLIENT_NEEDS_TO_AUTHENTICATE, .http_status = 401, + {BASE_DEF(ICECAST_ERROR_GEN_CLIENT_NEEDS_TO_AUTHENTICATE), .http_status = 401, .uuid = "25387198-0643-4577-9139-7c4f24f59d4a", .message = "You need to authenticate"}, - {.id = ICECAST_ERROR_GEN_HEADER_GEN_FAILED, .http_status = 500, + {BASE_DEF(ICECAST_ERROR_GEN_HEADER_GEN_FAILED), .http_status = 500, .uuid = "a8b3c3fe-cb87-45fe-9a9d-ee4c2075d43a", .message = "Header generation failed."}, - {.id = ICECAST_ERROR_GEN_MEMORY_EXHAUSTED, .http_status = 503, + {BASE_DEF(ICECAST_ERROR_GEN_MEMORY_EXHAUSTED), .http_status = 503, .uuid = "18411e73-713e-4910-b7e4-52a2e324b4e0", .message = "memory exhausted"}, - {.id = ICECAST_ERROR_GEN_SAFE_METHOD_ON_UNSAFE_CALL, .http_status = 405, + {BASE_DEF(ICECAST_ERROR_GEN_SAFE_METHOD_ON_UNSAFE_CALL), .http_status = 405, .uuid = "6f4c95e3-b446-4814-b4b8-0cb585dbe4bd", .message = "Safe HTTP method used on unsafe call"}, - {.id = ICECAST_ERROR_SOURCE_MOUNT_UNAVAILABLE, .http_status = 404 /* XXX */, + {BASE_DEF(ICECAST_ERROR_SOURCE_MOUNT_UNAVAILABLE), .http_status = 404 /* XXX */, .uuid = "88d06875-fcf2-4417-84af-05866c97745c", .message = "Mount unavailable"}, - {.id = ICECAST_ERROR_SOURCE_STREAM_PREPARATION_ERROR, .http_status = 500 /* XXX */, + {BASE_DEF(ICECAST_ERROR_SOURCE_STREAM_PREPARATION_ERROR), .http_status = 500 /* XXX */, .uuid = "9e50d94d-f03d-4515-8216-577bf8e9f70d", .message = "Stream preparation error"}, - {.id = ICECAST_ERROR_SOURCE_MAX_LISTENERS, .http_status = 503, + {BASE_DEF(ICECAST_ERROR_SOURCE_MAX_LISTENERS), .http_status = 503, .uuid = "df147168-baaa-4959-82a4-746a1232927d", .message = "Maximum listeners reached for this source"}, - {.id = ICECAST_ERROR_XSLT_PARSE, .http_status = 404 /* XXX */, + {BASE_DEF(ICECAST_ERROR_XSLT_PARSE), .http_status = 404 /* XXX */, .uuid = "f86b5b28-c1f8-49f6-a4cd-a18e2a6a44fd", .message = "Could not parse XSLT file"}, - {.id = ICECAST_ERROR_XSLT_problem, .http_status = 500, + {BASE_DEF(ICECAST_ERROR_XSLT_problem), .http_status = 500, .uuid = "d3c6e4b3-7d6e-4191-a81b-970273067ae3", .message = "XSLT problem"}, - {.id = ICECAST_ERROR_RECURSIVE_ERROR, .http_status = 500, + {BASE_DEF(ICECAST_ERROR_RECURSIVE_ERROR), .http_status = 500, .uuid = "13489d5c-eae6-4bf3-889e-ec1fa9a9b9ac", .message = "Recursive error"} }; @@ -170,3 +172,10 @@ const icecast_error_t * error_get_by_uuid(const char *uuid) return NULL; } + +const icecast_error_t * error_get_by_index(size_t idx) { + if (idx < (sizeof(__errors)/sizeof(*__errors))) { + return &(__errors[idx]); + } + return NULL; +} diff --git a/src/errors.h b/src/errors.h index 0b836772..80e215f0 100644 --- a/src/errors.h +++ b/src/errors.h @@ -59,11 +59,13 @@ struct icecast_error_tag { const int http_status; const char *uuid; const char *message; + const char *symbol; }; typedef struct icecast_error_tag icecast_error_t; const icecast_error_t * error_get_by_id(icecast_error_id_t id); const icecast_error_t * error_get_by_uuid(const char *uuid); +const icecast_error_t * error_get_by_index(size_t idx); #endif /* __ERRORS_H__ */ diff --git a/src/main.c b/src/main.c index b6f146c0..e021b3fd 100644 --- a/src/main.c +++ b/src/main.c @@ -45,6 +45,7 @@ #include #endif +#include "icecasttypes.h" #include #include "common/thread/thread.h" @@ -93,6 +94,8 @@ #include "prng.h" #include "geoip.h" #include "navigation.h" +#include "valuefile.h" +#include "string_renderer.h" #include @@ -261,6 +264,13 @@ static void show_version(bool full) } } +static void export_database(void) +{ + string_renderer_t * db = valuefile_export_database(); + fputs(string_renderer_to_string_zero_copy(db), stdout); + igloo_ro_unref(&db); +} + static bool _parse_config_opts(int argc, char **argv, char *filename, size_t size) { int i; @@ -302,6 +312,9 @@ static bool _parse_config_opts(int argc, char **argv, char *filename, size_t siz } else if (strcmp(opt, "-V") == 0) { show_version(true); exit(0); + } else if (strcmp(opt, "--export-database") == 0) { + export_database(); + exit(0); } else if (strcmp(opt, "-c") == 0) { if ((i + 1) < argc) { strncpy(filename, argv[++i], size-1); diff --git a/src/valuefile.c b/src/valuefile.c new file mode 100644 index 00000000..fcd4e3f3 --- /dev/null +++ b/src/valuefile.c @@ -0,0 +1,210 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2024 , Philipp "ph3-der-loewe" Schafft , + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "valuefile.h" +#include "string_renderer.h" +#include "global.h" +#include "errors.h" +#include "logging.h" +#define CATMODULE "valuefile" + +// universal: +#define WK_ASI "ddd60c5c-2934-404f-8f2d-fcb4da88b633" +#define WK_TAGNAME "bfae7574-3dae-425d-89b1-9c087c140c23" +#define WK_ENGLISH "c50134ca-0a32-5c5c-833c-2686043c0b3f" +#define WK_GB_TEXT "0678cfe2-1386-43db-86a9-6c491b2df64c" +#define WK_RELATES_TO "079ab791-784e-4bb9-ae2d-07e01710a60c" +#define WK_SEE_ALSO "a75f9010-9db3-4d78-bd78-0dd528d6b55d" +#define WK_DEFAULT_TYPE "87c4892f-ae39-476e-8ed0-d9ed321dafe9" +#define WK_UNSIGNED_INT "dea3782c-6bcb-4ce9-8a39-f8dab399d75d" + +// Icecast: +#define WK_EQ_HTTP "06cfbaf9-01fe-4d82-8075-0389231c46b3" +#define WK_STATE_info "c2bb1328-87d6-49ad-b7df-0b381a2e606d" +#define WK_STATE_error "e0faf79c-bddf-42d7-a237-975aca861a0b" +#define WK_STATE_warning "107c0e91-1fd8-4bbc-a54c-899cbb3d9bc6" + +static const char *states[] = {WK_STATE_info, WK_STATE_error, WK_STATE_warning}; + +static const struct { + const char *uuid; + const char *symbol; + const char *tagname; + const char *text; + const char *state; + const unsigned int http_status; +} extra[] = { + {.uuid = "be7fac90-54fb-4673-9e0d-d15d6a4963a2", .symbol = "AUTH_ALTER_REDIRECT_SEE_OTHER", .http_status = 303}, + {.uuid = "4b08a03a-ecce-4981-badf-26b0bb6c9d9c", .symbol = "AUTH_ALTER_REDIRECT_TEMPORARY", .http_status = 307}, + {.uuid = "36bf6815-95cb-4cc8-a7b0-6b4b0c82ac5d", .symbol = "AUTH_ALTER_REDIRECT_PERMANENT", .http_status = 308}, + {.uuid = WK_EQ_HTTP, .tagname = "equivalent-http-status"}, + {.uuid = WK_STATE_info, .tagname = "info"}, + {.uuid = WK_STATE_error, .tagname = "error"}, + {.uuid = WK_STATE_warning, .tagname = "warning"}, + // grep __reportxml_add_maintenance.\*\" admin.c | sed 's/^.*__reportxml_add_maintenance([^,]*,[^,]*, *"\([^"]*\)", *"\([^"]*\)", *"\([^"]*\)", NULL);.*$/{.uuid = "\1", .state = WK_STATE_\2, .text = "\3"},/' + {.uuid = "a93a842a-9664-43a9-b707-7f358066fe2b", .state = WK_STATE_error, .text = "Global client, and source limit is bigger than suitable for current open file limit."}, + {.uuid = "fdbacc56-1150-4c79-b53a-43cc79046f40", .state = WK_STATE_info, .text = "Core files are disabled."}, + {.uuid = "688bdebc-c190-4b5d-8764-3a050c48387a", .state = WK_STATE_info, .text = "Core files are enabled."}, + {.uuid = "c704804e-d3b9-4544-898b-d477078135de", .state = WK_STATE_warning, .text = "Developer logging is active. This mode is not for production."}, + {.uuid = "f90219e1-bd07-4b54-b1ee-0ba6a0289a15", .state = WK_STATE_error, .text = "IPv6 not enabled."}, + {.uuid = "709ab43b-251d-49a5-a4fe-c749eaabf17c", .state = WK_STATE_info, .text = "IPv4-mapped IPv6 is available on this system."}, + {.uuid = "c4f25c51-2720-4b38-a806-19ef024b5289", .state = WK_STATE_warning, .text = "Hostname is not set to anything useful in ."}, + {.uuid = "8defae31-a52e-4bba-b904-76db5362860f", .state = WK_STATE_warning, .text = "No useful location is given in ."}, + {.uuid = "cf86d88e-dc20-4359-b446-110e7065d17a", .state = WK_STATE_warning, .text = "No admin contact given in . YP directory support is disabled."}, + {.uuid = "e2ba5a8b-4e4f-41ca-b455-68ae5fb6cae0", .state = WK_STATE_error, .text = "No PRNG seed configured. PRNG is insecure."}, + {.uuid = "6620ef7b-46ef-4781-9a5e-8ee7f0f9d44e", .state = WK_STATE_error, .text = "Unknown tags are used in the config file. See the error.log for details."}, + {.uuid = "b6224fc4-53a1-433f-a6cd-d5b85c60f1c9", .state = WK_STATE_error, .text = "Obsolete tags are used in the config file. See the error.log for details and update your configuration accordingly."}, + {.uuid = "0f6f757d-52d8-4b9a-8e57-9bcd528fffba", .state = WK_STATE_error, .text = "Invalid tags are used in the config file. See the error.log for details and update your configuration accordingly."}, + {.uuid = "8fc33086-274d-4ccb-b32f-599b3fa0f41a", .state = WK_STATE_error, .text = "The configuration did not validate. See the error.log for details and update your configuration accordingly."}, + {.uuid = "6830cbf7-cd68-4c0c-ab5a-81499c70fd34", .state = WK_STATE_info, .text = "chroot configured and active."}, + {.uuid = "2d584a76-e67c-4268-b7e8-139b0b9b1131", .state = WK_STATE_error, .text = "chroot configured but failed."}, + {.uuid = "1a3fea5c-3352-4cb5-85cc-51ab9bd6ea83", .state = WK_STATE_error, .text = "chroot configured but not supported by operating system."}, + {.uuid = "bab05e81-fd03-4773-9fc5-c4609883a5e3", .state = WK_STATE_info, .text = "Change of UID/GID configured and active."}, + {.uuid = "4f856dd4-7aac-44b4-95b5-b6798f547603", .state = WK_STATE_error, .text = "Change of UID/GID configured but failed."}, + {.uuid = "afcaa756-b91c-4496-a9e2-44400a18789c", .state = WK_STATE_error, .text = "Change of UID/GID configured but not supported by operating system."}, + {.uuid = "f68dd8a3-22b1-4118-aba6-b039f2c5b51e", .state = WK_STATE_info, .text = "Currently no sources are connected to this server."}, + {.uuid = "a3a51986-3bba-42b9-ad5c-d9ecc9967320", .state = WK_STATE_warning, .text = "Legacy sources are connected. See mount list for details."}, + {.uuid = "08676614-50b4-4ea7-ba99-7c2ffcecf705", .state = WK_STATE_warning, .text = "More than 90% of the server's configured maximum clients are connected"}, + {.uuid = "417ae59c-de19-4ed1-ade1-429c689f1152", .state = WK_STATE_info, .text = "More than 75% of the server's configured maximum clients are connected"}, + {.uuid = "dc91ce96-f473-41d1-bfff-379666306911", .state = WK_STATE_info, .text = "Environment is noisy."}, + {.uuid = "40d134e3-fbbe-46b1-a409-9b2ca8954528", .state = WK_STATE_warning, .text = "No secure password hash support detected."}, + {NULL} +}; + +static igloo_error_t valuefile_value(string_renderer_t *renderer, const char *value, bool first) +{ + if (!first) + string_renderer_add_string(renderer, " "); + + if (!value) { + return string_renderer_add_string(renderer, "!null"); + } else if (!*value) { + return string_renderer_add_string(renderer, "!empty"); + } + + return string_renderer_add_string_with_options(renderer, value, false, STRING_RENDERER_ENCODING_URI); +} + +static igloo_error_t valuefile_line_metadata(string_renderer_t *renderer, const char *tag, const char *relation, const char *context, const char *type, const char *encoding, const char *value) +{ + string_renderer_add_string(renderer, "tag-metadata "); + string_renderer_add_string(renderer, tag); + string_renderer_add_string(renderer, " "); + string_renderer_add_string(renderer, relation); + valuefile_value(renderer, context, false); + valuefile_value(renderer, type, false); + valuefile_value(renderer, encoding, false); + valuefile_value(renderer, value, false); + string_renderer_add_string(renderer, "\n"); + + return igloo_ERROR_NONE; +} + +static igloo_error_t valuefile_line_relation(string_renderer_t *renderer, const char *tag, const char *relation, const char *related, const char *context, const char *filter) +{ + string_renderer_add_string(renderer, "tag-relation "); + string_renderer_add_string(renderer, tag); + string_renderer_add_string(renderer, " "); + string_renderer_add_string(renderer, relation); + string_renderer_add_string(renderer, " "); + string_renderer_add_string(renderer, related); + valuefile_value(renderer, context, false); + valuefile_value(renderer, filter, false); + string_renderer_add_string(renderer, "\n"); + + return igloo_ERROR_NONE; +} + +static igloo_error_t valuefile_line_tag_ise(string_renderer_t *renderer, const char *ise, const char *comment) +{ + string_renderer_add_string(renderer, "tag-ise "); + string_renderer_add_string(renderer, ise); + if (comment) { + string_renderer_add_string(renderer, " # "); + string_renderer_add_string(renderer, comment); + } + string_renderer_add_string(renderer, "\n"); + + return igloo_ERROR_NONE; +} + +static igloo_error_t valuefile_http_status(string_renderer_t *renderer, const char *tag, unsigned int http_status) +{ + if (http_status < 100 || http_status > 599) + return igloo_ERROR_NONE; + + string_renderer_add_string(renderer, "tag-metadata "); + string_renderer_add_string(renderer, tag); + string_renderer_add_string(renderer, " "); + string_renderer_add_string(renderer, WK_EQ_HTTP); + string_renderer_add_string(renderer, " !null !null !null "); + string_renderer_add_int(renderer, http_status); + string_renderer_add_string(renderer, "\n"); + + return igloo_ERROR_NONE; +} + +string_renderer_t * valuefile_export_database(void) +{ + string_renderer_t *renderer; + + if (igloo_ro_new(&renderer, string_renderer_t, igloo_instance) != igloo_ERROR_NONE) + return NULL; + + string_renderer_add_string(renderer, "!!ValueFile 54bf8af4-b1d7-44da-af48-5278d11e8f32 e5da6a39-46d5-48a9-b174-5c26008e208e\n"); + string_renderer_add_string(renderer, "!!Feature f06c2226-b33e-48f2-9085-cd906a3dcee0 # tagpool-source-format-modern-limited\n"); + string_renderer_add_string(renderer, "\n"); + + // errors: + string_renderer_add_string(renderer, "# Errors:\n"); + for (size_t i = 0; ; i++) { + const icecast_error_t * error = error_get_by_index(i); + if (!error) + break; + valuefile_line_tag_ise(renderer, error->uuid, error->message); + valuefile_line_metadata(renderer, error->uuid, WK_ASI, NULL, WK_TAGNAME, NULL, error->symbol); + valuefile_line_metadata(renderer, error->uuid, WK_GB_TEXT, WK_ENGLISH, NULL, NULL, error->message); + valuefile_http_status(renderer, error->uuid, error->http_status); + } + string_renderer_add_string(renderer, "\n"); + + string_renderer_add_string(renderer, "# Extra:\n"); + for (size_t i = 0; extra[i].uuid; i++) { + valuefile_line_tag_ise(renderer, extra[i].uuid, extra[i].symbol); + if (extra[i].symbol) + valuefile_line_metadata(renderer, extra[i].uuid, WK_ASI, NULL, WK_TAGNAME, NULL, extra[i].symbol); + if (extra[i].tagname) + valuefile_line_metadata(renderer, extra[i].uuid, WK_ASI, NULL, WK_TAGNAME, NULL, extra[i].tagname); + if (extra[i].text) + valuefile_line_metadata(renderer, extra[i].uuid, WK_GB_TEXT, WK_ENGLISH, NULL, NULL, extra[i].text); + if (extra[i].state) + valuefile_line_relation(renderer, extra[i].uuid, WK_RELATES_TO, extra[i].state, NULL, NULL); + + valuefile_http_status(renderer, extra[i].uuid, extra[i].http_status); + } + string_renderer_add_string(renderer, "\n"); + + string_renderer_add_string(renderer, "# Special:\n"); + valuefile_line_relation(renderer, WK_EQ_HTTP, WK_DEFAULT_TYPE, WK_UNSIGNED_INT, NULL, NULL); + for (size_t i = 0; i < (sizeof(states)/sizeof(*states)); i++) { + for (size_t j = 0; j < (sizeof(states)/sizeof(*states)); j++) { + if (i != j) + valuefile_line_relation(renderer, states[i], WK_SEE_ALSO, states[j], NULL, NULL); + } + } + string_renderer_add_string(renderer, "\n"); + + return renderer; +} diff --git a/src/valuefile.h b/src/valuefile.h new file mode 100644 index 00000000..1a59ba7c --- /dev/null +++ b/src/valuefile.h @@ -0,0 +1,18 @@ +/* Icecast + * + * This program is distributed under the GNU General Public License, version 2. + * A copy of this license is included with this source. + * + * Copyright 2024 , Philipp "ph3-der-loewe" Schafft , + */ + +#ifndef __VALUEFILE_H__ +#define __VALUEFILE_H__ + +#include + +#include "icecasttypes.h" + +string_renderer_t * valuefile_export_database(void); + +#endif