From 48fe6ce0d206a381f4eac44864cf6f8f284b8d78 Mon Sep 17 00:00:00 2001 From: jb4z Date: Sun, 19 Jan 2025 11:27:20 +0100 Subject: [PATCH 1/6] Update additional_message_headers.php: Add support for CALLABLE/callback via config The purpose of this patch is to have Roundcube execute a callback function during runtime, as defined in config.inc.php for more complex tasks. For example, one could configure something like this: $config['additional_message_headers']['X-Sender'] = null; $config['additional_message_headers']['X-RC-USR'] = (function() { $d = json_encode(['u' => rcube::get_instance()->get_user_name(), 'r' => $_SERVER['REMOTE_ADDR'], 'a' => empty($_SERVER['HTTP_USER_AGENT']) ? '-' : $_SERVER['HTTP_USER_AGENT'], 't' => $_SERVER['REQUEST_TIME'] ]); return base64_encode($d); # should also be encrypted ;) }); In this example, a) disables the cleartext X-Sender header; b) adds a dynamic header X-RC-USR in base64-JSON-encoded form, which could later be used for compliance purposes. If this header is automatically processed by the mail gateway, further analysis could aid in detecting abuse patterns, while not directly exposing this sensitive information as human-readable text; if properly encrypted (out of this scope), this could eliminate privacy concerns. One of the ideas behind this is that using Roundcube (or any other webmailer) usually masquerades the original user's IP address by the webmailer's server IP address to the SMTP server; this is not the case when a user talks to the SMTP server directly. With tight integration into your setup, you will never again have to sift through different logs/correlate IP address information just to find the guy who sent this message which the person behind the user's login denies having sent. This patch also reduces two arrays ($search, $replace) into one ($map) for maintainability. --- .../additional_message_headers.php | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/plugins/additional_message_headers/additional_message_headers.php b/plugins/additional_message_headers/additional_message_headers.php index 5359148feb..6fa8fb8fbb 100644 --- a/plugins/additional_message_headers/additional_message_headers.php +++ b/plugins/additional_message_headers/additional_message_headers.php @@ -42,22 +42,24 @@ public function message_headers($args) if (!empty($additional_headers)) { // Expand the % config variables - $search = [ - '/(^|[^%])%u/', - '/(^|[^%])%l/', - '/(^|[^%])%d/', + $map = [ + '/(^|[^%])%u/' => '${1}' . $rcube->get_user_name(), + '/(^|[^%])%l/' => '${1}' . $rcube->user->get_username('local'), + '/(^|[^%])%d/' => '${1}' . $rcube->user->get_username('domain'), ]; + $search = array_keys($map); + $replace = array_values($map); - $replace = [ - '${1}' . $rcube->get_user_name(), - '${1}' . $rcube->user->get_username('local'), - '${1}' . $rcube->user->get_username('domain'), - ]; - - $additional_headers = preg_replace($search, $replace, $additional_headers); - - // replace %% with % - $additional_headers = preg_replace('/%(%[uld])/', '\1', $additional_headers); + // Loop the array and see whether we're dealing with a CALLABLE or implying a STRING + foreach ($additional_headers as $key => $val) { + if (is_callable($val)) { + $additional_headers[$key] = $val(); + } else { + $additional_headers = preg_replace($search, $replace, $additional_headers); + // replace %% with % + $additional_headers = preg_replace('/%(%[uld])/', '\1', $additional_headers); + } + } $args['message']->headers($additional_headers, true); } From 7659584c7bc4d3233988dde520bb308649875ecf Mon Sep 17 00:00:00 2001 From: James Date: Sun, 19 Jan 2025 11:53:39 +0100 Subject: [PATCH 2/6] Update additional_message_headers.php Missed something while creating initial patch --- .../additional_message_headers/additional_message_headers.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/additional_message_headers/additional_message_headers.php b/plugins/additional_message_headers/additional_message_headers.php index 6fa8fb8fbb..ab55d0ff81 100644 --- a/plugins/additional_message_headers/additional_message_headers.php +++ b/plugins/additional_message_headers/additional_message_headers.php @@ -55,9 +55,9 @@ public function message_headers($args) if (is_callable($val)) { $additional_headers[$key] = $val(); } else { - $additional_headers = preg_replace($search, $replace, $additional_headers); + $additional_headers = preg_replace($search, $replace, $val); // replace %% with % - $additional_headers = preg_replace('/%(%[uld])/', '\1', $additional_headers); + $additional_headers = preg_replace('/%(%[uld])/', '\1', $val); } } From 31401fba49f0999742699806c2a2da2225e6c00e Mon Sep 17 00:00:00 2001 From: James Date: Sun, 19 Jan 2025 11:54:40 +0100 Subject: [PATCH 3/6] Update additional_message_headers.php 3 times' a charm --- .../additional_message_headers/additional_message_headers.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/additional_message_headers/additional_message_headers.php b/plugins/additional_message_headers/additional_message_headers.php index ab55d0ff81..32fcd7b569 100644 --- a/plugins/additional_message_headers/additional_message_headers.php +++ b/plugins/additional_message_headers/additional_message_headers.php @@ -55,9 +55,9 @@ public function message_headers($args) if (is_callable($val)) { $additional_headers[$key] = $val(); } else { - $additional_headers = preg_replace($search, $replace, $val); + $additional_headers[$key] = preg_replace($search, $replace, $val); // replace %% with % - $additional_headers = preg_replace('/%(%[uld])/', '\1', $val); + $additional_headers[$key] = preg_replace('/%(%[uld])/', '\1', $val); } } From acef54193e5bf114726d046fa2f2c9f8fc0ddd86 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 16 Feb 2025 02:40:31 +0100 Subject: [PATCH 4/6] Update additional_message_headers.php -- fix trailing whitespace --- .../additional_message_headers/additional_message_headers.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/additional_message_headers/additional_message_headers.php b/plugins/additional_message_headers/additional_message_headers.php index 32fcd7b569..5744eb027c 100644 --- a/plugins/additional_message_headers/additional_message_headers.php +++ b/plugins/additional_message_headers/additional_message_headers.php @@ -57,7 +57,7 @@ public function message_headers($args) } else { $additional_headers[$key] = preg_replace($search, $replace, $val); // replace %% with % - $additional_headers[$key] = preg_replace('/%(%[uld])/', '\1', $val); + $additional_headers[$key] = preg_replace('/%(%[uld])/', '\1', $val); } } From 749bcbbf771009f25f88fe59017222decb76c455 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 16 Feb 2025 02:56:06 +0100 Subject: [PATCH 5/6] Update config.inc.php.sample: add additional_message_headers, clarify wording for $config['skin'] (#9755) --- config/config.inc.php.sample | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/config/config.inc.php.sample b/config/config.inc.php.sample index cf38d238bc..f42e898933 100644 --- a/config/config.inc.php.sample +++ b/config/config.inc.php.sample @@ -62,5 +62,24 @@ $config['plugins'] = [ 'zipdownload', ]; -// skin name: folder from skins/ +// Specify Roundcube's Default Skin, equal to the folder name beneath skins/ $config['skin'] = 'elastic'; + +// Optional config of the additional_message_headers plugin (Issue #9755; Feb 2025) +// Uncomment to remove the X-Sender header from outgoing messages: +// $config['additional_message_headers']['X-Sender'] = null; +// +// You can also specify a callback function like this, which would be called when +// the user clicks the "Send" button, allowing you to access to the rcube object. +// This example adds an X-RC-USR header with a base64-encoded JSON string. +// Do not use this in production unless you know what you are doing and your +// users are informed via Terms of Service or other means of compliance. +// $config['additional_message_headers']['X-RC-USR'] = (function() { +// $d = json_encode([ +// 'u' => rcube::get_instance()->get_user_name(), +// 'r' => $_SERVER['REMOTE_ADDR'], +// 'a' => empty($_SERVER['HTTP_USER_AGENT']) ? '-' : $_SERVER['HTTP_USER_AGENT'], +// 't' => $_SERVER['REQUEST_TIME'] +// ]); +// return base64_encode($d); # should be encrypted! +// }); From 5c94d7067621a5f7fbf74f701370b615e9234e21 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 16 Feb 2025 03:08:38 +0100 Subject: [PATCH 6/6] Update config.inc.php.sample: add additional_message_headers, clarify wording for $config['skin'] (#9755) --- config/config.inc.php.sample | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config/config.inc.php.sample b/config/config.inc.php.sample index f42e898933..3df0ab65f7 100644 --- a/config/config.inc.php.sample +++ b/config/config.inc.php.sample @@ -70,10 +70,10 @@ $config['skin'] = 'elastic'; // $config['additional_message_headers']['X-Sender'] = null; // // You can also specify a callback function like this, which would be called when -// the user clicks the "Send" button, allowing you to access to the rcube object. +// the user clicks the "Send" button, giving you access to the rcube object. // This example adds an X-RC-USR header with a base64-encoded JSON string. -// Do not use this in production unless you know what you are doing and your -// users are informed via Terms of Service or other means of compliance. +// Kindly take care not to expose personally identifiable information. +// Do not use below example in production without adjustments and testing. // $config['additional_message_headers']['X-RC-USR'] = (function() { // $d = json_encode([ // 'u' => rcube::get_instance()->get_user_name(),