From 3d132ce528ff9e03a5ddd3c9308aeb8f5e2cb84f Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Tue, 19 Dec 2023 19:21:21 +0200 Subject: [PATCH 1/6] Correct source location in doxy configs --- doc/Doxyfile.in | 5 ++--- doc/Doxyfile_full.in | 5 ++--- doc/Doxyfile_xml.in | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index a876c148f3..5e46206528 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -152,7 +152,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@/gsad +STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -753,7 +753,7 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = @CMAKE_SOURCE_DIR@/gsad/src +INPUT = @CMAKE_SOURCE_DIR@/src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -822,7 +822,6 @@ EXCLUDE_SYMBOLS = # command). EXAMPLE_PATH = @CMAKE_SOURCE_DIR@ \ - @CMAKE_SOURCE_DIR@/gsad \ @CMAKE_CURRENT_SOURCE_DIR@ # If the value of the EXAMPLE_PATH tag contains directories, you can use the diff --git a/doc/Doxyfile_full.in b/doc/Doxyfile_full.in index 5736feeb51..a94f92b57d 100644 --- a/doc/Doxyfile_full.in +++ b/doc/Doxyfile_full.in @@ -152,7 +152,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@/gsad +STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -753,7 +753,7 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = @CMAKE_SOURCE_DIR@/gsad/src +INPUT = @CMAKE_SOURCE_DIR@/src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -822,7 +822,6 @@ EXCLUDE_SYMBOLS = # command). EXAMPLE_PATH = @CMAKE_SOURCE_DIR@ \ - @CMAKE_SOURCE_DIR@/gsad \ @CMAKE_CURRENT_SOURCE_DIR@ # If the value of the EXAMPLE_PATH tag contains directories, you can use the diff --git a/doc/Doxyfile_xml.in b/doc/Doxyfile_xml.in index a4913783e3..bdf9c51908 100644 --- a/doc/Doxyfile_xml.in +++ b/doc/Doxyfile_xml.in @@ -152,7 +152,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@/gsad +STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -753,7 +753,7 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = @CMAKE_SOURCE_DIR@/gsad/src +INPUT = @CMAKE_SOURCE_DIR@/src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -822,7 +822,6 @@ EXCLUDE_SYMBOLS = # command). EXAMPLE_PATH = @CMAKE_SOURCE_DIR@ \ - @CMAKE_SOURCE_DIR@/gsad \ @CMAKE_CURRENT_SOURCE_DIR@ # If the value of the EXAMPLE_PATH tag contains directories, you can use the From 2e074626c256ee74d0fb1122ef4639122af12985 Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Tue, 19 Dec 2023 19:35:17 +0200 Subject: [PATCH 2/6] Clear doxy warnings --- src/gsad.c | 3 --- src/gsad_gmp.c | 61 ++++++++++++++++--------------------------------- src/gsad_http.c | 1 - 3 files changed, 20 insertions(+), 45 deletions(-) diff --git a/src/gsad.c b/src/gsad.c index b5c5356812..ceef11eae6 100644 --- a/src/gsad.c +++ b/src/gsad.c @@ -30,9 +30,6 @@ * \section Introduction * \verbinclude README.md * - * \section Installation - * \verbinclude INSTALL - * * \section copying License * \verbinclude LICENSE */ diff --git a/src/gsad_gmp.c b/src/gsad_gmp.c index 3d3efab33a..176d3dae2b 100644 --- a/src/gsad_gmp.c +++ b/src/gsad_gmp.c @@ -766,6 +766,13 @@ message_invalid (gvm_connection_t *connection, credentials_t *credentials, * @brief Set redirect or return a basic action_result page based on entity. * * @param[in] connection Connection to manager + * @param[in] credentials Username and password for authentication. + * @param[in] params Request parameters. + * @param[in] entity Entity. + * @param[in] action Name of the action. + * @param[in] response_data Response data. + * + * @return Enveloped XML object. */ static gchar * response_from_entity (gvm_connection_t *connection, credentials_t *credentials, @@ -798,7 +805,6 @@ response_from_entity (gvm_connection_t *connection, credentials_t *credentials, * @param[in] type Type of resource. * @param[in] credentials Username and password for authentication. * @param[in] params Request parameters. - * @param[in] extra_xml Extra XML to insert inside page element. * @param[in] arguments Extra arguments for GMP GET command. * @param[out] response_data Extra data return for the HTTP response. * @@ -942,7 +948,6 @@ get_one (gvm_connection_t *connection, const char *type, * @param[in] type Entity type. * @param[in] credentials Username and password for authentication. * @param[in] params Request parameters. - * @param[in] extra_xml Extra XML to insert inside page element. * @param[in] arguments Extra arguments for GMP GET command. * @param[out] response_data Extra data return for the HTTP response. * @@ -1397,6 +1402,7 @@ export_resource (gvm_connection_t *connection, const char *type, * @brief Export a list of resources. * * @param[in] connection Connection to manager. + * @param[in] type Type of resource. * @param[in] credentials Username and password for authentication. * @param[in] params Request parameters. * @param[out] response_data Extra data return for the HTTP response. @@ -1700,7 +1706,6 @@ move_resource_to_trash (gvm_connection_t *connection, const char *type, * @brief Delete a resource from the trashcan * * @param[in] connection Connection to manager. - * @param[in] type Type of resource. * @param[in] credentials Username and password for authentication. * @param[in] params Request parameters. * @param[out] response_data Extra data return for the HTTP response. @@ -1870,19 +1875,6 @@ resource_action (gvm_connection_t *connection, credentials_t *credentials, } \ } -/** - * @brief Returns page to create a new container task. - * - * @param[in] connection Connection to manager. - * @param[in] credentials Credentials of user issuing the action. - * @param[in] params Request parameters. - * @param[in] message If not NULL, display message. - * @param[in] extra_xml Extra XML to insert inside page element. - * @param[out] response_data Extra data return for the HTTP response. - * - * @return Enveloped XML object. - */ - /** * @brief Create a report, get all tasks, envelope the result. * @@ -3325,10 +3317,10 @@ create_credential_gmp (gvm_connection_t *connection, credentials_t *credentials, /** * @brief Get one credential, envelope the result. * - * @param[in] connection Connection to manager. + * @param[in] connection Connection to manager. * @param[in] credentials Username and password for authentication. * @param[in] params Request parameters. - * @param[in] commands Extra commands to run before the others. + * @param[in] extra_xml Extra XML to insert inside page element. * @param[out] response_data Extra data return for the HTTP response. * * @return Enveloped XML object. @@ -4313,9 +4305,7 @@ get_alerts (gvm_connection_t *connection, credentials_t *, params_t *, * @param[in] xml XML. * @param[out] data Data. * @param[out] event Event. - * - * @return 0 on success, -1 on error. - */ + */ static void append_alert_event_data (GString *xml, params_t *data, const char *event) { @@ -5588,9 +5578,8 @@ delete_target_gmp (gvm_connection_t *connection, credentials_t *credentials, * @brief Restore a resource, get all trash, envelope the result. * * @param[in] connection Connection to manager. - * @param[in] connection Connection to manager. - * @param[in] credentials Username and password for authentication. - * @param[in] params Request parameters. + * @param[in] credentials Username and password for authentication. + * @param[in] params Request parameters. * @param[out] response_data Extra data return for the HTTP response. * * @return Enveloped XML object. @@ -7815,9 +7804,7 @@ delete_report_gmp (gvm_connection_t *connection, credentials_t *credentials, * @param[in] connection Connection to manager. * @param[in] credentials Username and password for authentication. * @param[in] params Request parameters. - * @param[in] commands Extra commands to run before the others. * @param[in] extra_xml Extra XML to insert inside page element. - * @param[out] error Set to 1 if error, else 0. * @param[out] response_data Extra data return for the HTTP response. * * @return Report. @@ -10957,6 +10944,8 @@ get_trash_gmp (gvm_connection_t *connection, credentials_t *credentials, * @param[out] xml GString to write responses to. * @param[out] modify_failed_flag Pointer to an int to set to 1 on failure * to modify one of the settings. + * @param[out] response_data Extra data return for the HTTP response. + * * @return 0 on success, -1 on error. */ static int @@ -11024,10 +11013,6 @@ send_settings_filters (gvm_connection_t *connection, params_t *data, * @param[in] credentials Credentials of user issuing the action. * @param[in] params Request parameters. * @param[in] accept_language Accept-Language, from browser. - * @param[out] timezone Timezone. Caller must free. - * @param[out] password Password. Caller must free. - * @param[out] severity Severity. Caller must free. - * @param[out] language Language. Caller must free. * @param[out] response_data Extra data return for the HTTP response. * * @return Enveloped XML object. @@ -12917,9 +12902,9 @@ create_port_range_gmp (gvm_connection_t *connection, credentials_t *credentials, * @brief Get one port_list, envelope the result. * * @param[in] connection Connection to manager. - * @param[in] credentials Username and password for authentication. - * @param[in] params Request parameters. - * @param[in] commands Extra commands to run before the others. + * @param[in] credentials Username and password for authentication. + * @param[in] params Request parameters. + * @param[in] extra_xml Extra XML to insert inside page element. * @param[out] response_data Extra data return for the HTTP response. * * @return Enveloped XML object. @@ -15664,7 +15649,6 @@ get_asset_gmp (gvm_connection_t *connection, credentials_t *credentials, * @param[in] connection Connection to manager. * @param[in] credentials Credentials for the manager connection. * @param[in] params Request parameters. - * @param[in] extra_xml Extra XML to insert inside page element. * @param[out] response_data Extra data return for the HTTP response. * * @return XML enveloped assets response or error message. @@ -16844,17 +16828,12 @@ authenticate_gmp (const gchar *username, const gchar *password, gchar **role, } /** - * @brief Check authentication credentials. + * @brief Log out. * * @param[in] username Username. * @param[in] password Password. - * @param[out] role Role. - * @param[out] timezone Timezone. - * @param[out] capabilities Capabilities of manager. - * @param[out] language User Interface Language, or NULL. - * @param[out] pw_warning Password warning message, NULL if password is OK. * - * @return 0 success, 1 manager down, 2 failed, 3 timeout, -1 error. + * @return 0 success, else error. */ int logout_gmp (const gchar *username, const gchar *password) diff --git a/src/gsad_http.c b/src/gsad_http.c index 61c1e38c40..f2510e8eb2 100644 --- a/src/gsad_http.c +++ b/src/gsad_http.c @@ -860,7 +860,6 @@ add_cors_headers (http_response_t *response) * @brief Add header to forbid caching to a HTTP response. * * @param[in] response The HTTP response to add the headers to. - * @param[in] allow_caching 1 to allow caching, 0 to forbid. */ void add_forbid_caching_headers (http_response_t *response) From fea119628a55bed798cc45c0dd38a0eab811e5e0 Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Tue, 19 Dec 2023 19:42:01 +0200 Subject: [PATCH 3/6] Fix spacing --- src/gsad_gmp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gsad_gmp.c b/src/gsad_gmp.c index 79283e89ce..665ee0a65e 100644 --- a/src/gsad_gmp.c +++ b/src/gsad_gmp.c @@ -4299,13 +4299,14 @@ get_alerts (gvm_connection_t *connection, credentials_t *, params_t *, #define EVENT_TYPE_TICKET_RECEIVED "Ticket received" #define EVENT_TYPE_ASSIGNED_TICKET_CHANGED "Assigned ticket changed" #define EVENT_TYPE_OWNED_TICKET_CHANGED "Owned ticket changed" + /** * @brief Send event data for an alert. * * @param[in] xml XML. * @param[out] data Data. * @param[out] event Event. - */ + */ static void append_alert_event_data (GString *xml, params_t *data, const char *event) { From 2cf505dd92de14a410eaa5ef13f04ea5a5bab7dc Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Tue, 5 Dec 2023 20:57:59 +0200 Subject: [PATCH 4/6] Add: compress responses in exec_gmp_get with Brotli --- src/CMakeLists.txt | 8 ++- src/gsad.c | 135 ++++++++++++++++++++++++++++++++++++++------- 2 files changed, 122 insertions(+), 21 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f7d2e54738..349ae67f6c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,6 +31,7 @@ pkg_check_modules (LIBGVM_UTIL REQUIRED libgvm_util>=22.6) pkg_check_modules (LIBGVM_GMP REQUIRED libgvm_gmp>=22.6) pkg_check_modules (GNUTLS REQUIRED gnutls>=3.2.15) pkg_check_modules (ZLIB REQUIRED zlib>=1.2) +pkg_check_modules (BROTLI libbrotlienc) message (STATUS "Looking for libgcrypt...") find_library (LIBGCRYPT gcrypt) @@ -63,6 +64,9 @@ set (HARDENING_FLAGS "-D_FORTIFY_SOURCE=2 -fstack-protector") set (LINKER_HARDENING_FLAGS "-Wl,-z,relro -Wl,-z,now") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat -Wformat-security") +if (BROTLI_FOUND) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_BROTLI=1") +endif (BROTLI_FOUND) set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Werror") set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${HARDENING_FLAGS}") @@ -73,7 +77,8 @@ include_directories (${LIBMICROHTTPD_INCLUDE_DIRS} ${LIBXML_INCLUDE_DIRS} ${LIBGVM_UTIL_INCLUDE_DIRS} ${LIBGVM_GMP_INCLUDE_DIRS} ${GNUTLS_INCLUDE_DIRS} - ${ZLIB_INCLUDE_DIRS}) + ${ZLIB_INCLUDE_DIRS} + ${BROTLI_INCLUDE_DIRS}) find_package (Threads) @@ -104,6 +109,7 @@ target_link_libraries (gsad ${LIBMICROHTTPD_LDFLAGS} ${LIBGCRYPT_LDFLAGS} ${GNUTLS_LDFLAGS} ${ZLIB_LDFLAGS} + ${BROTLI_LDFLAGS} ${LIBGVM_BASE_LDFLAGS} ${LIBGVM_UTIL_LDFLAGS} ${LIBGVM_GMP_LDFLAGS} diff --git a/src/gsad.c b/src/gsad.c index 1542ecb8ea..bd9932d850 100644 --- a/src/gsad.c +++ b/src/gsad.c @@ -42,6 +42,9 @@ #include #include +#ifdef HAVE_BROTLI +#include +#endif #include #include #include @@ -1158,32 +1161,59 @@ watch_client_connection (void *data) * @return 1 if may, else 0. */ static int -may_compress_response (http_connection_t *con) +may_compress (http_connection_t *con, const char *encoding) { - const char *ae; - const char *de; + const char *all, *one; - ae = MHD_lookup_connection_value (con, MHD_HEADER_KIND, - MHD_HTTP_HEADER_ACCEPT_ENCODING); - if (ae == NULL) + all = MHD_lookup_connection_value (con, MHD_HEADER_KIND, + MHD_HTTP_HEADER_ACCEPT_ENCODING); + if (all == NULL) return 0; - if (strcmp (ae, "*") == 0) + if (strcmp (all, "*") == 0) return 1; - de = strstr (ae, "deflate"); - if (de == NULL) + one = strstr (all, encoding); + if (one == NULL) return 0; - if (((de == ae) || (de[-1] == ',') || (de[-1] == ' ')) - && ((de[strlen ("deflate")] == '\0') || (de[strlen ("deflate")] == ',') - || (de[strlen ("deflate")] == ';'))) + if (((one == all) || (one[-1] == ',') || (one[-1] == ' ')) + && ((one[strlen (encoding)] == '\0') || (one[strlen (encoding)] == ',') + || (one[strlen (encoding)] == ';'))) return 1; return 0; } /** - * @brief Compress response. + * @brief Check whether may compress response. + * + * @param[in] con HTTP Connection + * + * @return 1 if may, else 0. + */ +static int +may_deflate (http_connection_t *con) +{ + return may_compress (con, "deflate"); +} + +#ifdef HAVE_BROTLI +/** + * @brief Check whether may compress response. + * + * @param[in] con HTTP Connection + * + * @return 1 if may, else 0. + */ +static int +may_brotli (http_connection_t *con) +{ + return may_compress (con, "br"); +} +#endif + +/** + * @brief Compress response with zlib. * * @param[in] res_len Response length. * @param[in] res Response. @@ -1193,8 +1223,8 @@ may_compress_response (http_connection_t *con) * @return 1 on success, else 0. */ static int -compress_response (const size_t res_len, const char *res, size_t *comp_len, - char **comp) +compress_response_deflate (const size_t res_len, const char *res, size_t *comp_len, + char **comp) { Bytef *cbuf; uLongf cbuf_size; @@ -1216,6 +1246,50 @@ compress_response (const size_t res_len, const char *res, size_t *comp_len, return 0; } +#ifdef HAVE_BROTLI +/** + * @brief Compress response with Brotli. + * + * @param[in] res_len Response length. + * @param[in] res Response. + * @param[out] comp_len Compressed length. + * @param[out] comp Compressed response. + * + * @return 1 on success, else 0. + */ +static int +compress_response_brotli (const size_t res_len, const char *res, size_t *comp_len, + char **comp) +{ + size_t cbuf_size; + uint8_t *cbuf; + int ret; + + cbuf_size = BrotliEncoderMaxCompressedSize (res_len); + cbuf = g_malloc (cbuf_size); + + ret = BrotliEncoderCompress (BROTLI_DEFAULT_QUALITY, + BROTLI_DEFAULT_WINDOW, + BROTLI_DEFAULT_MODE, + res_len, + (uint8_t*) res, + &cbuf_size, + cbuf); + + if ((ret == BROTLI_TRUE) && (cbuf_size < res_len)) + { + *comp = (char *) cbuf; + *comp_len = cbuf_size; + g_warning ("%s: 1", __func__); + return 1; + } + + g_free (cbuf); + g_warning ("%s: 0", __func__); + return 0; +} +#endif + /** * @brief Handle a complete GET request. * @@ -1242,8 +1316,10 @@ exec_gmp_get (http_connection_t *con, gsad_connection_info_t *con_info, cmd_response_data_t *response_data; pthread_t watch_thread; connection_watcher_data_t *watcher_data; - validator_t validator = get_validator (); + validator_t validator; + gchar *encoding; + validator = get_validator (); response_data = cmd_response_data_new (); cmd = params_value (params, "cmd"); @@ -1547,24 +1623,43 @@ exec_gmp_get (http_connection_t *con, gsad_connection_info_t *con_info, if (res_len == 0) res_len = strlen (res); - if (may_compress_response (con)) + encoding = NULL; + +#ifdef HAVE_BROTLI + if (may_brotli (con)) + { + gsize comp_len; + + if (compress_response_brotli (res_len, res, &comp_len, &comp)) + { + free (res); + res_len = comp_len; + res = comp; + encoding = "br"; + } + } +#endif + + if ((encoding == NULL) + && may_deflate (con)) { gsize comp_len; - if (compress_response (res_len, res, &comp_len, &comp)) + if (compress_response_deflate (res_len, res, &comp_len, &comp)) { free (res); res_len = comp_len; res = comp; + encoding = "deflate"; } } response = MHD_create_response_from_buffer (res_len, (void *) res, MHD_RESPMEM_MUST_FREE); - if (comp) + if (encoding) MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_ENCODING, - "deflate"); + encoding); if (watcher_data) { From 2eb2ba1ed440715d6cac44fa94a6ddaaaf058e74 Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Tue, 19 Dec 2023 15:06:50 +0200 Subject: [PATCH 5/6] Clang format --- src/gsad.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/gsad.c b/src/gsad.c index bd9932d850..29edae3ada 100644 --- a/src/gsad.c +++ b/src/gsad.c @@ -1223,8 +1223,8 @@ may_brotli (http_connection_t *con) * @return 1 on success, else 0. */ static int -compress_response_deflate (const size_t res_len, const char *res, size_t *comp_len, - char **comp) +compress_response_deflate (const size_t res_len, const char *res, + size_t *comp_len, char **comp) { Bytef *cbuf; uLongf cbuf_size; @@ -1258,8 +1258,8 @@ compress_response_deflate (const size_t res_len, const char *res, size_t *comp_l * @return 1 on success, else 0. */ static int -compress_response_brotli (const size_t res_len, const char *res, size_t *comp_len, - char **comp) +compress_response_brotli (const size_t res_len, const char *res, + size_t *comp_len, char **comp) { size_t cbuf_size; uint8_t *cbuf; @@ -1268,13 +1268,9 @@ compress_response_brotli (const size_t res_len, const char *res, size_t *comp_le cbuf_size = BrotliEncoderMaxCompressedSize (res_len); cbuf = g_malloc (cbuf_size); - ret = BrotliEncoderCompress (BROTLI_DEFAULT_QUALITY, - BROTLI_DEFAULT_WINDOW, - BROTLI_DEFAULT_MODE, - res_len, - (uint8_t*) res, - &cbuf_size, - cbuf); + ret = BrotliEncoderCompress (BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, + BROTLI_DEFAULT_MODE, res_len, (uint8_t *) res, + &cbuf_size, cbuf); if ((ret == BROTLI_TRUE) && (cbuf_size < res_len)) { @@ -1640,8 +1636,7 @@ exec_gmp_get (http_connection_t *con, gsad_connection_info_t *con_info, } #endif - if ((encoding == NULL) - && may_deflate (con)) + if ((encoding == NULL) && may_deflate (con)) { gsize comp_len; From 74624ba0b556e08e84394e8f2ee3d0b7dcabfb1d Mon Sep 17 00:00:00 2001 From: Matt Mundell Date: Tue, 19 Dec 2023 15:10:54 +0200 Subject: [PATCH 6/6] Add libbrotli to Prerequisites --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9552a56c6c..5d048d214a 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ Prerequisites: * pkg-config * gcc * zlib >= 1.2 +* libbrotli (optional, for Brotli compression) Prerequisites for building documentation: * Doxygen