]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
login-common: Use var_expand_template() to simplify logging
authorAki Tuomi <aki.tuomi@open-xchange.com>
Thu, 29 Jan 2026 12:59:46 +0000 (14:59 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Fri, 20 Mar 2026 16:11:43 +0000 (16:11 +0000)
src/login-common/Makefile.am
src/login-common/client-common.c
src/login-common/client-common.h
src/login-common/login-log.h [new file with mode: 0644]
src/login-common/login-settings.c
src/login-common/login-settings.h

index 2999fa94a7892f7ccca7e73663a4f3c7c59faef4..8952a5f9e5e7e2d97c9a7aa06deed77eac5989d6 100644 (file)
@@ -28,6 +28,7 @@ headers = \
        login-proxy.h \
        login-proxy-state.h \
        login-settings.h \
+       login-log.h \
        sasl-server.h
 
 pkginc_libdir=$(pkgincludedir)
index 17a4ca30b62108e1bcb7f7ca8db113059af86271..909509e0f668b056f43f89845031f5f8556107af 100644 (file)
@@ -30,6 +30,8 @@
 #include "login-proxy.h"
 #include "settings-parser.h"
 #include "client-common.h"
+#include "var-expand-split.h"
+#include "login-log.h"
 
 struct client *clients = NULL;
 struct client *destroyed_clients = NULL;
@@ -1282,6 +1284,42 @@ client_var_expand_callback(void *context, struct var_expand_params *params_r)
        *params_r = *params;
 }
 
+static int expand_element(string_t *dest, const char *elem,
+                         const struct login_log_settings *log_set, unsigned int *i,
+                         const struct var_expand_params *params,
+                         bool *has_user_r, const char **errelem_r,
+                         const char **error_r)
+{
+       if (strchr(elem, *LOG_ELEMENT_PLACEHOLDER) == NULL) {
+               str_append(dest, elem);
+               return 0;
+       }
+
+       const char *ptr = elem;
+       const char *pptr = ptr;
+       int ret = 0;
+
+       /* Find all placeholders in this element, and try expand corresponding
+          program. If it fails, reconstruct the original program for error
+          logging. */
+       while ((ptr = strchr(pptr, *LOG_ELEMENT_PLACEHOLDER)) != NULL) {
+               str_append_data(dest, pptr, ptr - pptr);
+               const struct var_expand_program *prog =
+                       array_idx_elem(&log_set->elements, *i);
+               *has_user_r = var_expand_program_has_variable(prog, "user", TRUE);
+               (*i)++;
+               if (var_expand_program_execute_one(dest, prog, params, error_r) < 0) {
+                       /* write the failed program here */
+                       if (errelem_r != NULL)
+                               *errelem_r = var_expand_program_to_string_one(prog);
+                       ret = -1;
+               }
+               pptr = ptr + 1;
+       }
+       str_append(dest, pptr);
+       return ret;
+}
+
 static const char *
 client_get_log_str(struct client *client, const char *msg)
 {
@@ -1295,43 +1333,60 @@ client_get_log_str(struct client *client, const char *msg)
                .event = client->event,
        };
        static bool expand_error_logged = FALSE;
-       char *const *e;
-       const char *error;
+       const char *const *e;
+       const char *errelem, *error;
        string_t *str, *str2;
 
        str = t_str_new(256);
        str2 = t_str_new(256);
+
        size_t pos = 0;
-       for (e = client->set->log_format_elements_split; *e != NULL; e++) {
-               pos = str->used;
-               struct var_expand_program *prog;
-               if (var_expand_program_create(*e, &prog, &error) < 0 ||
-                   var_expand_program_execute(str, prog, params, &error) < 0) {
+       unsigned int i = 0, i2;
+
+       for (e = client->set->log_set->template; *e != NULL; e++) {
+               pos = str_len(str);
+               bool has_user = FALSE;
+               str_truncate(str2, 0);
+               /* Keep track which program we were at, as expand_element
+                  is called twice, so we can rewind back to where we were. */
+               i2 = i;
+               int ret = expand_element(str2, *e, client->set->log_set, &i,
+                                        params, &has_user, &errelem, &error);
+               if (ret < 0) {
                        if (!expand_error_logged) {
                                /* NOTE: Don't log via client->event -
                                   it would cause recursion. */
                                i_error("Failed to expand log_format_elements=%s: %s",
-                                       *e, error);
+                                       errelem, error);
                                expand_error_logged = TRUE;
                        }
                }
-               const char *const *vars = var_expand_program_variables(prog);
-               if (str_array_find(vars, "user")) {
-                       /* username is added even if it's empty */
-                       var_expand_program_free(&prog);
-               } else {
+               str_append(str, str_c(str2));
+               if (has_user) {
+                       /* if the element contained username somehow, we will
+                          print it even if it would expand to empty. */
+               } else if (strchr(*e, *LOG_ELEMENT_PLACEHOLDER) != NULL) {
+                       /* render the element again against empty client
+                          so we can drop any elements that would've actually
+                          rendered as empty. */
+                       i = i2;
                        str_truncate(str2, 0);
-                       int ret = var_expand_program_execute(str2, prog,
-                                                            &empty_params, &error);
-                       var_expand_program_free(&prog);
+                       ret = expand_element(str2, *e, client->set->log_set, &i,
+                                            &empty_params, &has_user, NULL,
+                                            &error);
                        if (ret < 0 || strcmp(str_c(str)+pos, str_c(str2)) == 0) {
-                               /* we just logged this error above. no need
-                                  to do it again. */
+                               /* so either it still errored or, the element
+                                 was rendered without any acttual value.
+
+                                 any errors were already logged in the previous
+                                 expansion, so we can ignore it here.
+
+                                 drop the element from output. */
                                str_truncate(str, pos);
                                continue;
                        }
                }
-               pos = str->used;
+               pos = str_len(str);
                if (str_len(str) > 0)
                        str_append(str, ", ");
        }
index bc6af6f7a01053c2ef02c73bb3ca6360d6ae659a..e8cfcfbd9bc0e0e50811858df9b5b156b628aae1 100644 (file)
@@ -195,6 +195,9 @@ struct client {
        const char *session_id, *listener_name, *postlogin_socket_path;
        const char *local_name;
        const char *client_cert_common_name;
+       struct var_expand_program *log_progam;
+       const char *const *const log_template;
+       ARRAY_TYPE(const_expansion_program) *log_elements;
 
        string_t *client_id;
        ARRAY_TYPE(const_string) forward_fields;
diff --git a/src/login-common/login-log.h b/src/login-common/login-log.h
new file mode 100644 (file)
index 0000000..5bb0bf8
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef LOGIN_LOG_H
+#define LOGIN_LOG_H 1
+
+struct login_log_settings {
+       struct var_expand_program *program;
+       const char *const *template;
+       ARRAY_TYPE(const_expansion_program) elements;
+};
+
+#endif
index d54b2915210690a076392edeeb89b6f8bd1154e3..1bbc5eecdc727b00b7b938210f2caa91df98ef26 100644 (file)
@@ -1,9 +1,13 @@
 /* Copyright (c) 2005-2018 Dovecot authors, see the included COPYING file */
 
 #include "login-common.h"
+#include "array.h"
+#include "var-expand-private.h"
+#include "var-expand-split.h"
 #include "settings-parser.h"
 #include "master-service-settings.h"
 #include "login-settings.h"
+#include "login-log.h"
 #include "settings-parser.h"
 
 #include <unistd.h>
@@ -88,14 +92,35 @@ const struct setting_parser_info login_setting_parser_info = {
 };
 
 /* <settings checks> */
-static bool login_settings_check(void *_set, pool_t pool,
+static bool login_settings_check(void *_set, pool_t pool ATTR_UNUSED,
                                 const char **error_r)
 {
        struct login_settings *set = _set;
 
-       set->log_format_elements_split =
-               p_strsplit(pool, set->login_log_format_elements, " ");
-
+       struct var_expand_program *program;
+       /* Replace any \001, as we are using it for placeholder for elements. */
+       const char *login_log_format =
+               t_str_replace(set->login_log_format_elements,
+                             *LOG_ELEMENT_PLACEHOLDER, '\002');
+       if (var_expand_program_create(login_log_format, &program, error_r) < 0)
+               return FALSE;
+#ifndef CONFIG_BINARY
+       struct login_log_settings *log_set = p_new(pool, struct login_log_settings, 1);
+       /* we want the expansion program to be free'd when the settings are free'd */
+       pool_add_external_ref(pool, program->pool);
+       pool_unref(&program->pool);
+       const char *template;
+
+       p_array_init(&log_set->elements, pool, 16);
+       var_expand_program_template(pool, program, LOG_ELEMENT_PLACEHOLDER,
+                                   &template, &log_set->elements);
+       /* and finally we split it like before */
+       log_set->template = (const char *const *) p_strsplit(pool, template, " ");
+       log_set->program = program;
+       set->log_set = log_set;
+#else
+       var_expand_program_free(&program);
+#endif
        if (strcmp(set->ssl, "required") == 0 && set->auth_allow_cleartext) {
                *error_r = "auth_allow_cleartext=yes has no effect with ssl=required";
                return FALSE;
index d3e3eee133cbaef94246a5c5b5be8139cc88c784..a25f9cfa8dc82183b2188bb0ead1a122da65dff8 100644 (file)
@@ -1,6 +1,12 @@
 #ifndef LOGIN_SETTINGS_H
 #define LOGIN_SETTINGS_H
 
+/* <settings checks> */
+#define LOG_ELEMENT_PLACEHOLDER "\001"
+/* </settings checks> */
+
+struct login_log_settings;
+
 struct login_settings {
        pool_t pool;
        ARRAY_TYPE(const_string) login_trusted_networks;
@@ -30,7 +36,7 @@ struct login_settings {
        unsigned int mail_max_userip_connections;
 
        /* generated: */
-       char *const *log_format_elements_split;
+       const struct login_log_settings *log_set;
 };
 
 extern const struct setting_parser_info login_setting_parser_info;