From: Vsevolod Stakhov Date: Mon, 28 Jul 2025 08:53:43 +0000 (+0100) Subject: [Feature] Add some convenience options to rspamc X-Git-Tag: 3.13.0~38^2~9 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e013a3bdc564d33943817e9b8c60d7b041615975;p=thirdparty%2Frspamd.git [Feature] Add some convenience options to rspamc --- diff --git a/src/client/rspamc.cxx b/src/client/rspamc.cxx index af88acb337..04bbaeac85 100644 --- a/src/client/rspamc.cxx +++ b/src/client/rspamc.cxx @@ -91,6 +91,8 @@ static gboolean skip_attachments = FALSE; static const char *pubkey = nullptr; static const char *user_agent = "rspamc"; static const char *files_list = nullptr; +static const char *queue_id = nullptr; +static std::string settings; std::vector children; static GPatternSpec **exclude_compiled = nullptr; @@ -103,6 +105,11 @@ static gboolean rspamc_password_callback(const char *option_name, gpointer data, GError **error); +static gboolean rspamc_settings_callback(const char *option_name, + const char *value, + gpointer data, + GError **error); + static GOptionEntry entries[] = { {"connect", 'h', 0, G_OPTION_ARG_STRING, &connect_str, @@ -183,6 +190,10 @@ static GOptionEntry entries[] = "Use specific User-Agent instead of \"rspamc\"", nullptr}, {"files-list", '\0', 0, G_OPTION_ARG_FILENAME, &files_list, "Read one or more newline separated filenames to scan from file", nullptr}, + {"queue-id", '\0', 0, G_OPTION_ARG_STRING, &queue_id, + "Set Queue-ID header for the request", nullptr}, + {"settings", '\0', 0, G_OPTION_ARG_CALLBACK, (void *) &rspamc_settings_callback, + "Set Settings header as JSON/UCL for the request", nullptr}, {nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr}}; static void rspamc_symbols_output(FILE *out, ucl_object_t *obj); @@ -567,6 +578,46 @@ rspamc_password_callback(const char *option_name, return TRUE; } +static gboolean +rspamc_settings_callback(const char *option_name, + const char *value, + gpointer data, + GError **error) +{ + if (value == nullptr) { + g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Settings parameter cannot be empty"); + return FALSE; + } + + // Parse the settings string using UCL to validate it + struct ucl_parser *parser = ucl_parser_new(UCL_PARSER_KEY_LOWERCASE); + if (!ucl_parser_add_string(parser, value, strlen(value))) { + auto *ucl_error = ucl_parser_get_error(parser); + g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Invalid JSON/UCL in settings: %s", ucl_error); + ucl_parser_free(parser); + return FALSE; + } + + // Get the parsed object and validate it + auto *obj = ucl_parser_get_object(parser); + if (obj == nullptr) { + g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed to parse settings as JSON/UCL"); + ucl_parser_free(parser); + return FALSE; + } + + // Store the validated settings string + settings = value; + + ucl_object_unref(obj); + ucl_parser_free(parser); + + return TRUE; +} + /* * Parse command line */ @@ -890,6 +941,14 @@ add_options(GQueue *opts) hdr++; } + if (queue_id != nullptr) { + add_client_header(opts, "Queue-Id", queue_id); + } + + if (!settings.empty()) { + add_client_header(opts, "Settings", settings.c_str()); + } + if (!flagbuf.empty()) { if (flagbuf.back() == ',') { flagbuf.pop_back();