2
0
Эх сурвалжийг харах

[Web] Allow to add external sender addresses, can be disabled by domain admin ACL and is disabled by default

andryyy 6 жил өмнө
parent
commit
893ce5a789

+ 16 - 0
data/web/edit.php

@@ -578,9 +578,18 @@ if (isset($_SESSION['mailcow_cc_role'])) {
                 <?php
               endforeach;
 
+              // Generated here, but used in extended_sender_acl
+              if (!empty($sender_acl_handles['external_sender_aliases'])) {
+                $ext_sender_acl = implode(', ', $sender_acl_handles['external_sender_aliases']);
+              }
+              else {
+                $ext_sender_acl = '';
+              }
+
               ?>
               </select>
               <div style="display:none" id="sender_acl_disabled"><?=$lang['edit']['sender_acl_disabled'];?></div>
+              <small class="help-block"><?=$lang['edit']['sender_acl_info'];?></small>
             </div>
           </div>
           <div class="form-group">
@@ -595,6 +604,13 @@ if (isset($_SESSION['mailcow_cc_role'])) {
             <input type="password" class="form-control" name="password2">
             </div>
           </div>
+          <div data-acl="<?=$_SESSION['acl']['extend_sender_acl'];?>" class="form-group">
+            <label class="control-label col-sm-2" for="extended_sender_acl"><?=$lang['edit']['extended_sender_acl'];?></label>
+            <div class="col-sm-10">
+            <input type="text" class="form-control" name="extended_sender_acl" value="<?=empty($ext_sender_acl) ? '' : $ext_sender_acl; ?>" placeholder="user1@example.com, user2@example.org, @example.com, ...">
+            <small class="help-block"><?=$lang['edit']['extended_sender_acl_info'];?></small>
+            </div>
+          </div>
           <div class="form-group">
             <div class="col-sm-offset-2 col-sm-10">
               <div class="checkbox">

+ 2 - 2
data/web/inc/functions.inc.php

@@ -552,8 +552,8 @@ function update_sogo_static_view() {
     WHERE TABLE_NAME = 'sogo_view'");
   $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
   if ($num_results != 0) {
-    $stmt = $pdo->query("REPLACE INTO _sogo_static_view (`c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `kind`, `multiple_bookings`)
-      SELECT `c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `kind`, `multiple_bookings` from sogo_view");
+    $stmt = $pdo->query("REPLACE INTO _sogo_static_view (`c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings`)
+      SELECT `c_uid`, `domain`, `c_name`, `c_password`, `c_cn`, `mail`, `aliases`, `ad_aliases`, `ext_acl`, `kind`, `multiple_bookings` from sogo_view");
     $stmt = $pdo->query("DELETE FROM _sogo_static_view WHERE `c_uid` NOT IN (SELECT `username` FROM `mailbox` WHERE `active` = '1');");
   }
   flush_memcached();

+ 112 - 4
data/web/inc/functions.mailbox.inc.php

@@ -409,6 +409,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             );
             return false;
           }
+          $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `external` = 1 AND `send_as` LIKE :domain");
+          $stmt->execute(array(
+            ':domain' => '%@' . $domain
+          ));
           $stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `defquota`, `maxquota`, `quota`, `backupmx`, `gal`, `active`, `relay_all_recipients`)
             VALUES (:domain, :description, :aliases, :mailboxes, :defquota, :maxquota, :quota, :backupmx, :gal, :active, :relay_all_recipients)");
           $stmt->execute(array(
@@ -710,6 +714,18 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               );
               continue;
             }
+            $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+              WHERE `domain`= :target_domain AND `backupmx` = '1'");
+            $stmt->execute(array(':target_domain' => $target_domain));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results == 0) {
+              $_SESSION['return'][] = array(
+                'type' => 'danger',
+                'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
+                'msg' => array('targetd_relay_domain', htmlspecialchars($target_domain))
+              );
+              continue;
+            }
             $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `alias_domain`= :alias_domain
               UNION
               SELECT `domain` FROM `domain` WHERE `domain`= :alias_domain_in_domain");
@@ -723,6 +739,10 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               );
               continue;
             }
+            $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `external` = 1 AND `send_as` LIKE :domain");
+            $stmt->execute(array(
+              ':domain' => '%@' . $domain
+            ));
             $stmt = $pdo->prepare("INSERT INTO `alias_domain` (`alias_domain`, `target_domain`, `active`)
               VALUES (:alias_domain, :target_domain, :active)");
             $stmt->execute(array(
@@ -2093,6 +2113,75 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               );
               continue;
             }
+            $extra_acls = array();
+            if (isset($_data['extended_sender_acl'])) {
+              if (!isset($_SESSION['acl']['extend_sender_acl']) || $_SESSION['acl']['extend_sender_acl'] != "1" ) {
+                $_SESSION['return'][] = array(
+                  'type' => 'danger',
+                  'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
+                  'msg' => 'access_denied'
+                );
+                return false;
+              }
+              $extra_acls = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['extended_sender_acl']));
+              foreach ($extra_acls as $i => &$extra_acl) {
+                if (empty($extra_acl)) {
+                  continue;
+                }
+                if (substr($extra_acl, 0, 1) === "@") {
+                  $extra_acl = ltrim($extra_acl, '@');
+                }
+                if (!filter_var($extra_acl, FILTER_VALIDATE_EMAIL) && !is_valid_domain_name($extra_acl)) {
+                  $_SESSION['return'][] = array(
+                    'type' => 'danger',
+                    'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
+                    'msg' => array('extra_acl_invalid', htmlspecialchars($extra_acl))
+                  );
+                  unset($extra_acls[$i]);
+                  continue;
+                }
+                $domains = array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains'));
+                if (filter_var($extra_acl, FILTER_VALIDATE_EMAIL)) {
+                  $extra_acl_domain = idn_to_ascii(substr(strstr($extra_acl, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46);
+                  if (in_array($extra_acl_domain, $domains)) {
+                    $_SESSION['return'][] = array(
+                      'type' => 'danger',
+                      'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
+                      'msg' => array('extra_acl_invalid_domain', $extra_acl_domain)
+                    );
+                    unset($extra_acls[$i]);
+                    continue;
+                  }
+                }
+                else {
+                  if (in_array($extra_acl, $domains)) {
+                    $_SESSION['return'][] = array(
+                      'type' => 'danger',
+                      'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
+                      'msg' => array('extra_acl_invalid_domain', $extra_acl_domain)
+                    );
+                    unset($extra_acls[$i]);
+                    continue;
+                  }
+                  $extra_acl = '@' . $extra_acl;
+                }
+              }
+              $extra_acls = array_filter($extra_acls);
+              $extra_acls = array_values($extra_acls);
+              $extra_acls = array_unique($extra_acls);
+              $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `external` = 1 AND `logged_in_as` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              foreach ($extra_acls as $sender_acl_external) {
+                $stmt = $pdo->prepare("INSERT INTO `sender_acl` (`send_as`, `logged_in_as`, `external`)
+                  VALUES (:sender_acl, :username, 1)");
+                $stmt->execute(array(
+                  ':sender_acl' => $sender_acl_external,
+                  ':username' => $username
+                ));
+              }
+            }
             if (isset($_data['sender_acl'])) {
               // Get sender_acl items set by admin
               $sender_acl_admin = array_merge(
@@ -2181,7 +2270,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
                 $sender_acl_merged = array_merge($sender_acl_domain_admin, $sender_acl_admin);
                 // If merged array still contains "*", set it as only value
                 !in_array('*', $sender_acl_merged) ?: $sender_acl_merged = array('*');
-                $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
+                $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `external` = 0 AND `logged_in_as` = :username");
                 $stmt->execute(array(
                   ':username' => $username
                 ));
@@ -2204,7 +2293,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
                 }
               }
               else {
-                $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
+                $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `external` = 0 AND `logged_in_as` = :username");
                 $stmt->execute(array(
                   ':username' => $username
                 ));
@@ -2359,6 +2448,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
           $data['sender_acl_addresses']['rw']             = array();
           $data['sender_acl_addresses']['selectable']     = array();
           $data['fixed_sender_aliases']                   = array();
+          $data['external_sender_aliases']                = array();
           // Fixed addresses
           $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` REGEXP :goto AND `address` NOT LIKE '@%'");
           $stmt->execute(array(':goto' => '(^|,)'.$_data.'($|,)'));
@@ -2376,9 +2466,18 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               $data['fixed_sender_aliases'][] = $row['alias_domain_alias'];
             }
           }
+          // External addresses
+          $stmt = $pdo->prepare("SELECT `send_as` as `send_as_external` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as AND `external` = '1'");
+          $stmt->execute(array(':logged_in_as' => $_data));
+          $exernal_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+          while ($row = array_shift($exernal_rows)) {
+            if (!empty($row['send_as_external'])) {
+              $data['external_sender_aliases'][] = $row['send_as_external'];
+            }
+          }
           // Return array $data['sender_acl_domains/addresses']['ro'] with read-only objects
           // Return array $data['sender_acl_domains/addresses']['rw'] with read-write objects (can be deleted)
-          $stmt = $pdo->prepare("SELECT REPLACE(`send_as`, '@', '') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as AND (`send_as` LIKE '@%' OR `send_as` = '*')");
+          $stmt = $pdo->prepare("SELECT REPLACE(`send_as`, '@', '') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as AND `external` = '0' AND (`send_as` LIKE '@%' OR `send_as` = '*')");
           $stmt->execute(array(':logged_in_as' => $_data));
           $domain_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
           while ($domain_row = array_shift($domain_rows)) {
@@ -2397,7 +2496,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               $data['sender_acl_domains']['rw'][] = $domain_row['send_as'];
             }
           }
-          $stmt = $pdo->prepare("SELECT `send_as` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as AND (`send_as` NOT LIKE '@%' AND `send_as` != '*')");
+          $stmt = $pdo->prepare("SELECT `send_as` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as AND `external` = '0' AND (`send_as` NOT LIKE '@%' AND `send_as` != '*')");
           $stmt->execute(array(':logged_in_as' => $_data));
           $address_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
           while ($address_row = array_shift($address_rows)) {
@@ -2414,12 +2513,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             WHERE `domain` NOT IN (
               SELECT REPLACE(`send_as`, '@', '') FROM `sender_acl` 
                 WHERE `logged_in_as` = :logged_in_as1
+                  AND `external` = '0'
                   AND `send_as` LIKE '@%')
             UNION
             SELECT '*' FROM `domain`
               WHERE '*' NOT IN (
                 SELECT `send_as` FROM `sender_acl`  
                   WHERE `logged_in_as` = :logged_in_as2
+                    AND `external` = '0'
               )");
           $stmt->execute(array(
             ':logged_in_as1' => $_data,
@@ -2441,6 +2542,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
               AND `address` NOT IN (
                 SELECT `send_as` FROM `sender_acl` 
                   WHERE `logged_in_as` = :logged_in_as
+                    AND `external` = '0'
                     AND `send_as` NOT LIKE '@%')");
           $stmt->execute(array(
             ':logged_in_as' => $_data,
@@ -2909,7 +3011,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
             ':aliasdomain' => $_data,
           ));
           $row = $stmt->fetch(PDO::FETCH_ASSOC);
+          $stmt = $pdo->prepare("SELECT `backupmx` FROM `domain` WHERE `domain` = :target_domain");
+          $stmt->execute(array(
+            ':target_domain' => $row['target_domain']
+          ));
+          $row_parent = $stmt->fetch(PDO::FETCH_ASSOC);
           $aliasdomaindata['alias_domain'] = $row['alias_domain'];
+          $aliasdomaindata['parent_is_backupmx'] = $row_parent['backupmx'];
           $aliasdomaindata['target_domain'] = $row['target_domain'];
           $aliasdomaindata['active'] = $row['active'];
           $aliasdomaindata['rl'] = $rl;

+ 11 - 2
data/web/inc/init_db.inc.php

@@ -3,7 +3,7 @@ function init_db_schema() {
   try {
     global $pdo;
 
-    $db_version = "22082019_2140";
+    $db_version = "01092019_1240";
 
     $stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
     $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@@ -21,10 +21,16 @@ function init_db_schema() {
       AND active = '1'
       AND address NOT LIKE '@%'
       GROUP BY goto;",
+    // Unused at the moment - we cannot allow to show a foreign mailbox as sender address in SOGo, as SOGo does not like this
+    // We need to create delegation in SOGo AND set a sender_acl in mailcow to allow to send as user X
     "grouped_sender_acl" => "CREATE VIEW grouped_sender_acl (username, send_as_acl) AS
       SELECT logged_in_as, IFNULL(GROUP_CONCAT(send_as SEPARATOR ' '), '') AS send_as_acl FROM sender_acl
       WHERE send_as NOT LIKE '@%'
       GROUP BY logged_in_as;",
+    "grouped_sender_acl_external" => "CREATE VIEW grouped_sender_acl_external (username, send_as_acl) AS
+      SELECT logged_in_as, IFNULL(GROUP_CONCAT(send_as SEPARATOR ' '), '') AS send_as_acl FROM sender_acl
+      WHERE send_as NOT LIKE '@%' AND external = '1'
+      GROUP BY logged_in_as;",
     "grouped_domain_alias_address" => "CREATE VIEW grouped_domain_alias_address (username, ad_alias) AS
       SELECT username, IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ' '), '') AS ad_alias FROM mailbox
       LEFT OUTER JOIN alias_domain ON target_domain=domain
@@ -78,6 +84,7 @@ function init_db_schema() {
           // TODO -> use TEXT and check if SOGo login breaks on empty aliases
           "aliases" => "TEXT NOT NULL",
           "ad_aliases" => "VARCHAR(6144) NOT NULL DEFAULT ''",
+          "ext_acl" => "VARCHAR(6144) NOT NULL DEFAULT ''",
           "kind" => "VARCHAR(100) NOT NULL DEFAULT ''",
           "multiple_bookings" => "INT NOT NULL DEFAULT -1"
         ),
@@ -174,7 +181,8 @@ function init_db_schema() {
         "cols" => array(
           "id" => "INT NOT NULL AUTO_INCREMENT",
           "logged_in_as" => "VARCHAR(255) NOT NULL",
-          "send_as" => "VARCHAR(255) NOT NULL"
+          "send_as" => "VARCHAR(255) NOT NULL",
+          "external" => "TINYINT(1) NOT NULL DEFAULT '0'"
         ),
         "keys" => array(
           "primary" => array(
@@ -466,6 +474,7 @@ function init_db_schema() {
           "filters" => "TINYINT(1) NOT NULL DEFAULT '1'",
           "ratelimit" => "TINYINT(1) NOT NULL DEFAULT '1'",
           "spam_policy" => "TINYINT(1) NOT NULL DEFAULT '1'",
+          "extend_sender_acl" => "TINYINT(1) NOT NULL DEFAULT '0'",
           "unlimited_quota" => "TINYINT(1) NOT NULL DEFAULT '0'",
           "alias_domains" => "TINYINT(1) NOT NULL DEFAULT '0'",
           ),

+ 4 - 0
data/web/js/build/014-mailcow.js

@@ -146,6 +146,10 @@ $(document).ready(function() {
       $(this).find('button').each(function() {
         $(this).attr("disabled", true);
       });
+    } else if ($(this).hasClass('form-group')) {
+      $(this).find('input').each(function() {
+        $(this).attr("disabled", true);
+      });
     } else if ($(this).hasClass('btn')) {
       $(this).attr("disabled", true);
     } else if ($(this).attr('data-provide') == 'slider') {

+ 1 - 0
data/web/js/site/index.js

@@ -1,3 +1,4 @@
 $(document).ready(function() {
 	$('nav').hide();
+  localStorage.clear();
 });

+ 17 - 12
data/web/js/site/mailbox.js

@@ -282,7 +282,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         "placeholder": lang.filter_table
@@ -401,7 +401,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         //"container": "#tab-mailboxes.panel",
@@ -475,7 +475,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         "placeholder": lang.filter_table
@@ -547,7 +547,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         "placeholder": lang.filter_table
@@ -611,7 +611,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         "placeholder": lang.filter_table
@@ -681,7 +681,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         "placeholder": lang.filter_table
@@ -749,7 +749,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         "placeholder": lang.filter_table
@@ -843,7 +843,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         "placeholder": lang.filter_table
@@ -874,7 +874,7 @@ jQuery(function($){
       "columns": [
         {"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
         {"sorted": true,"name":"alias_domain","title":lang.alias,"style":{"width":"250px"}},
-        {"name":"target_domain","title":lang.target_domain},
+        {"name":"target_domain","title":lang.target_domain,"type":"html"},
         {"name":"active","filterable": false,"style":{"maxWidth":"50px","width":"70px"},"title":lang.active},
         {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"250px","width":"250px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
       ],
@@ -894,6 +894,11 @@ jQuery(function($){
               '<a href="#dnsInfoModal" class="btn btn-xs btn-info" data-toggle="modal" data-domain="' + encodeURIComponent(item.alias_domain) + '"><span class="glyphicon glyphicon-question-sign"></span> DNS</a></div>' +
               '</div>';
             item.chkbox = '<input type="checkbox" data-id="alias-domain" name="multi_select" value="' + encodeURIComponent(item.alias_domain) + '" />';
+            if(item.parent_is_backupmx == '1') {
+              item.target_domain = '<span><a href="/edit/domain/' + item.target_domain + '">' + item.target_domain + '</a> <div class="label label-warning">' + lang.alias_domain_backupmx + '</div></span>';
+            } else {
+              item.target_domain = '<span><a href="/edit/domain/' + item.target_domain + '">' + item.target_domain + '</a></span>';
+            }
           });
         }
       }),
@@ -907,7 +912,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         "placeholder": lang.filter_table
@@ -989,7 +994,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         "placeholder": lang.filter_table
@@ -1059,7 +1064,7 @@ jQuery(function($){
       },
       "filtering": {
         "enabled": true,
-        "delay": 100,
+        "delay": 1200,
         "position": "left",
         "connectors": false,
         "placeholder": lang.filter_table

+ 29 - 14
data/web/lang/lang.de.php

@@ -108,6 +108,7 @@ $lang['success']['resource_modified'] = "Änderungen an Ressource %s wurden gesp
 $lang['success']['object_modified'] = "Änderungen an Objekt %s wurden gespeichert";
 $lang['success']['f2b_modified'] = "Änderungen an Fail2ban-Parametern wurden gespeichert";
 $lang['danger']['targetd_not_found'] = 'Ziel-Domain %s nicht gefunden';
+$lang['danger']['targetd_relay_domain'] = 'Ziel-Domain %s ist eine Relay-Domain';
 $lang['success']['aliasd_added'] = 'Alias-Domain %s wurde angelegt';
 $lang['success']['aliasd_modified'] = 'Änderungen an Alias-Domain %s wurden gespeichert';
 $lang['success']['domain_modified'] = 'Änderungen an Domain %s wurden gespeichert';
@@ -209,7 +210,7 @@ $lang['user']['spamfilter_behavior'] = 'Bewertung';
 $lang['user']['spamfilter_green'] = 'Grün: Die Nachricht ist kein Spam';
 $lang['user']['spamfilter_yellow'] = 'Gelb: Die Nachricht ist vielleicht Spam, wird als Spam markiert und in den Junk-Ordner verschoben';
 $lang['user']['spamfilter_red'] = 'Rot: Die Nachricht ist eindeutig Spam und wird vom Server abgelehnt';
-$lang['user']['spamfilter_default_score'] = 'Standardwert:';
+$lang['user']['spamfilter_default_score'] = 'Standardwert';
 $lang['user']['spamfilter_hint'] = 'Der erste Wert beschreibt den "low spam score", der zweite Wert den "high spam score".';
 $lang['user']['spamfilter_table_domain_policy'] = "n.v. (Domainrichtlinie)";
 $lang['user']['waiting'] = "Warte auf Ausführung";
@@ -344,9 +345,9 @@ $lang['edit']['skipcrossduplicates'] = 'Duplikate auch über Ordner hinweg über
 $lang['add']['automap'] = 'Ordner automatisch mappen ("Sent items", "Sent" => "Sent" etc.)';
 $lang['add']['skipcrossduplicates'] = 'Duplikate auch über Ordner hinweg überspringen ("first come, first serve")';
 $lang['edit']['exclude'] = 'Elemente ausschließen (Regex)';
-$lang['edit']['max_mailboxes'] = 'Max. Mailboxanzahl:';
+$lang['edit']['max_mailboxes'] = 'Max. Mailboxanzahl';
 $lang['edit']['title'] = 'Objekt bearbeiten';
-$lang['edit']['target_address'] = 'Ziel-Adresse(n) <small>(getrennt durch Komma)</small>:';
+$lang['edit']['target_address'] = 'Ziel-Adresse(n) <small>(getrennt durch Komma)</small>';
 $lang['edit']['active'] = 'Aktiv';
 $lang['add']['gal'] = 'Globales Adressbuch';
 $lang['edit']['gal'] = 'Globales Adressbuch';
@@ -356,20 +357,20 @@ $lang['edit']['force_pw_update'] = 'Erzwinge Passwortänderung bei nächstem Log
 $lang['edit']['force_pw_update_info'] = 'Dem Benutzer wird lediglich der Zugang zur mailcow UI ermöglicht.';
 $lang['edit']['sogo_access'] = 'SOGo Zugriffsrecht';
 $lang['edit']['sogo_access_info'] = 'Zugriff auf SOGo erlauben oder verbieten. Diese Einstellung hat weder Einfluss auf den Zugang sonstiger Dienste noch entfernt sie ein vorhandenes SOGo Benutzerprofil.';
-$lang['edit']['target_domain'] = 'Ziel-Domain:';
-$lang['edit']['password'] = 'Passwort:';
-$lang['edit']['password_repeat'] = 'Passwort (Wiederholung):';
+$lang['edit']['target_domain'] = 'Ziel-Domain';
+$lang['edit']['password'] = 'Passwort';
+$lang['edit']['password_repeat'] = 'Passwort (Wiederholung)';
 $lang['edit']['domain_admin'] = 'Domain-Administrator bearbeiten';
 $lang['edit']['domain'] = 'Domain bearbeiten';
 $lang['edit']['edit_alias_domain'] = 'Alias-Domain bearbeiten';
 $lang['edit']['domains'] = 'Domains';
 $lang['edit']['alias'] = 'Alias bearbeiten';
 $lang['edit']['mailbox'] = 'Mailbox bearbeiten';
-$lang['edit']['description'] = 'Beschreibung:';
-$lang['edit']['max_aliases'] = 'Max. Aliasse:';
-$lang['edit']['max_quota'] = 'Max. Größe per Mailbox (MiB):';
-$lang['edit']['domain_quota'] = 'Domain Speicherplatz gesamt (MiB):';
-$lang['edit']['backup_mx_options'] = 'Backup MX Optionen:';
+$lang['edit']['description'] = 'Beschreibung';
+$lang['edit']['max_aliases'] = 'Max. Aliasse';
+$lang['edit']['max_quota'] = 'Max. Größe per Mailbox (MiB)';
+$lang['edit']['domain_quota'] = 'Domain Speicherplatz gesamt (MiB)';
+$lang['edit']['backup_mx_options'] = 'Backup MX Optionen';
 $lang['edit']['relay_domain'] = 'Diese Domain relayen';
 $lang['edit']['relay_all'] = 'Alle Empfänger-Adressen relayen';
 $lang['edit']['relay_all_info'] = '<small>Wenn <b>nicht</b> alle Empfänger-Adressen relayt werden sollen, müssen "blinde" Mailboxen für jede Adresse, die relayt werden soll, erstellen werden.</small>';
@@ -411,8 +412,17 @@ $lang['acl']['filters'] = 'Filter';
 $lang['acl']['ratelimit'] = 'Rate limit';
 $lang['acl']['recipient_maps'] = 'Empfängerumschreibungen';
 $lang['acl']['unlimited_quota'] = 'Unendliche Quota für Mailboxen';
+$lang['acl']['extend_sender_acl'] = 'Eingabe externer Absenderadressen erlauben';
 $lang['acl']['prohibited'] = 'Untersagt durch Richtlinie';
 
+$lang['edit']['extended_sender_acl'] = 'Externe Absenderadressen';
+$lang['edit']['extended_sender_acl_info'] = 'Der DKIM Domainkey der externen Absenderdomain sollte in diesen Server importiert werden, falls vorhanden.<br>
+  Wird SPF verwendet, muss diesem Server der Versand gestattet werden.<br>
+  Wird eine Domain oder Alias-Domain zu diesem Server hinzugefügt, die sich mit der externen Absenderadresse überschneidet, wird der externe Absender hier entfernt.<br>
+  Ein Eintrag @domain.tld erlaubt den Versand als *@domain.tld';
+$lang['edit']['sender_acl_info'] = 'Wird einem Mailbox-Benutzer A der Versand als Mailbox-Benutzer B gestattet, so erscheint der Absender <b>nicht</b> automatisch in SOGo zur Auswahl.<br>
+  In SOGo muss zusätzlich eine Delegation eingerichtet werden. Dieses Verhalten trifft nicht auf Alias-Adressen zu.';
+
 $lang['mailbox']['quarantine_notification'] = 'Quarantäne-Benachrichtigung';
 $lang['mailbox']['never'] = 'Niemals';
 $lang['mailbox']['hourly'] = 'Stündlich';
@@ -837,8 +847,8 @@ $lang['danger']['subject_empty'] = 'Betreff darf nicht leer sein';
 $lang['danger']['from_invalid'] = 'From address must be a valid email address';
 $lang['danger']['network_host_invalid'] = 'Netzwerk oder Host ungültig: %s';
 
-$lang['add']['mailbox_quota_def'] = 'Standard-Quota einer Mailbox:';
-$lang['edit']['mailbox_quota_def'] = 'Standard-Quota einer Mailbox:';
+$lang['add']['mailbox_quota_def'] = 'Standard-Quota einer Mailbox';
+$lang['edit']['mailbox_quota_def'] = 'Standard-Quota einer Mailbox';
 $lang['danger']['mailbox_defquota_exceeds_mailbox_maxquota'] = 'Standard-Quota überschreitet das Limit der maximal erlaubten Größe einer Mailbox';
 $lang['danger']['defquota_empty'] = 'Standard-Quota darf nicht 0 sein';
 $lang['mailbox']['mailbox_defquota'] = 'Standard-Quota';
@@ -858,4 +868,9 @@ $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, *, box@example.org (mehrere Werte getrennt durch Komma einzugeben)';
+$lang['admin']['transport_dest_format'] = 'Syntax: example.org, .example.org, *, box@example.org (mehrere Werte getrennt durch Komma einzugeben)';
+
+$lang['mailbox']['alias_domain_backupmx'] = 'Alias-Domain für Relay-Domain inaktiv';
+
+$lang['danger']['extra_acl_invalid'] = 'Externe Absenderadresse "%s" ist ungültig';
+$lang['danger']['extra_acl_invalid_domain'] = 'Externe Absenderadresse "%s" verwendet eine ungültige Domain';

+ 18 - 2
data/web/lang/lang.en.php

@@ -13,7 +13,7 @@ $lang['footer']['restarting_container'] = 'Restarting container, this may take a
 $lang['footer']['restart_container_info'] = '<b>Important:</b> A graceful restart may take a while to complete, please wait for it to finish.';
 
 $lang['footer']['confirm_delete'] = 'Confirm deletion';
-$lang['footer']['delete_these_items'] = 'Please confirm your changes to the following object id:';
+$lang['footer']['delete_these_items'] = 'Please confirm your changes to the following object id';
 $lang['footer']['delete_now'] = 'Delete now';
 $lang['footer']['cancel'] = 'Cancel';
 
@@ -112,6 +112,7 @@ $lang['success']['resource_modified'] = "Changes to mailbox %s have been saved";
 $lang['success']['object_modified'] = "Changes to object %s have been saved";
 $lang['success']['f2b_modified'] = "Changes to Fail2ban parameters have been saved";
 $lang['danger']['targetd_not_found'] = "Target domain %s not found";
+$lang['danger']['targetd_relay_domain'] = "Target domain %s is a relay domain";
 $lang['success']['aliasd_added'] = "Added alias domain %s";
 $lang['success']['aliasd_modified'] = "Changes to alias domain %s have been saved";
 $lang['success']['domain_modified'] = "Changes to domain %s have been saved";
@@ -213,7 +214,7 @@ $lang['user']['spamfilter_table_add'] = 'Add item';
 $lang['user']['spamfilter_green'] = 'Green: this message is not spam';
 $lang['user']['spamfilter_yellow'] = 'Yellow: this message may be spam, will be tagged as spam and moved to your junk folder';
 $lang['user']['spamfilter_red'] = 'Red: This message is spam and will be rejected by the server';
-$lang['user']['spamfilter_default_score'] = 'Default values:';
+$lang['user']['spamfilter_default_score'] = 'Default values';
 $lang['user']['spamfilter_hint'] = 'The first value describes the "low spam score", the second represents the "high spam score".';
 $lang['user']['spamfilter_table_domain_policy'] = "n/a (domain policy)";
 $lang['user']['waiting'] = "Waiting";
@@ -424,8 +425,17 @@ $lang['acl']['filters'] = 'Filters';
 $lang['acl']['ratelimit'] = 'Rate limit';
 $lang['acl']['recipient_maps'] = 'Recipient maps';
 $lang['acl']['unlimited_quota'] = 'Unlimited quota for mailboxes';
+$lang['acl']['extend_sender_acl'] = 'Allow to extend sender ACL by external addresses';
 $lang['acl']['prohibited'] = 'Prohibited by ACL';
 
+$lang['edit']['extended_sender_acl'] = 'External sender addresses';
+$lang['edit']['extended_sender_acl_info'] = 'A DKIM domain key should be imported, if available.<br>
+  Remember to add this server to the corresponding SPF TXT record.<br>
+  Whenever a domain or alias domain is added to this server, that overlaps with an external address, the external address is removed.<br>
+  Use @domain.tld to allow to send as *@domain.tld.';
+$lang['edit']['sender_acl_info'] = 'If mailbox user A is allowed to send as mailbox user B, the sender address is not automatically displayed as selectable "from" field in SOGo.<br>
+  Mailbox user A needs to create a delegation in SOGo to allow mailbox user b to select their address as sender. This behaviour does not apply to alias addresses.';
+
 $lang['mailbox']['quarantine_notification'] = 'Quarantine notifications';
 $lang['mailbox']['never'] = 'Never';
 $lang['mailbox']['hourly'] = 'Hourly';
@@ -888,3 +898,9 @@ $lang['add']['relayhost_wrapped_tls_info'] = 'Please do <b>not</b> use TLS-wrapp
 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, *, box@example.org (multiple values can be comma-separated)';
+
+$lang['mailbox']['alias_domain_backupmx'] = 'Alias domain inactive for relay domain';
+
+$lang['danger']['extra_acl_invalid'] = 'External sender address "%s" is invalid';
+$lang['danger']['extra_acl_invalid_domain'] = 'External sender "%s" uses an invalid domain';
+