public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
PHP
49%
JavaScript
35%
HTML
9%
CSS
4%
Shell
2%
Python
1%
Lua
0%
Perl
0%
Ruby
0%
SCSS
0%
Create file
Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git
Commit
[Web] Minor changes or fixes; [Web] Add multiple transport destinations at the same time - comma separated
ab425407
data/web/admin.php | 2 +-
data/web/inc/functions.transports.inc.php | 74 ++++++++++++++++---------------
data/web/js/site/admin.js | 16 ++++---
data/web/lang/lang.de.php | 6 ++-
data/web/lang/lang.en.php | 5 ++-
5 files changed, 57 insertions(+), 46 deletions(-)
Diff
diff --git a/data/web/admin.php b/data/web/admin.php
index 07d38776..5c79aab4 100644
--- a/data/web/admin.php
+++ b/data/web/admin.php
@@ -289,7 +289,7 @@ if (!isset($_SESSION['gal']) && $license_cache = $redis->Get('LICENSE_STATUS_CAC
<form class="form" data-id="transport" role="form" method="post">
<div class="form-group">
<label for="destination"><?=$lang['admin']['destination'];?></label>
- <input class="form-control input-sm" name="destination" placeholder='example.org, .example.org, *, [email protected]' required>
+ <input class="form-control input-sm" name="destination" placeholder='<?=$lang['admin']['transport_dest_format'];?>' required>
</div>
<div class="form-group">
<label for="nexthop"><?=$lang['admin']['nexthop'];?></label>
diff --git a/data/web/inc/functions.transports.inc.php b/data/web/inc/functions.transports.inc.php
index 0bc19c13..f87a7c01 100644
--- a/data/web/inc/functions.transports.inc.php
+++ b/data/web/inc/functions.transports.inc.php
@@ -186,7 +186,7 @@ function transport($_action, $_data = null) {
);
return false;
}
- $destination = trim($_data['destination']);
+ $destinations = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['destination']));
$active = intval($_data['active']);
$lookup_mx = intval($_data['lookup_mx']);
$nexthop = trim($_data['nexthop']);
@@ -194,15 +194,6 @@ function transport($_action, $_data = null) {
$next_hop_clean = (isset($next_hop_matches[1])) ? $next_hop_matches[1] : $nexthop;
$username = str_replace(':', '\:', trim($_data['username']));
$password = str_replace(':', '\:', trim($_data['password']));
- // ".domain" is a valid destination, "..domain" is not
- if (empty($destination) || (is_valid_domain_name(preg_replace('/^' . preg_quote('.', '/') . '/', '', $destination)) === false && $destination != '*' && filter_var($destination, FILTER_VALIDATE_EMAIL) === false)) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => 'invalid_destination'
- );
- return false;
- }
if (empty($nexthop)) {
$_SESSION['return'][] = array(
'type' => 'danger',
@@ -225,16 +216,35 @@ function transport($_action, $_data = null) {
);
return false;
}
- if ($transport_data['destination'] == $destination) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => 'transport_dest_exists'
- );
- return false;
+ foreach ($destinations as $d_ix => &$dest) {
+ if (empty($dest)) {
+ unset($destinations[$d_ix]);
+ continue;
+ }
+ if ($transport_data['destination'] == $dest) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('transport_dest_exists', $dest)
+ );
+ unset($destinations[$d_ix]);
+ continue;
+ }
+ // ".domain" is a valid destination, "..domain" is not
+ if (empty($dest) || (is_valid_domain_name(preg_replace('/^' . preg_quote('.', '/') . '/', '', $dest)) === false && $dest != '*' && filter_var($dest, FILTER_VALIDATE_EMAIL) === false)) {
+ $_SESSION['return'][] = array(
+ 'type' => 'danger',
+ 'log' => array(__FUNCTION__, $_action, $_data_log),
+ 'msg' => array('invalid_destination', $dest)
+ );
+ unset($destinations[$d_ix]);
+ continue;
+ }
}
}
}
+ $destinations = array_values($destinations);
+ if (empty($destinations)) { return false; }
if (isset($next_hop_matches[1])) {
if (in_array($next_hop_clean, $existing_nh)) {
$_SESSION['return'][] = array(
@@ -257,35 +267,27 @@ function transport($_action, $_data = null) {
}
}
}
- try {
+ foreach ($destinations as $insert_dest) {
$stmt = $pdo->prepare("INSERT INTO `transports` (`nexthop`, `destination`, `username` , `password`, `lookup_mx`, `active`)
VALUES (:nexthop, :destination, :username, :password, :lookup_mx, :active)");
$stmt->execute(array(
':nexthop' => $nexthop,
- ':destination' => $destination,
+ ':destination' => $insert_dest,
':username' => $username,
':password' => str_replace(':', '\:', $password),
':lookup_mx' => $lookup_mx,
':active' => $active
));
- $stmt = $pdo->prepare("UPDATE `transports` SET
- `username` = :username,
- `password` = :password
- WHERE `nexthop` = :nexthop");
- $stmt->execute(array(
- ':nexthop' => $nexthop,
- ':username' => $username,
- ':password' => $password
- ));
- }
- catch (PDOException $e) {
- $_SESSION['return'][] = array(
- 'type' => 'danger',
- 'log' => array(__FUNCTION__, $_action, $_data_log),
- 'msg' => array('mysql_error', $e)
- );
- return false;
}
+ $stmt = $pdo->prepare("UPDATE `transports` SET
+ `username` = :username,
+ `password` = :password
+ WHERE `nexthop` = :nexthop");
+ $stmt->execute(array(
+ ':nexthop' => $nexthop,
+ ':username' => $username,
+ ':password' => $password
+ ));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
diff --git a/data/web/js/site/admin.js b/data/web/js/site/admin.js
index cb124dd3..cc8809ba 100644
--- a/data/web/js/site/admin.js
+++ b/data/web/js/site/admin.js
@@ -74,7 +74,8 @@ jQuery(function($){
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"state": {"enabled": true},
"filtering": {"enabled": true,"delay": 1,"position": "left","connectors": false,"placeholder": lang.filter_table},
- "sorting": {"enabled": true}
+ "sorting": {"enabled": true},
+ "toggleSelector": "table tbody span.footable-toggle"
});
}
function draw_admins() {
@@ -101,7 +102,8 @@ jQuery(function($){
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": false},
"state": {"enabled": true},
- "sorting": {"enabled": true}
+ "sorting": {"enabled": true},
+ "toggleSelector": "table tbody span.footable-toggle"
});
}
function draw_fwd_hosts() {
@@ -126,7 +128,8 @@ jQuery(function($){
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
- "sorting": {"enabled": true}
+ "sorting": {"enabled": true},
+ "toggleSelector": "table tbody span.footable-toggle"
});
}
function draw_relayhosts() {
@@ -153,7 +156,8 @@ jQuery(function($){
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
- "sorting": {"enabled": true}
+ "sorting": {"enabled": true},
+ "toggleSelector": "table tbody span.footable-toggle"
});
}
function draw_transport_maps() {
@@ -180,7 +184,8 @@ jQuery(function($){
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
- "sorting": {"enabled": true}
+ "sorting": {"enabled": true},
+ "toggleSelector": "table tbody span.footable-toggle"
});
}
function draw_queue() {
@@ -210,6 +215,7 @@ jQuery(function($){
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"sorting": {"enabled": true},
+ "toggleSelector": "table tbody span.footable-toggle",
"on": {
"ready.ft.table": function(e, ft){
table_admin_ready(ft, 'queuetable');
diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php
index b7215750..bbeba230 100644
--- a/data/web/lang/lang.de.php
+++ b/data/web/lang/lang.de.php
@@ -19,7 +19,7 @@ $lang['footer']['cancel'] = 'Abbrechen';
$lang['footer']['hibp_nok'] = 'Übereinstimmung gefunden! Dieses Passwort ist potentiell gefährlich!';
$lang['footer']['hibp_ok'] = 'Keine Übereinstimmung gefunden.';
-$lang['danger']['transport_dest_exists'] = "Transport Maps Ziel existiert bereits";
+$lang['danger']['transport_dest_exists'] = 'Transport Maps Ziel "%s" existiert bereits';
$lang['danger']['unlimited_quota_acl'] = "Unendliche Quota untersagt durch ACL";
$lang['danger']['mysql_error'] = "MySQL Fehler: %s";
$lang['danger']['redis_error'] = "Redis Fehler: %s";
@@ -31,7 +31,7 @@ $lang['success']['verified_u2f_login'] = "U2F Anmeldung verifiziert";
$lang['success']['verified_yotp_login'] = "Yubico OTP Anmeldung verifiziert";
$lang['danger']['yotp_verification_failed'] = "Yubico OTP Verifizierung fehlgeschlagen: %s";
$lang['danger']['ip_list_empty'] = "Liste erlaubter IPs darf nicht leer sein";
-$lang['danger']['invalid_destination'] = "Ziel-Format ist ungültig";
+$lang['danger']['invalid_destination'] = 'Ziel-Format "%s" ist ungültig';
$lang['danger']['invalid_nexthop'] = "Next Hop ist ungültig";
$lang['danger']['invalid_nexthop_authenticated'] = 'Dieser Next Hop existiert bereits mit abweichenden Authentifizierungsdaten. Die bestehenden Authentifizierungsdaten dieses "Next Hops" müssen vorab angepasst werden.';
$lang['danger']['next_hop_interferes'] = "%s verhindert das Hinzufügen von Next Hop %s";
@@ -857,3 +857,5 @@ $lang['edit']['mbox_rl_info'] = 'Dieses Limit wird auf den SASL Loginnamen angew
$lang['add']['relayhost_wrapped_tls_info'] = 'Bitte <b>keine</b> TLS-wrapped Ports verwenden (etwa SMTPS via Port 465/tcp).<br>
Der Transport wird stattdessen STARTTLS anfordern, um TLS zu verwenden. TLS kann unter "TLS Policy Maps" erzwungen werden.';
+
+$lang['admin']['transport_dest_format'] = 'Syntax: example.org, .example.org, *, [email protected] (mehrere Werte getrennt durch Komma einzugeben)';
\ No newline at end of file
diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php
index 3d6f599f..2d762b60 100644
--- a/data/web/lang/lang.en.php
+++ b/data/web/lang/lang.en.php
@@ -20,7 +20,7 @@ $lang['footer']['cancel'] = 'Cancel';
$lang['footer']['hibp_nok'] = 'Matched! This is a potentially dangerous password!';
$lang['footer']['hibp_ok'] = 'No match found.';
-$lang['danger']['transport_dest_exists'] = "Transport destination exists";
+$lang['danger']['transport_dest_exists'] = 'Transport destination "%s" exists';
$lang['danger']['unlimited_quota_acl'] = "Unlimited quota prohibited by ACL";
$lang['danger']['mysql_error'] = "MySQL error: %s";
$lang['danger']['redis_error'] = "Redis error: %s";
@@ -32,7 +32,7 @@ $lang['success']['verified_u2f_login'] = "Verified U2F login";
$lang['success']['verified_yotp_login'] = "Verified Yubico OTP login";
$lang['danger']['yotp_verification_failed'] = "Yubico OTP verification failed: %s";
$lang['danger']['ip_list_empty'] = "List of allowed IPs cannot be empty";
-$lang['danger']['invalid_destination'] = "Destination format is invalid";
+$lang['danger']['invalid_destination'] = 'Destination format "%s" is invalid';
$lang['danger']['invalid_nexthop'] = "Next hop format is invalid";
$lang['danger']['invalid_nexthop_authenticated'] = "Next hop exists with different credentials, please update the existing credentials for this next hop first.";
$lang['danger']['next_hop_interferes'] = "%s interferes with nexthop %s";
@@ -887,3 +887,4 @@ $lang['edit']['mbox_rl_info'] = 'This rate limit is applied on the SASL login na
$lang['add']['relayhost_wrapped_tls_info'] = 'Please do <b>not</b> use TLS-wrapped ports (mostly used on port 465).<br>
Use any non-wrapped port and issue STARTTLS. A TLS policy to enforce TLS can be created in "TLS policy maps".';
+$lang['admin']['transport_dest_format'] = 'Syntax: example.org, .example.org, *, [email protected] (multiple values can be comma-separated)';