NobGit
public nobgit read

NobMail

Based on mailcow: dockerized

Languages

Repository composition by tracked source files.

PHP
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

More structural changes

fd5a5908
andryyy <[email protected]> 9 years ago
data/Dockerfiles/rspamd/Dockerfile     |    3 +
 data/web/add.php                       |    6 +-
 data/web/admin.php                     |   55 +-
 data/web/css/edit.css                  |   24 +
 data/web/edit.php                      |  180 +-
 data/web/inc/footer.inc.php            |    3 +-
 data/web/inc/functions.inc.php         | 5382 ++++++--------------------------
 data/web/inc/functions.mailbox.inc.php | 3116 ++++++++++++++++++
 data/web/inc/functions.policy.inc.php  |  354 +++
 data/web/inc/header.inc.php            |    1 +
 data/web/inc/prerequisites.inc.php     |   16 +-
 data/web/inc/triggers.inc.php          |   67 +-
 data/web/js/admin.js                   |  261 +-
 data/web/js/api.js                     |  154 +
 data/web/js/edit.js                    |   83 +
 data/web/js/mailbox.js                 |   92 -
 data/web/js/user.js                    |  241 +-
 data/web/json_api.php                  |  515 ++-
 data/web/lang/lang.de.php              |    3 +-
 data/web/lang/lang.en.php              |    3 +-
 data/web/user.php                      |  350 +--
 21 files changed, 5710 insertions(+), 5199 deletions(-)
 create mode 100644 data/web/css/edit.css
 create mode 100644 data/web/inc/functions.mailbox.inc.php
 create mode 100644 data/web/inc/functions.policy.inc.php
 create mode 100644 data/web/js/api.js
 create mode 100644 data/web/js/edit.js

Diff

diff --git a/data/Dockerfiles/rspamd/Dockerfile b/data/Dockerfiles/rspamd/Dockerfile
index 4a2ea589..42f2afc2 100644
--- a/data/Dockerfiles/rspamd/Dockerfile
+++ b/data/Dockerfiles/rspamd/Dockerfile
@@ -17,6 +17,9 @@ COPY settings.conf /etc/rspamd/modules.d/settings.conf
 COPY antivirus.conf /etc/rspamd/modules.d/antivirus.conf
 COPY dkim_signing.lua /usr/share/rspamd/lua/dkim_signing.lua
 
+RUN apt-get autoremove --purge \
+	&& apt-get clean
+
 CMD /usr/bin/rspamd -f -u _rspamd -g _rspamd
 
 RUN rm -rf /tmp/* /var/tmp/*
diff --git a/data/web/add.php b/data/web/add.php
index d3c7ca95..b81a4e55 100644
--- a/data/web/add.php
+++ b/data/web/add.php
@@ -139,7 +139,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 						<div class="col-sm-10">
 							<select name="target_domain" id="target_domain" title="<?=$lang['add']['select'];?>" required>
 							<?php
-              foreach (mailbox_get_domains() as $domain) {
+              foreach (mailbox('get', 'domains') as $domain) {
 								echo "<option>".htmlspecialchars($domain)."</option>";
 							}
 							?>
@@ -177,7 +177,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 						<div class="col-sm-10">
 							<select id="addSelectDomain" name="domain" id="domain" required>
 							<?php
-              foreach (mailbox_get_domains() as $domain) {
+              foreach (mailbox('get', 'domains') as $domain) {
 								echo "<option>".htmlspecialchars($domain)."</option>";
 							}
 							?>
@@ -242,7 +242,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 						<div class="col-sm-10">
 							<select name="domain" id="domain" title="<?=$lang['add']['select'];?>" required>
 							<?php
-              foreach (mailbox_get_domains() as $domain) {
+              foreach (mailbox('get', 'domains') as $domain) {
 								echo "<option>".htmlspecialchars($domain)."</option>";
 							}
 							?>
diff --git a/data/web/admin.php b/data/web/admin.php
index a6f032be..8766b211 100644
--- a/data/web/admin.php
+++ b/data/web/admin.php
@@ -15,8 +15,15 @@ $tfa_data = get_tfa();
     <li role="presentation">
       <a href="#tab-config" aria-controls="tab-config" role="tab" data-toggle="tab"><?=$lang['admin']['configuration'];?></a>
     </li>
-    <li role="presentation">
-      <a href="#tab-logs" aria-controls="tab-logs" role="tab" data-toggle="tab"><?=$lang['admin']['logs'];?></a>
+    <li class="dropdown">
+    <a class="dropdown-toggle" data-toggle="dropdown" href="#">Logs
+    <span class="caret"></span></a>
+    <ul class="dropdown-menu">
+    <li role="presentation"><a href="#tab-postfix-logs" aria-controls="tab-postfix-logs" role="tab" data-toggle="tab">Postfix</a></li>
+    <li role="presentation"><a href="#tab-dovecot-logs" aria-controls="tab-dovecot-logs" role="tab" data-toggle="tab">Dovecot</a></li>
+    <li role="presentation"><a href="#tab-sogo-logs" aria-controls="tab-sogo-logs" role="tab" data-toggle="tab">SOGo</a></li>
+    <li role="presentation"><a href="#tab-rspamd-history" aria-controls="tab-rspamd-history" role="tab" data-toggle="tab">Rspamd</a></li>
+    </ul>
     </li>
   </ul>
 
@@ -113,7 +120,7 @@ $tfa_data = get_tfa();
               <div class="col-sm-10">
                 <select title="<?=$lang['admin']['search_domain_da'];?>" style="width:100%" name="domain[]" size="5" multiple>
                 <?php
-                foreach (mailbox_get_domains() as $domain) {
+                foreach (mailbox('get', 'domains') as $domain) {
                   echo "<option>".htmlspecialchars($domain)."</option>";
                 }
                 ?>
@@ -163,7 +170,7 @@ $tfa_data = get_tfa();
           </div>
         </div>
         <?php
-        foreach(mailbox_get_domains() as $domain) {
+        foreach(mailbox('get', 'domains') as $domain) {
             if (!empty($dkim = dkim_get_key_details($domain))) {
           ?>
             <div class="row">
@@ -192,7 +199,7 @@ $tfa_data = get_tfa();
           </div>
           <?php
           }
-          foreach(mailbox_get_alias_domains($domain) as $alias_domain) {
+          foreach(mailbox('get', 'alias_domains') as $alias_domain) {
             if (!empty($dkim = dkim_get_key_details($alias_domain))) {
             ?>
               <div class="row">
@@ -297,37 +304,43 @@ $tfa_data = get_tfa();
     </div>
   </div>
 
-  <div role="tabpanel" class="tab-pane" id="tab-logs">
+  <div role="tabpanel" class="tab-pane" id="tab-postfix-logs">
     <div class="panel panel-default">
-      <div class="panel-heading">Dovecot
+      <div class="panel-heading">Postfix
         <div class="btn-group pull-right">
           <a class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['admin']['action'];?> <span class="caret"></span></a>
           <ul class="dropdown-menu">
-            <li><a href="#" id="refresh_dovecot_log"><?=$lang['admin']['refresh'];?></a></li>
+            <li><a href="#" id="refresh_postfix_log"><?=$lang['admin']['refresh'];?></a></li>
           </ul>
         </div>
       </div>
       <div class="panel-body">
         <div class="table-responsive">
-          <table class="table table-striped" id="dovecot_log"></table>
+          <table class="table table-striped" id="postfix_log"></table>
         </div>
       </div>
     </div>
+  </div>
+
+  <div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
     <div class="panel panel-default">
-      <div class="panel-heading">Postfix
+      <div class="panel-heading">Dovecot
         <div class="btn-group pull-right">
           <a class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['admin']['action'];?> <span class="caret"></span></a>
           <ul class="dropdown-menu">
-            <li><a href="#" id="refresh_postfix_log"><?=$lang['admin']['refresh'];?></a></li>
+            <li><a href="#" id="refresh_dovecot_log"><?=$lang['admin']['refresh'];?></a></li>
           </ul>
         </div>
       </div>
       <div class="panel-body">
         <div class="table-responsive">
-          <table class="table table-striped" id="postfix_log"></table>
+          <table class="table table-striped" id="dovecot_log"></table>
         </div>
       </div>
     </div>
+  </div>
+
+  <div role="tabpanel" class="tab-pane" id="tab-sogo-logs">
     <div class="panel panel-default">
       <div class="panel-heading">SOGo
         <div class="btn-group pull-right">
@@ -345,6 +358,24 @@ $tfa_data = get_tfa();
     </div>
   </div>
 
+  <div role="tabpanel" class="tab-pane" id="tab-rspamd-history">
+    <div class="panel panel-default">
+      <div class="panel-heading">Rspamd history
+        <div class="btn-group pull-right">
+          <a class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['admin']['action'];?> <span class="caret"></span></a>
+          <ul class="dropdown-menu">
+            <li><a href="#" id="refresh_rspamd_history"><?=$lang['admin']['refresh'];?></a></li>
+          </ul>
+        </div>
+      </div>
+      <div class="panel-body">
+        <div class="table-responsive">
+          <table class="table table-striped" id="rspamd_history"></table>
+        </div>
+      </div>
+    </div>
+  </div>
+
   </div>
 </div> <!-- /container -->
 <script type='text/javascript'>
diff --git a/data/web/css/edit.css b/data/web/css/edit.css
new file mode 100644
index 00000000..605a1010
--- /dev/null
+++ b/data/web/css/edit.css
@@ -0,0 +1,24 @@
+table.footable>tbody>tr.footable-empty>td {
+  font-size:15px !important;
+  font-style:italic;
+}
+.pagination a {
+  text-decoration: none !important;
+}
+.panel panel-default {
+  overflow: visible !important;
+}
+.table-responsive {
+  overflow: visible !important;
+}
+.footer-add-item {
+  display:block;
+  text-align: center;
+  font-style: italic;
+  padding: 10px;
+  background: #F5F5F5;
+}
+.mass-actions-user {
+  user-select: none;
+  padding:10px 0 10px 0;
+}
diff --git a/data/web/edit.php b/data/web/edit.php
index 9f711dc2..b13626ea 100644
--- a/data/web/edit.php
+++ b/data/web/edit.php
@@ -20,7 +20,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 		if (isset($_GET["alias"]) &&
 			!empty($_GET["alias"])) {
 				$alias = $_GET["alias"];
-        $result = mailbox_get_alias_details($alias);
+        $result = mailbox('get', 'alias_details', $alias);
 				if (!empty($result)) {
 				?>
 					<h4><?=$lang['edit']['alias'];?></h4>
@@ -138,7 +138,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 		is_valid_domain_name($_GET["domain"]) &&
 		!empty($_GET["domain"])) {
 			$domain = $_GET["domain"];
-      $result = mailbox_get_domain_details($domain);
+      $result = mailbox('get', 'domain_details', $domain);
 			if (!empty($result)) {
 			?>
 				<h4><?=$lang['edit']['domain'];?></h4>
@@ -225,119 +225,54 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 		<hr>
 		<div class="row">
 			<div class="col-sm-6">
-				<h4><span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span> <?=$lang['user']['spamfilter_wl'];?></h4>
-				<p><?=$lang['user']['spamfilter_wl_desc'];?></p>
-				<div class="row">
-					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_rule'];?></b></div>
-					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div>
-				</div>
-				<?php
-        $get_policy_list = get_policy_list($domain);
-				if (empty($get_policy_list['whitelist'])):
-				?>
-					<div class="row">
-						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
-					</div>
-				<?php
-				else:
-          foreach($get_policy_list['whitelist'] as $wl):
-          ?>
-          <div class="row striped">
-            <form class="form-inline" method="post">
-            <div class="col-xs-6"><code><?=$wl['value'];?></code></div>
-            <div class="col-xs-6">
-              <?php
-              if ($wl['object'] == $domain):
-              ?>
-							<input type="hidden" name="delete_prefid" value="<?=$wl['prefid'];?>">
-							<input type="hidden" name="delete_policy_list_item">
-							<input type="hidden" name="domain" value="<?=$domain;?>">
-                <a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
-              <?php
-              else:
-              ?>
-                <span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
-              <?php
-              endif;
-              ?>
-            </div>
-            </form>
+				<h4><?=$lang['user']['spamfilter_wl'];?></h4>
+        <p><?=$lang['user']['spamfilter_wl_desc'];?></p>
+        <div class="table-responsive">
+          <table class="table table-striped table-condensed" id="wl_policy_domain_table"></table>
+        </div>
+        <div class="mass-actions-user">
+          <div class="btn-group">
+            <a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="policy_wl_domain" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
+            <a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_wl_domain" data-api-url='delete/policy_mailbox' href="#"><?=$lang['mailbox']['remove'];?></a></li>
+            </ul>
           </div>
-          <?php
-          endforeach;
-        endif;
-				?>
-				<hr style="margin:5px 0px 7px 0px">
-				<div class="row">
-					<form class="form-inline" method="post">
-					<div class="col-xs-6">
-						<input type="text" class="form-control input-sm" name="object_from" id="object_from" placeholder="*@example.org" required>
-						<input type="hidden" name="object_list" value="wl">
-						<input type="hidden" name="domain" value="<?=$domain;?>">
-					</div>
-					<div class="col-xs-6">
-						<button type="submit" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
-					</div>
-					</form>
-				</div>
-			</div>
+        </div>
+        <form class="form-inline" method="post">
+          <div class="input-group">
+            <input type="text" class="form-control" name="object_from" id="object_from" placeholder="*@example.org" required>
+            <input type="hidden" name="domain" value="<?= $domain ;?>">
+            <input type="hidden" name="object_list" value="wl">
+            <span class="input-group-btn">
+              <button type="submit" id="add_policy_list_item" name="add_policy_list_item" class="btn btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
+            </span>
+          </div>
+        </form>
+      </div>
 			<div class="col-sm-6">
-				<h4><span class="glyphicon glyphicon-thumbs-down" aria-hidden="true"></span> <?=$lang['user']['spamfilter_bl'];?></h4>
-				<p><?=$lang['user']['spamfilter_bl_desc'];?></p>
-				<div class="row">
-					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_rule'];?></b></div>
-					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div>
-				</div>
-				<?php
-				if (empty($get_policy_list['blacklist'])):
-				?>
-					<div class="row">
-						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
-					</div>
-				<?php
-				else:
-          foreach($get_policy_list['blacklist'] as $bl):
-          ?>
-          <div class="row striped">
-            <form class="form-inline" method="post">
-            <div class="col-xs-6"><code><?=$bl['value'];?></code></div>
-            <div class="col-xs-6">
-							<input type="hidden" name="delete_prefid" value="<?=$bl['prefid'];?>">
-              <?php
-              if ($bl['object'] == $domain):
-              ?>
-								<input type="hidden" name="delete_policy_list_item">
-								<input type="hidden" name="domain" value="<?=$domain;?>">
-                <a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
-              <?php
-              else:
-              ?>
-                <span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
-              <?php
-              endif;
-              ?>
-            </div>
-            </form>
+				<h4><?=$lang['user']['spamfilter_bl'];?></h4>
+        <p><?=$lang['user']['spamfilter_bl_desc'];?></p>
+        <div class="table-responsive">
+          <table class="table table-striped table-condensed" id="bl_policy_domain_table"></table>
+        </div>
+        <div class="mass-actions-user">
+          <div class="btn-group">
+            <a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="policy_bl_domain" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
+            <a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_bl_domain" data-api-url='delete/policy_mailbox' href="#"><?=$lang['mailbox']['remove'];?></a></li>
+            </ul>
           </div>
-          <?php
-          endforeach;
-        endif;
-				?>
-				<hr style="margin:5px 0px 7px 0px">
-				<div class="row">
-					<form class="form-inline" method="post">
-					<div class="col-xs-6">
-						<input type="text" class="form-control input-sm" name="object_from" id="object_from" placeholder="*@example.org" required>
-						<input type="hidden" name="object_list" value="bl">
-						<input type="hidden" name="domain" value="<?=$domain;?>">
-					</div>
-					<div class="col-xs-6">
-						<button type="submit" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
-					</div>
-					</form>
-				</div>
-			</div>
-		</div>
+        </div>
+        <form class="form-inline" method="post">
+          <div class="input-group">
+            <input type="text" class="form-control" name="object_from" id="object_from" placeholder="*@example.org" required>
+            <input type="hidden" name="domain" value="<?= $domain ;?>">
+            <input type="hidden" name="object_list" value="bl">
+            <span class="input-group-btn">
+              <button type="submit" id="add_policy_list_item" name="add_policy_list_item" class="btn btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
+            </span>
+          </div>
+        </form>
+      </div>
+    </div>
         <?php
 			}
 			else {
@@ -350,7 +285,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 		is_valid_domain_name($_GET["aliasdomain"]) &&
 		!empty($_GET["aliasdomain"])) {
 			$alias_domain = $_GET["aliasdomain"];
-      $result = mailbox_get_alias_domain_details($alias_domain);
+      $result = mailbox('get', 'alias_domain_details', $alias_domain);
       if (!empty($result)) {
 			?>
 				<h4><?=$lang['edit']['edit_alias_domain'];?></h4>
@@ -399,7 +334,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 	}
 	elseif (isset($_GET['mailbox']) && filter_var($_GET["mailbox"], FILTER_VALIDATE_EMAIL) && !empty($_GET["mailbox"])) {
     $mailbox = $_GET["mailbox"];
-    $result = mailbox_get_mailbox_details($mailbox);
+    $result = mailbox('get', 'mailbox_details', $mailbox);
     if (!empty($result)) {
       ?>
       <h4><?=$lang['edit']['mailbox'];?></h4>
@@ -426,7 +361,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
           <div class="col-sm-10">
             <select data-width="100%" style="width:100%" id="sender_acl" name="sender_acl[]" size="10" multiple>
             <?php
-            $sender_acl_handles = mailbox_get_sender_acl_handles($mailbox);
+            $sender_acl_handles = mailbox('get', 'sender_acl_handles', $mailbox);
 
             foreach ($sender_acl_handles['sender_acl_domains']['ro'] as $domain):
               ?>
@@ -504,7 +439,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
   }
 	elseif (isset($_GET['resource']) && filter_var($_GET["resource"], FILTER_VALIDATE_EMAIL) && !empty($_GET["resource"])) {
 			$resource = $_GET["resource"];
-      $result = mailbox_get_resource_details($resource);
+      $result = mailbox('get', 'resource_details', $resource);
       if (!empty($result)) {
         ?>
 				<h4><?=$lang['edit']['resource'];?></h4>
@@ -566,7 +501,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] ==
 	if (isset($_GET['syncjob']) &&
     is_numeric($_GET['syncjob'])) {
 			$id = $_GET["syncjob"];
-      $result = get_syncjob_details($id);
+      $result = mailbox('get', 'syncjob_details', $id);
       if (!empty($result)) {
 			?>
 				<h4><?=$lang['edit']['syncjob'];?></h4>
@@ -686,6 +621,17 @@ else {
 	</div>
 <a href="<?=$_SESSION['return_to'];?>">&#8592; <?=$lang['edit']['previous'];?></a>
 </div> <!-- /container -->
+<script type='text/javascript'>
+<?php
+$lang_user = json_encode($lang['user']);
+echo "var lang = ". $lang_user . ";\n";
+echo "var table_for_domain = '". ((isset($domain)) ? $domain : null) . "';\n";
+echo "var csrf_token = '". $_SESSION['CSRF']['TOKEN'] . "';\n";
+echo "var pagination_size = '". $PAGINATION_SIZE . "';\n";
+?>
+</script>
+<script src="js/footable.min.js"></script>
+<script src="js/edit.js"></script>
 <?php
 require_once("inc/footer.inc.php");
 ?>
diff --git a/data/web/inc/footer.inc.php b/data/web/inc/footer.inc.php
index eb2d9ce8..d364a4df 100644
--- a/data/web/inc/footer.inc.php
+++ b/data/web/inc/footer.inc.php
@@ -31,7 +31,7 @@ endif;
       <h4 class="modal-title"><?= $lang['footer']['confirm_delete']; ?></h4>
     </div>
     <div class="modal-body">
-      <p><?= $lang['footer']['delete_these_items']; ?></p>
+      <p id="DeleteText"><?= $lang['footer']['delete_these_items']; ?></p>
       <ul id="ItemsToDelete"></ul>
       <hr>
       <button class="btn btn-sm btn-danger" id="IsConfirmed"><?= $lang['footer']['delete_now']; ?></button>
@@ -48,6 +48,7 @@ endif;
 <script src="/js/bootstrap-select.min.js"></script>
 <script src="/js/notifications.min.js"></script>
 <script src="/js/u2f-api.js"></script>
+<script src="/js/api.js"></script>
 <script>
 // Select language and reopen active URL without POST
 function setLang(sel) {
diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php
index 40e6a86a..28f76bfd 100644
--- a/data/web/inc/functions.inc.php
+++ b/data/web/inc/functions.inc.php
@@ -278,209 +278,6 @@ function edit_admin_account($postarray) {
 		'msg' => sprintf($lang['success']['admin_modified'])
 	);
 }
-function set_time_limited_aliases($postarray) {
-	global $lang;
-	global $pdo;
-  (isset($postarray['username'])) ? $username = $postarray['username'] : $username = $_SESSION['mailcow_cc_username'];
-
-  if ($_SESSION['mailcow_cc_role'] != "user" &&
-    $_SESSION['mailcow_cc_role'] != "admin") {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-  }
-  if (filter_var($username, FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-  }
-
-	try {
-    $stmt = $pdo->prepare("SELECT `domain` FROM `mailbox` WHERE `username` = :username");
-    $stmt->execute(array(':username' => $username));
-    $domain = $stmt->fetch(PDO::FETCH_ASSOC)['domain'];
-  }
-  catch (PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-
-	switch ($postarray["set_time_limited_aliases"]) {
-		case "generate":
-			if (!is_numeric($postarray["validity"]) || $postarray["validity"] > 672) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => sprintf($lang['danger']['validity_missing'])
-				);
-				return false;
-			}
-			$validity = strtotime("+".$postarray["validity"]." hour"); 
-			$letters = 'abcefghijklmnopqrstuvwxyz1234567890';
-			$random_name = substr(str_shuffle($letters), 0, 24);
-			try {
-				$stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `goto`, `validity`) VALUES
-					(:address, :goto, :validity)");
-				$stmt->execute(array(
-					':address' => $random_name . '@' . $domain,
-					':goto' => $username,
-					':validity' => $validity
-				));
-			}
-			catch (PDOException $e) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'MySQL: '.$e
-				);
-				return false;
-			}
-			$_SESSION['return'] = array(
-				'type' => 'success',
-				'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
-			);
-		break;
-		case "deleteall":
-			try {
-				$stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `goto` = :username");
-				$stmt->execute(array(
-					':username' => $username
-				));
-			}
-			catch (PDOException $e) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'MySQL: '.$e
-				);
-				return false;
-			}	
-			$_SESSION['return'] = array(
-				'type' => 'success',
-				'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
-			);
-		break;
-		case "delete":
-			if (empty($postarray['item']) || !filter_var($postarray['item'], FILTER_VALIDATE_EMAIL)) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => sprintf($lang['danger']['access_denied'])
-				);
-				return false;
-			}
-      $item	= $postarray['item'];
-			try {
-				$stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `goto` = :username AND `address` = :item");
-				$stmt->execute(array(
-					':username' => $username,
-					':item' => $item
-				));
-			}
-			catch (PDOException $e) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'MySQL: '.$e
-				);
-				return false;
-			}	
-			$_SESSION['return'] = array(
-				'type' => 'success',
-				'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
-			);
-		break;
-		case "extendall":
-			try {
-				$stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = (`validity` + 3600) WHERE
-          `goto` = :username AND
-					`validity` >= :validity");
-				$stmt->execute(array(
-					':username' => $username,
-					':validity' => time(),
-				));
-			}
-			catch (PDOException $e) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'MySQL: '.$e
-				);
-				return false;
-			}
-			$_SESSION['return'] = array(
-				'type' => 'success',
-				'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
-			);
-		break;
-		case "extend":
-			if (empty($postarray['item']) || !filter_var($postarray['item'], FILTER_VALIDATE_EMAIL)) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => sprintf($lang['danger']['access_denied'])
-				);
-				return false;
-			}
-      $item	= $postarray['item'];
-			try {
-				$stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = (`validity` + 3600) WHERE 
-          `goto` = :username AND
-					`address` = :item AND
-					`validity` >= :validity");
-				$stmt->execute(array(
-					':username' => $username,
-					':item' => $item,
-					':validity' => time(),
-				));
-			}
-			catch (PDOException $e) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'MySQL: '.$e
-				);
-				return false;
-			}
-			$_SESSION['return'] = array(
-				'type' => 'success',
-				'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
-			);
-		break;
-	}
-}
-function get_time_limited_aliases($username = null) {
-  // 'username' can be be set, if not, default to mailcow_cc_username
-  global $lang;
-	global $pdo;
-  $data = array();
-  if (isset($username) && filter_var($username, FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
-      return false;
-    }
-  }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
-  }
-  try {
-    $stmt = $pdo->prepare("SELECT `address`,
-      `goto`,
-      `validity`
-        FROM `spamalias`
-          WHERE `goto` = :username
-            AND `validity` >= :unixnow");
-    $stmt->execute(array(':username' => $username, ':unixnow' => time()));
-    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-  }
-  return $data;
-}
 function edit_user_account($postarray) {
 	global $lang;
 	global $pdo;
@@ -557,241 +354,230 @@ function edit_user_account($postarray) {
 		'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
 	);
 }
-function get_spam_score($username = null) {
+function user_get_alias_details($username) {
+	global $lang;
 	global $pdo;
-	$default = "5, 15";
-  if (isset($username) && filter_var($username, FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
-      return false;
+  if ($_SESSION['mailcow_cc_role'] == "user") {
+    $username	= $_SESSION['mailcow_cc_username'];
+  }
+  if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
+    return false;
+  }
+  try {
+    $data['address'] = $username;
+    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `aliases` FROM `alias`
+      WHERE `goto` REGEXP :username_goto
+      AND `address` NOT LIKE '@%'
+      AND `address` != :username_address");
+    $stmt->execute(array(':username_goto' => '(^|,)'.$username.'($|,)', ':username_address' => $username));
+    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($run)) {
+      $data['aliases'] = $row['aliases'];
+    }
+    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ', '), '&#10008;') AS `ad_alias` FROM `mailbox`
+      LEFT OUTER JOIN `alias_domain` on `target_domain` = `domain`
+        WHERE `username` = :username ;");
+    $stmt->execute(array(':username' => $username));
+    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($run)) {
+      $data['ad_alias'] = $row['ad_alias'];
+    }
+    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '&#10008;') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` NOT LIKE '@%';");
+    $stmt->execute(array(':username' => $username));
+    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($run)) {
+      $data['aliases_also_send_as'] = $row['send_as'];
+    }
+    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '&#10008;') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` LIKE '@%';");
+    $stmt->execute(array(':username' => $username));
+    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($run)) {
+      $data['aliases_send_as_all'] = $row['send_as'];
+    }
+    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') as `address` FROM `alias` WHERE `goto` REGEXP :username AND `address` LIKE '@%';");
+    $stmt->execute(array(':username' => '(^|,)'.$username.'($|,)'));
+    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($run)) {
+      $data['is_catch_all'] = $row['address'];
     }
+    return $data;
   }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+    return false;
   }
-	try {
-		$stmt = $pdo->prepare("SELECT `value` FROM `filterconf` WHERE `object` = :username AND
-			(`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
-		$stmt->execute(array(':username' => $username));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-	}
-	catch(PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
+}
+function is_valid_domain_name($domain_name) { 
+	if (empty($domain_name)) {
 		return false;
 	}
-	if (empty($num_results)) {
-		return $default;
-	}
-	else {
-		try {
-			$stmt = $pdo->prepare("SELECT `value` FROM `filterconf` WHERE `option` = 'highspamlevel' AND `object` = :username");
-			$stmt->execute(array(':username' => $username));
-			$highspamlevel = $stmt->fetch(PDO::FETCH_ASSOC);
-
-			$stmt = $pdo->prepare("SELECT `value` FROM `filterconf` WHERE `option` = 'lowspamlevel' AND `object` = :username");
-			$stmt->execute(array(':username' => $username));
-			$lowspamlevel = $stmt->fetch(PDO::FETCH_ASSOC);
-
-			return $lowspamlevel['value'].', '.$highspamlevel['value'];
-		}
-		catch(PDOException $e) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => 'MySQL: '.$e
-			);
-			return false;
-		}
-	}
+	$domain_name = idn_to_ascii($domain_name);
+	return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name)
+		   && preg_match("/^.{1,253}$/", $domain_name)
+		   && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name));
 }
-function edit_spam_score($postarray) {
-  // Array items
-  // 'username' can be set, defaults to mailcow_cc_username
-  // 'lowspamlevel'
-  // 'highspamlevel'
+function add_domain_admin($postarray) {
 	global $lang;
 	global $pdo;
-  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    else {
-      $username = $postarray['username'];
-    }
-  }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
-  }
-	$lowspamlevel	= explode(',', $postarray['score'])[0];
-	$highspamlevel	= explode(',', $postarray['score'])[1];
+	$username		= strtolower(trim($postarray['username']));
+	$password		= $postarray['password'];
+	$password2  = $postarray['password2'];
+  $active  = intval($postarray['active']);
 
-	if (!is_numeric($lowspamlevel) || !is_numeric($highspamlevel)) {
+	if ($_SESSION['mailcow_cc_role'] != "admin") {
 		$_SESSION['return'] = array(
 			'type' => 'danger',
 			'msg' => sprintf($lang['danger']['access_denied'])
 		);
 		return false;
 	}
-	try {
-		$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username
-			AND (`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
-		$stmt->execute(array(
-			':username' => $username
-		));
-
-		$stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option`, `value`)
-			VALUES (:username, 'highspamlevel', :highspamlevel)");
-		$stmt->execute(array(
-			':username' => $username,
-			':highspamlevel' => $highspamlevel
-		));
-
-		$stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option`, `value`)
-			VALUES (:username, 'lowspamlevel', :lowspamlevel)");
-		$stmt->execute(array(
-			':username' => $username,
-			':lowspamlevel' => $lowspamlevel
-		));
+	if (empty($postarray['domain'])) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['domain_invalid'])
+		);
+		return false;
 	}
-	catch (PDOException $e) {
-		$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username
-			AND (`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
-		$stmt->execute(array(
-			':username' => $username
-		));
+	if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['username_invalid'])
+		);
+		return false;
+	}
+	try {
+		$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
+			WHERE `username` = :username");
+		$stmt->execute(array(':username' => $username));
+		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+		
+		$stmt = $pdo->prepare("SELECT `username` FROM `admin`
+			WHERE `username` = :username");
+		$stmt->execute(array(':username' => $username));
+		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+		
+		$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
+			WHERE `username` = :username");
+		$stmt->execute(array(':username' => $username));
+		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+	}
+	catch(PDOException $e) {
 		$_SESSION['return'] = array(
 			'type' => 'danger',
 			'msg' => 'MySQL: '.$e
 		);
 		return false;
 	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['mailbox_modified'], $username)
-	);
-}
-function get_policy_list($object = null) {
-  // 'object' can be be set, if not, default to mailcow_cc_username
-	global $lang;
-	global $pdo;
-  if (isset($object)) {
-    if (!filter_var($object, FILTER_VALIDATE_EMAIL) && is_valid_domain_name($object)) {
-      $object = idn_to_ascii(strtolower(trim($object)));
-      if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
-        return false;
-      }
-    }
-    elseif (filter_var($object, FILTER_VALIDATE_EMAIL)) {
-      if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
-        return false;
-      }
+	foreach ($num_results as $num_results_each) {
+		if ($num_results_each != 0) {
+			$_SESSION['return'] = array(
+				'type' => 'danger',
+				'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($username))
+			);
+			return false;
+		}
+	}
+	if (!empty($password) && !empty($password2)) {
+    if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['password_complexity'])
+      );
+      return false;
     }
-  }
-  else {
-     $object = $_SESSION['mailcow_cc_username'];
-  }
-  try {
-    // WHITELIST
-    $stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='whitelist_from' AND (`object` = :username OR `object` = SUBSTRING_INDEX(:username_domain, '@' ,-1))");
-    $stmt->execute(array(':username' => $object, ':username_domain' => $object));
-    $rows['whitelist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    // BLACKLIST
-    $stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='blacklist_from' AND (`object` = :username OR `object` = SUBSTRING_INDEX(:username_domain, '@' ,-1))");
-    $stmt->execute(array(':username' => $object, ':username_domain' => $object));
-    $rows['blacklist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-  }
-  return $rows;
-}
-function add_policy_list_item($postarray) {
-  // Array data
-  // Either 'domain' or 'username' can be be set
-  // If none of the above is set, default to mailcow_cc_username
-  //
-  // If 'delete_prefid' then delete item id
-	global $lang;
-	global $pdo;
-  $object = (isset($postarray['username'])) ? $postarray['username'] : null;
-  $object = (isset($postarray['domain'])) ? $postarray['domain'] : null;
-  (!isset($object)) ? $object = $_SESSION['mailcow_cc_username'] : null;
-
-  if (is_valid_domain_name($object)) {
-		if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+		if ($password != $password2) {
 			$_SESSION['return'] = array(
 				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['access_denied'])
+				'msg' => sprintf($lang['danger']['password_mismatch'])
 			);
 			return false;
 		}
-    $object = idn_to_ascii(strtolower(trim($object)));
-  }
-  else {
-		if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+		$password_hashed = hash_password($password);
+		foreach ($postarray['domain'] as $domain) {
+			if (!is_valid_domain_name($domain)) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['domain_invalid'])
+				);
+				return false;
+			}
+			try {
+				$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
+						VALUES (:username, :domain, :created, :active)");
+				$stmt->execute(array(
+					':username' => $username,
+					':domain' => $domain,
+					':created' => date('Y-m-d H:i:s'),
+					':active' => $active
+				));
+			}
+			catch (PDOException $e) {
+        delete_domain_admin(array('username' => $username));
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+		}
+		try {
+			$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
+				VALUES (:username, :password_hashed, '0', :active)");
+			$stmt->execute(array(
+				':username' => $username,
+				':password_hashed' => $password_hashed,
+				':active' => $active
+			));
+		}
+		catch (PDOException $e) {
 			$_SESSION['return'] = array(
 				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['access_denied'])
+				'msg' => 'MySQL: '.$e
 			);
 			return false;
 		}
-  }
-
-	($postarray['object_list'] == "bl") ? $object_list = "blacklist_from" : null;
-	($postarray['object_list'] == "wl") ? $object_list = "whitelist_from" : null;
-	$object_from = preg_replace('/\.+/', '.', rtrim(preg_replace("/\.\*/", "*", trim(strtolower($postarray['object_from']))), '.'));
-  if (!ctype_alnum(str_replace(array('@', '.', '-', '*'), '', $object_from))) {
+	}
+	else {
 		$_SESSION['return'] = array(
 			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['policy_list_from_invalid'])
+			'msg' => sprintf($lang['danger']['password_empty'])
 		);
 		return false;
 	}
-	if ($object_list != "blacklist_from" && $object_list != "whitelist_from") {
-    $_SESSION['return'] = array(
+	$_SESSION['return'] = array(
+		'type' => 'success',
+		'msg' => sprintf($lang['success']['domain_admin_added'], htmlspecialchars($username))
+	);
+}
+function delete_domain_admin($postarray) {
+	global $pdo;
+	global $lang;
+	if ($_SESSION['mailcow_cc_role'] != "admin") {
+		$_SESSION['return'] = array(
 			'type' => 'danger',
 			'msg' => sprintf($lang['danger']['access_denied'])
 		);
 		return false;
 	}
-	try {
-		$stmt = $pdo->prepare("SELECT `object` FROM `filterconf`
-			WHERE (`option` = 'whitelist_from'  OR `option` = 'blacklist_from')
-				AND `object` = :object
-				AND `value` = :object_from");
-		$stmt->execute(array(':object' => $object, ':object_from' => $object_from));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results != 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['policy_list_from_exists'])
-      );
-      return false;
-    }
-	}
-	catch(PDOException $e) {
+	$username = $postarray['username'];
+	if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
 		$_SESSION['return'] = array(
 			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
+			'msg' => sprintf($lang['danger']['username_invalid'])
 		);
 		return false;
 	}
-
 	try {
-		$stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option` ,`value`)
-			VALUES (:object, :object_list, :object_from)");
+		$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
+		$stmt->execute(array(
+			':username' => $username,
+		));
+		$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
 		$stmt->execute(array(
-			':object' => $object,
-			':object_list' => $object_list,
-			':object_from' => $object_from
+			':username' => $username,
 		));
 	}
 	catch (PDOException $e) {
@@ -803,91 +589,32 @@ function add_policy_list_item($postarray) {
 	}
 	$_SESSION['return'] = array(
 		'type' => 'success',
-		'msg' => sprintf($lang['success']['object_modified'], $object)
+		'msg' => sprintf($lang['success']['domain_admin_removed'], htmlspecialchars($username))
 	);
 }
-function delete_policy_list_item($postarray) {
-  // Array data
-  // Either 'domain' or 'username' can be be set
-  // If none of the above is set, default to mailcow_cc_username
-  //
-  // 'delete_prefid' is item to be deleted
-	global $lang;
+function get_domain_admins() {
 	global $pdo;
-  $object = (isset($postarray['username'])) ? $postarray['username'] : null;
-  $object = (isset($postarray['domain'])) ? $postarray['domain'] : null;
-  (!isset($object)) ? $object = $_SESSION['mailcow_cc_username'] : null;
-
-  if (is_valid_domain_name($object)) {
-		if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['access_denied'])
-			);
-			return false;
-		}
-    $object = idn_to_ascii(strtolower(trim($object)));
-  }
-  else {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-  }
-
-  if (!is_numeric($postarray['delete_prefid'])) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['access_denied'])
-    );
-    return false;
-  }
-
-  try {
-    $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :object AND `prefid` = :prefid");
-    $stmt->execute(array(
-      ':object' => $object,
-      ':prefid' => $postarray['delete_prefid']
-    ));
-  }
-  catch (PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-  $_SESSION['return'] = array(
-    'type' => 'success',
-    'msg' => sprintf($lang['success']['object_modified'], $object)
-  );
-  return true;
-}
-function get_syncjobs($username = null) {
-  // 'username' can be set, if not set, defaults to mailcow_cc_username
 	global $lang;
-	global $pdo;
-  $data = array();
-  if (isset($username) && filter_var($username, FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
-      return false;
-    }
-  }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
-  }
+  $domainadmins = array();
+	if ($_SESSION['mailcow_cc_role'] != "admin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
   try {
-    $stmt = $pdo->prepare("SELECT *,
-      CONCAT(LEFT(`password1`, 3), '...') AS `password1_short`,
-      `active` AS `active_int`,
-      CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
-        FROM `imapsync`
-          WHERE `user2` = :username");
-    $stmt->execute(array(':username' => $username));
-    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    $stmt = $pdo->query("SELECT DISTINCT
+      `username`
+        FROM `domain_admins` 
+          WHERE `username` IN (
+            SELECT `username` FROM `admin`
+              WHERE `superadmin`!='1'
+          )");
+    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while ($row = array_shift($rows)) {
+      $domainadmins[] = $row['username'];
+    }
   }
   catch(PDOException $e) {
     $_SESSION['return'] = array(
@@ -895,3541 +622,274 @@ function get_syncjobs($username = null) {
       'msg' => 'MySQL: '.$e
     );
   }
-  return $data;
+  return $domainadmins;
 }
-function get_syncjob_details($id) {
-	global $lang;
+function get_domain_admin_details($domain_admin) {
 	global $pdo;
-  $syncjobdetails = array();
-	if ($_SESSION['mailcow_cc_role'] != "user" &&
-		$_SESSION['mailcow_cc_role'] != "admin") {
-			return false;
+
+	global $lang;
+  $domainadmindata = array();
+	if (isset($domain_admin) && $_SESSION['mailcow_cc_role'] != "admin") {
+		return false;
+	}
+  if (!isset($domain_admin) && $_SESSION['mailcow_cc_role'] != "domainadmin") {
+		return false;
+	}
+  (!isset($domain_admin)) ? $domain_admin = $_SESSION['mailcow_cc_username'] : null;
+  
+  if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $domain_admin))) {
+		return false;
 	}
-  if (!is_numeric($id)) {
-    return false;
-  }
-  try {
-    $stmt = $pdo->prepare("SELECT * FROM `imapsync` WHERE (`user2` = :username OR 'admin' = :role) AND id = :id");
-    $stmt->execute(array(':id' => $id, ':role' => $_SESSION['mailcow_cc_role'], ':username' => $_SESSION['mailcow_cc_username']));
-    $syncjobdetails = $stmt->fetch(PDO::FETCH_ASSOC);
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-  }
-  return $syncjobdetails;
-}
-function delete_syncjob($postarray) {
-  // Array items
-  // 'username' can be set, defaults to mailcow_cc_username
-	global $lang;
-	global $pdo;
-  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    else {
-      $username = $postarray['username'];
-    }
-  }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
-  }
-  $id = $postarray['id'];
-  if (!is_numeric($id)) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['access_denied'])
-    );
-    return false;
-  }
-  try {
-    $stmt = $pdo->prepare("DELETE FROM `imapsync` WHERE `user2` = :username AND `id`= :id");
-    $stmt->execute(array(
-      ':username' => $username,
-      ':id' => $id,
-    ));
-  }
-  catch (PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-  $_SESSION['return'] = array(
-    'type' => 'success',
-    'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($username))
-  );
-  return true;
-}
-function add_syncjob($postarray) {
-  // Array items
-  // 'username' can be set, defaults to mailcow_cc_username
-	global $lang;
-	global $pdo;
-  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    else {
-      $username = $postarray['username'];
-    }
-  }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
-  }
-
-	$active  = intval($postarray['active']);
-	$delete2duplicates = intval($postarray['delete2duplicates']);
-	$delete1  = intval($postarray['delete1']);
-  $port1            = $postarray['port1'];
-  $host1            = $postarray['host1'];
-  $password1        = $postarray['password1'];
-  $exclude          = $postarray['exclude'];
-  $maxage           = $postarray['maxage'];
-  $subfolder2       = $postarray['subfolder2'];
-  $user1            = $postarray['user1'];
-  $mins_interval    = $postarray['mins_interval'];
-  $enc1             = $postarray['enc1'];
-
-  if (empty($subfolder2)) {
-    $subfolder2 = "";
-  }
-  if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) {
-    $maxage = "0";
-  }
-  if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['access_denied'])
-    );
-    return false;
-  }
-  if (!filter_var($mins_interval, FILTER_VALIDATE_INT, array('options' => array('min_range' => 10, 'max_range' => 3600)))) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['access_denied'])
-    );
-    return false;
-  }
-  if (!is_valid_domain_name($host1)) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['access_denied'])
-    );
-    return false;
-  }
-  if ($enc1 != "TLS" && $enc1 != "SSL" && $enc1 != "PLAIN") {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['access_denied'])
-    );
-    return false;
-  }
-  if (@preg_match("/" . $exclude . "/", null) === false) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['access_denied'])
-    );
-    return false;
-  }
-  try {
-    $stmt = $pdo->prepare("SELECT `user2`, `user1` FROM `imapsync`
-      WHERE `user2` = :user2 AND `user1` = :user1");
-    $stmt->execute(array(':user1' => $user1, ':user2' => $username));
-    $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-  if ($num_results != 0) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($host1 . ' / ' . $user1))
-    );
-    return false;
-  }
-  try {
-    $stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `active`)
-      VALUES (:user2, :exclude, :maxage, :delete1, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :active)");
-    $stmt->execute(array(
-      ':user2' => $username,
-      ':exclude' => $exclude,
-      ':maxage' => $maxage,
-      ':delete1' => $delete1,
-      ':subfolder2' => $subfolder2,
-      ':host1' => $host1,
-      ':authmech1' => 'PLAIN',
-      ':user1' => $user1,
-      ':password1' => $password1,
-      ':mins_interval' => $mins_interval,
-      ':port1' => $port1,
-      ':enc1' => $enc1,
-      ':delete2duplicates' => $delete2duplicates,
-      ':active' => $active,
-    ));
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-  $_SESSION['return'] = array(
-    'type' => 'success',
-    'msg' => sprintf($lang['success']['mailbox_modified'], $username)
-  );
-  return true;
-}
-function edit_syncjob($postarray) {
-  // Array items
-	global $lang;
-	global $pdo;
-  if (!is_array($postarray['id'])) {
-    $ids = array();
-    $ids[] = $postarray['id'];
-  }
-  else {
-    $ids = $postarray['id'];
-  }
-
-  foreach ($ids as $id) {
-    $is_now = get_syncjob_details($id);
-    if (!empty($is_now)) {
-      $username = $is_now['user2'];
-      $user1 = (!empty($postarray['user1'])) ? $postarray['user1'] : $is_now['user1'];
-      $active = (isset($postarray['active'])) ? $postarray['active'] : $is_now['active_int'];
-      $delete2duplicates = (isset($postarray['delete2duplicates'])) ? $postarray['delete2duplicates'] : $is_now['delete2duplicates'];
-      $delete1 = (isset($postarray['delete1'])) ? $postarray['delete1'] : $is_now['delete1'];
-      $port1 = (!empty($postarray['port1'])) ? $postarray['port1'] : $is_now['port1'];
-      $password1 = (!empty($postarray['password1'])) ? $postarray['password1'] : $is_now['password1'];
-      $host1 = (!empty($postarray['host1'])) ? $postarray['host1'] : $is_now['host1'];
-      $subfolder2 = (!empty($postarray['subfolder2'])) ? $postarray['subfolder2'] : $is_now['subfolder2'];
-      $enc1 = (!empty($postarray['enc1'])) ? $postarray['enc1'] : $is_now['enc1'];
-      $mins_interval = (!empty($postarray['mins_interval'])) ? $postarray['mins_interval'] : $is_now['mins_interval'];
-      $exclude = (!empty($postarray['exclude'])) ? $postarray['exclude'] : $is_now['exclude'];
-      $maxage = (!empty($postarray['maxage'])) ? $postarray['maxage'] : $is_now['maxage'];
-    }
-    else {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    if (empty($subfolder2)) {
-      $subfolder2 = "";
-    }
-    if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) {
-      $maxage = "0";
-    }
-    if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    if (!filter_var($mins_interval, FILTER_VALIDATE_INT, array('options' => array('min_range' => 10, 'max_range' => 3600)))) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    if (!is_valid_domain_name($host1)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    if ($enc1 != "TLS" && $enc1 != "SSL" && $enc1 != "PLAIN") {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    if (@preg_match("/" . $exclude . "/", null) === false) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    try {
-      $stmt = $pdo->prepare("UPDATE `imapsync` SET `delete1` = :delete1, `maxage` = :maxage, `subfolder2` = :subfolder2, `exclude` = :exclude, `host1` = :host1, `user1` = :user1, `password1` = :password1, `mins_interval` = :mins_interval, `port1` = :port1, `enc1` = :enc1, `delete2duplicates` = :delete2duplicates, `active` = :active
-        WHERE `id` = :id");
-      $stmt->execute(array(
-        ':delete1' => $delete1,
-        ':id' => $id,
-        ':exclude' => $exclude,
-        ':maxage' => $maxage,
-        ':subfolder2' => $subfolder2,
-        ':host1' => $host1,
-        ':user1' => $user1,
-        ':password1' => $password1,
-        ':mins_interval' => $mins_interval,
-        ':port1' => $port1,
-        ':enc1' => $enc1,
-        ':delete2duplicates' => $delete2duplicates,
-        ':active' => $active,
-      ));
-    }
-    catch(PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  }
-  $_SESSION['return'] = array(
-    'type' => 'success',
-    'msg' => sprintf($lang['success']['mailbox_modified'], $username)
-  );
-  return true;
-}
-function edit_tls_policy($postarray) {
-	global $lang;
-	global $pdo;
-  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    else {
-      $username = $postarray['username'];
-    }
-  }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
-  }
-  $tls_out = intval($postarray['tls_out']);
-  $tls_in = intval($postarray['tls_in']);
-	$username = $_SESSION['mailcow_cc_username'];
-	if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['username_invalid'])
-		);
-		return false;
-	}
-	try {
-		$stmt = $pdo->prepare("UPDATE `mailbox` SET `tls_enforce_out` = :tls_out, `tls_enforce_in` = :tls_in WHERE `username` = :username");
-		$stmt->execute(array(
-			':tls_out' => $tls_out,
-			':tls_in' => $tls_in,
-			':username' => $username
-		));
-	}
-	catch (PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['mailbox_modified'], $username)
-	);
-}
-function get_tls_policy($username = null) {
-	global $lang;
-	global $pdo;
-  $data = array();
-  if (isset($username) && filter_var($username, FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
-      return false;
-    }
-  }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
-  }
-	try {
-		$stmt = $pdo->prepare("SELECT `tls_enforce_out`, `tls_enforce_in` FROM `mailbox` WHERE `username` = :username");
-		$stmt->execute(array(':username' => $username));
-		$data = $stmt->fetch(PDO::FETCH_ASSOC);
-	}
-	catch(PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-	return $data;
-}
-function edit_delimiter_action($postarray) {
-  // Array items
-  // 'username' can be set, defaults to mailcow_cc_username
-	global $lang;
-	global $redis;
-  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    else {
-      $username = $postarray['username'];
-    }
-  }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
-  }
-  if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['username_invalid'])
-    );
-    return false;
-  }
-  if (isset($postarray['tagged_mail_handler']) && $postarray['tagged_mail_handler'] == "subject") {
-    try {
-      $redis->hSet('RCPT_WANTS_SUBJECT_TAG', $username, 1);
-    }
-    catch (RedisException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'Redis: '.$e
-      );
-      return false;
-    }
-  }
-  else {
-    try {
-      $redis->hDel('RCPT_WANTS_SUBJECT_TAG', $username);
-    }
-    catch (RedisException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'Redis: '.$e
-      );
-      return false;
-    }
-  }
-  $_SESSION['return'] = array(
-    'type' => 'success',
-    'msg' => sprintf($lang['success']['mailbox_modified'], $username)
-  );
-  return true;
-}
-function get_delimiter_action($username = null) {
-  // 'username' can be set, defaults to mailcow_cc_username
-	global $lang;
-	global $redis;
-	$data = array();
-  if (isset($username) && filter_var($username, FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
-      return false;
-    }
-  }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
-  }
-  try {
-    if ($redis->hGet('RCPT_WANTS_SUBJECT_TAG', $username)) {
-      return "subject";
-    }
-    else {
-      return "subfolder";
-    }
-  }
-  catch (RedisException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'Redis: '.$e
-    );
-    return false;
-  }
-}
-function user_get_alias_details($username) {
-	global $lang;
-	global $pdo;
-  if ($_SESSION['mailcow_cc_role'] == "user") {
-    $username	= $_SESSION['mailcow_cc_username'];
-  }
-  if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
-    return false;
-  }
-  try {
-    $data['address'] = $username;
-    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `aliases` FROM `alias`
-      WHERE `goto` REGEXP :username_goto
-      AND `address` NOT LIKE '@%'
-      AND `address` != :username_address");
-    $stmt->execute(array(':username_goto' => '(^|,)'.$username.'($|,)', ':username_address' => $username));
-    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row = array_shift($run)) {
-      $data['aliases'] = $row['aliases'];
-    }
-    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ', '), '&#10008;') AS `ad_alias` FROM `mailbox`
-      LEFT OUTER JOIN `alias_domain` on `target_domain` = `domain`
-        WHERE `username` = :username ;");
-    $stmt->execute(array(':username' => $username));
-    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row = array_shift($run)) {
-      $data['ad_alias'] = $row['ad_alias'];
-    }
-    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '&#10008;') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` NOT LIKE '@%';");
-    $stmt->execute(array(':username' => $username));
-    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row = array_shift($run)) {
-      $data['aliases_also_send_as'] = $row['send_as'];
-    }
-    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`send_as` SEPARATOR ', '), '&#10008;') AS `send_as` FROM `sender_acl` WHERE `logged_in_as` = :username AND `send_as` LIKE '@%';");
-    $stmt->execute(array(':username' => $username));
-    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row = array_shift($run)) {
-      $data['aliases_send_as_all'] = $row['send_as'];
-    }
-    $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') as `address` FROM `alias` WHERE `goto` REGEXP :username AND `address` LIKE '@%';");
-    $stmt->execute(array(':username' => '(^|,)'.$username.'($|,)'));
-    $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row = array_shift($run)) {
-      $data['is_catch_all'] = $row['address'];
-    }
-    return $data;
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-}
-function is_valid_domain_name($domain_name) { 
-	if (empty($domain_name)) {
-		return false;
-	}
-	$domain_name = idn_to_ascii($domain_name);
-	return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name)
-		   && preg_match("/^.{1,253}$/", $domain_name)
-		   && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name));
-}
-function add_domain_admin($postarray) {
-	global $lang;
-	global $pdo;
-	$username		= strtolower(trim($postarray['username']));
-	$password		= $postarray['password'];
-	$password2  = $postarray['password2'];
-  $active  = intval($postarray['active']);
-
-	if ($_SESSION['mailcow_cc_role'] != "admin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-	if (empty($postarray['domain'])) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['domain_invalid'])
-		);
-		return false;
-	}
-	if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['username_invalid'])
-		);
-		return false;
-	}
-	try {
-		$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
-			WHERE `username` = :username");
-		$stmt->execute(array(':username' => $username));
-		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-		
-		$stmt = $pdo->prepare("SELECT `username` FROM `admin`
-			WHERE `username` = :username");
-		$stmt->execute(array(':username' => $username));
-		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-		
-		$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
-			WHERE `username` = :username");
-		$stmt->execute(array(':username' => $username));
-		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-	}
-	catch(PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-	foreach ($num_results as $num_results_each) {
-		if ($num_results_each != 0) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($username))
-			);
-			return false;
-		}
-	}
-	if (!empty($password) && !empty($password2)) {
-    if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['password_complexity'])
-      );
-      return false;
-    }
-		if ($password != $password2) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['password_mismatch'])
-			);
-			return false;
-		}
-		$password_hashed = hash_password($password);
-		foreach ($postarray['domain'] as $domain) {
-			if (!is_valid_domain_name($domain)) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => sprintf($lang['danger']['domain_invalid'])
-				);
-				return false;
-			}
-			try {
-				$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
-						VALUES (:username, :domain, :created, :active)");
-				$stmt->execute(array(
-					':username' => $username,
-					':domain' => $domain,
-					':created' => date('Y-m-d H:i:s'),
-					':active' => $active
-				));
-			}
-			catch (PDOException $e) {
-        delete_domain_admin(array('username' => $username));
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'MySQL: '.$e
-				);
-				return false;
-			}
-		}
-		try {
-			$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
-				VALUES (:username, :password_hashed, '0', :active)");
-			$stmt->execute(array(
-				':username' => $username,
-				':password_hashed' => $password_hashed,
-				':active' => $active
-			));
-		}
-		catch (PDOException $e) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => 'MySQL: '.$e
-			);
-			return false;
-		}
-	}
-	else {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['password_empty'])
-		);
-		return false;
-	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['domain_admin_added'], htmlspecialchars($username))
-	);
-}
-function delete_domain_admin($postarray) {
-	global $pdo;
-	global $lang;
-	if ($_SESSION['mailcow_cc_role'] != "admin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-	$username = $postarray['username'];
-	if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['username_invalid'])
-		);
-		return false;
-	}
-	try {
-		$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
-		$stmt->execute(array(
-			':username' => $username,
-		));
-		$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
-		$stmt->execute(array(
-			':username' => $username,
-		));
-	}
-	catch (PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['domain_admin_removed'], htmlspecialchars($username))
-	);
-}
-function get_domain_admins() {
-	global $pdo;
-	global $lang;
-  $domainadmins = array();
-	if ($_SESSION['mailcow_cc_role'] != "admin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-  try {
-    $stmt = $pdo->query("SELECT DISTINCT
-      `username`
-        FROM `domain_admins` 
-          WHERE `username` IN (
-            SELECT `username` FROM `admin`
-              WHERE `superadmin`!='1'
-          )");
-    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row = array_shift($rows)) {
-      $domainadmins[] = $row['username'];
-    }
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-  }
-  return $domainadmins;
-}
-function get_domain_admin_details($domain_admin) {
-	global $pdo;
-
-	global $lang;
-  $domainadmindata = array();
-	if (isset($domain_admin) && $_SESSION['mailcow_cc_role'] != "admin") {
-		return false;
-	}
-  if (!isset($domain_admin) && $_SESSION['mailcow_cc_role'] != "domainadmin") {
-		return false;
-	}
-  (!isset($domain_admin)) ? $domain_admin = $_SESSION['mailcow_cc_username'] : null;
-  
-  if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $domain_admin))) {
-		return false;
-	}
-  try {
-    $stmt = $pdo->prepare("SELECT
-      `tfa`.`active` AS `tfa_active_int`,
-      CASE `tfa`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `tfa_active`,
-      `domain_admins`.`username`,
-      `domain_admins`.`created`,
-      `domain_admins`.`active` AS `active_int`,
-      CASE `domain_admins`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
-        FROM `domain_admins`
-        LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
-          WHERE `domain_admins`.`username`= :domain_admin");
-    $stmt->execute(array(
-      ':domain_admin' => $domain_admin
-    ));
-    $row = $stmt->fetch(PDO::FETCH_ASSOC);
-    if (empty($row)) { 
-      return false;
-    }
-    $domainadmindata['username'] = $row['username'];
-    $domainadmindata['tfa_active'] = $row['tfa_active'];
-    $domainadmindata['active'] = $row['active'];
-    $domainadmindata['tfa_active_int'] = $row['tfa_active_int'];
-    $domainadmindata['active_int'] = $row['active_int'];
-    $domainadmindata['modified'] = $row['created'];
-    // GET SELECTED
-    $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
-      WHERE `domain` IN (
-        SELECT `domain` FROM `domain_admins`
-          WHERE `username`= :domain_admin)");
-    $stmt->execute(array(':domain_admin' => $domain_admin));
-    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while($row = array_shift($rows)) {
-      $domainadmindata['selected_domains'][] = $row['domain'];
-    }
-    // GET UNSELECTED
-    $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
-      WHERE `domain` NOT IN (
-        SELECT `domain` FROM `domain_admins`
-          WHERE `username`= :domain_admin)");
-    $stmt->execute(array(':domain_admin' => $domain_admin));
-    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while($row = array_shift($rows)) {
-      $domainadmindata['unselected_domains'][] = $row['domain'];
-    }
-    if (!isset($domainadmindata['unselected_domains'])) {
-      $domainadmindata['unselected_domains'] = "";
-    }
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-  }
-  return $domainadmindata;
-}
-function set_tfa($postarray) {
-	global $lang;
-	global $pdo;
-	global $yubi;
-	global $u2f;
-	global $tfa;
-
-  if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-    $_SESSION['mailcow_cc_role'] != "admin") {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-  }
-  $username = $_SESSION['mailcow_cc_username'];
-  
-  $stmt = $pdo->prepare("SELECT `password` FROM `admin`
-      WHERE `username` = :user");
-  $stmt->execute(array(':user' => $username));
-  $row = $stmt->fetch(PDO::FETCH_ASSOC);
-  if (!verify_ssha256($row['password'], $postarray["confirm_password"])) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['access_denied'])
-    );
-    return false;
-  }
-  
-	switch ($postarray["tfa_method"]) {
-		case "yubi_otp":
-      $key_id = (!isset($postarray["key_id"])) ? 'unidentified' : $postarray["key_id"];
-      $yubico_id = $postarray['yubico_id'];
-      $yubico_key = $postarray['yubico_key'];
-      $yubi = new Auth_Yubico($yubico_id, $yubico_key);
-      if (!$yubi) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['access_denied'])
-        );
-        return false;
-      }
-			if (!ctype_alnum($postarray["otp_token"]) || strlen($postarray["otp_token"]) != 44) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => sprintf($lang['danger']['tfa_token_invalid'])
-				);
-				return false;
-			}
-      $yauth = $yubi->verify($postarray["otp_token"]);
-      if (PEAR::isError($yauth)) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'Yubico API: ' . $yauth->getMessage()
-				);
-				return false;
-      }
-			try {
-        // We could also do a modhex translation here
-        $yubico_modhex_id = substr($postarray["otp_token"], 0, 12);
-        $stmt = $pdo->prepare("DELETE FROM `tfa` 
-          WHERE `username` = :username
-            AND (`authmech` != 'yubi_otp')
-            OR (`authmech` = 'yubi_otp' AND `secret` LIKE :modhex)");
-				$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
-				$stmt = $pdo->prepare("INSERT INTO `tfa` (`key_id`, `username`, `authmech`, `active`, `secret`) VALUES
-					(:key_id, :username, 'yubi_otp', '1', :secret)");
-				$stmt->execute(array(':key_id' => $key_id, ':username' => $username, ':secret' => $yubico_id . ':' . $yubico_key . ':' . $yubico_modhex_id));
-			}
-			catch (PDOException $e) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'MySQL: '.$e
-				);
-				return false;
-			}
-			$_SESSION['return'] = array(
-				'type' => 'success',
-				'msg' => sprintf($lang['success']['object_modified'], htmlspecialchars($username))
-			);
-		break;
-
-		case "u2f":
-      $key_id = (!isset($postarray["key_id"])) ? 'unidentified' : $postarray["key_id"];
-      try {
-        $reg = $u2f->doRegister(json_decode($_SESSION['regReq']), json_decode($postarray['token']));
-        $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `authmech` != 'u2f'");
-				$stmt->execute(array(':username' => $username));
-        $stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`) VALUES (?, ?, 'u2f', ?, ?, ?, ?, '1')");
-        $stmt->execute(array($username, $key_id, $reg->keyHandle, $reg->publicKey, $reg->certificate, $reg->counter));
-        $_SESSION['return'] = array(
-          'type' => 'success',
-          'msg' => sprintf($lang['success']['object_modified'], $username)
-        );
-        $_SESSION['regReq'] = null;
-      }
-      catch (Exception $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => "U2F: " . $e->getMessage()
-        );
-        $_SESSION['regReq'] = null;
-        return false;
-      }
-		break;
-
-		case "totp":
-      $key_id = (!isset($postarray["key_id"])) ? 'unidentified' : $postarray["key_id"];
-      if ($tfa->verifyCode($_POST['totp_secret'], $_POST['totp_confirm_token']) === true) {
-        try {
-        $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
-        $stmt->execute(array(':username' => $username));
-        $stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `secret`, `active`) VALUES (?, ?, 'totp', ?, '1')");
-        $stmt->execute(array($username, $key_id, $_POST['totp_secret']));
-        }
-        catch (PDOException $e) {
-          $_SESSION['return'] = array(
-            'type' => 'danger',
-            'msg' => 'MySQL: '.$e
-          );
-          return false;
-        }
-        $_SESSION['return'] = array(
-          'type' => 'success',
-          'msg' => sprintf($lang['success']['object_modified'], $username)
-        );
-      }
-      else {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'TOTP verification failed'
-        );
-      }
-		break;
-
-		case "none":
-			try {
-				$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
-				$stmt->execute(array(':username' => $username));
-			}
-			catch (PDOException $e) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'MySQL: '.$e
-				);
-				return false;
-			}
-			$_SESSION['return'] = array(
-				'type' => 'success',
-				'msg' => sprintf($lang['success']['object_modified'], htmlspecialchars($username))
-			);
-		break;
-	}
-}
-function unset_tfa_key($postarray) {
-  // Can only unset own keys
-  // Needs at least one key left
-  global $pdo;
-  global $lang;
-  $id = intval($postarray['unset_tfa_key']);
-  if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
-    $_SESSION['mailcow_cc_role'] != "admin") {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-  }
-  $username = $_SESSION['mailcow_cc_username'];
-  try {
-    if (!is_numeric($id)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    $stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
-      WHERE `username` = :username AND `active` = '1'");
-    $stmt->execute(array(':username' => $username));
-    $row = $stmt->fetch(PDO::FETCH_ASSOC);
-    if ($row['keys'] == "1") {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['last_key'])
-      );
-      return false;
-    }
-    $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `id` = :id");
-    $stmt->execute(array(':username' => $username, ':id' => $id));
-    $_SESSION['return'] = array(
-      'type' => 'success',
-      'msg' => sprintf($lang['success']['object_modified'], $username)
-    );
-  }
-  catch (PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-}
-function get_tfa($username = null) {
-	global $pdo;
-  if (isset($_SESSION['mailcow_cc_username'])) {
-    $username = $_SESSION['mailcow_cc_username'];
-  }
-  elseif (empty($username)) {
-    return false;
-  }
-
-  $stmt = $pdo->prepare("SELECT * FROM `tfa`
-      WHERE `username` = :username AND `active` = '1'");
-  $stmt->execute(array(':username' => $username));
-  $row = $stmt->fetch(PDO::FETCH_ASSOC);
-  
-	switch ($row["authmech"]) {
-		case "yubi_otp":
-      $data['name'] = "yubi_otp";
-      $data['pretty'] = "Yubico OTP";
-      $stmt = $pdo->prepare("SELECT `id`, `key_id`, RIGHT(`secret`, 12) AS 'modhex' FROM `tfa` WHERE `authmech` = 'yubi_otp' AND `username` = :username");
-      $stmt->execute(array(
-        ':username' => $username,
-      ));
-      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-      while($row = array_shift($rows)) {
-        $data['additional'][] = $row;
-      }
-      return $data;
-    break;
-		case "u2f":
-      $data['name'] = "u2f";
-      $data['pretty'] = "Fido U2F";
-      $stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username");
-      $stmt->execute(array(
-        ':username' => $username,
-      ));
-      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-      while($row = array_shift($rows)) {
-        $data['additional'][] = $row;
-      }
-      return $data;
-    break;
-		case "hotp":
-      $data['name'] = "hotp";
-      $data['pretty'] = "HMAC-based OTP";
-      return $data;
-		break;
- 		case "totp":
-      $data['name'] = "totp";
-      $data['pretty'] = "Time-based OTP";
-      $stmt = $pdo->prepare("SELECT `id`, `key_id`, `secret` FROM `tfa` WHERE `authmech` = 'totp' AND `username` = :username");
-      $stmt->execute(array(
-        ':username' => $username,
-      ));
-      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-      while($row = array_shift($rows)) {
-        $data['additional'][] = $row;
-      }
-      return $data;
-      break;
-    default:
-      $data['name'] = 'none';
-      $data['pretty'] = "-";
-      return $data;
-    break;
-	}
-}
-function verify_tfa_login($username, $token) {
-	global $pdo;
-	global $lang;
-	global $yubi;
-	global $u2f;
-	global $tfa;
-
-  $stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
-      WHERE `username` = :username AND `active` = '1'");
-  $stmt->execute(array(':username' => $username));
-  $row = $stmt->fetch(PDO::FETCH_ASSOC);
-  
-	switch ($row["authmech"]) {
-		case "yubi_otp":
-			if (!ctype_alnum($token) || strlen($token) != 44) {
-        return false;
-      }
-      $yubico_modhex_id = substr($token, 0, 12);
-      $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
-          WHERE `username` = :username
-          AND `authmech` = 'yubi_otp'
-          AND `active`='1'
-          AND `secret` LIKE :modhex");
-      $stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
-      $row = $stmt->fetch(PDO::FETCH_ASSOC);
-      $yubico_auth = explode(':', $row['secret']);
-      $yubi = new Auth_Yubico($yubico_auth[0], $yubico_auth[1]);
-      $yauth = $yubi->verify($token);
-      if (PEAR::isError($yauth)) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'Yubico Authentication error: ' . $yauth->getMessage()
-				);
-				return false;
-      }
-      else {
-        $_SESSION['tfa_id'] = $row['id'];
-        return true;
-      }
-    return false;
-  break;
-  case "u2f":
-    try {
-      $reg = $u2f->doAuthenticate(json_decode($_SESSION['authReq']), get_u2f_registrations($username), json_decode($token));
-      $stmt = $pdo->prepare("UPDATE `tfa` SET `counter` = ? WHERE `id` = ?");
-      $stmt->execute(array($reg->counter, $reg->id));
-      $_SESSION['tfa_id'] = $reg->id;
-      $_SESSION['authReq'] = null;
-      return true;
-    }
-    catch (Exception $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => "U2F: " . $e->getMessage()
-      );
-      $_SESSION['regReq'] = null;
-      return false;
-    }
-    return false;
-  break;
-  case "hotp":
-      return false;
-  break;
-  case "totp":
-    try {
-      $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
-          WHERE `username` = :username
-          AND `authmech` = 'totp'
-          AND `active`='1'");
-      $stmt->execute(array(':username' => $username));
-      $row = $stmt->fetch(PDO::FETCH_ASSOC);
-      if ($tfa->verifyCode($row['secret'], $_POST['token']) === true) {
-        $_SESSION['tfa_id'] = $row['id'];
-        return true;
-      }
-      return false;
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  break;
-  default:
-      return false;
-  break;
-	}
-  return false;
-}
-function edit_domain_admin($postarray) {
-	global $lang;
-	global $pdo;
-
-	if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-	// Administrator
-  if ($_SESSION['mailcow_cc_role'] == "admin") {
-    $username     = $postarray['username'];
-    $username_now = $postarray['username_now'];
-    $password     = $postarray['password'];
-    $password2    = $postarray['password2'];
-    $active       = intval($postarray['active']);
-
-    if(isset($postarray['domain'])) {
-      foreach ($postarray['domain'] as $domain) {
-        if (!is_valid_domain_name($domain)) {
-          $_SESSION['return'] = array(
-            'type' => 'danger',
-            'msg' => sprintf($lang['danger']['domain_invalid'])
-          );
-          return false;
-        }
-      }
-    }
-
-    if (empty($postarray['domain'])) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['domain_invalid'])
-      );
-      return false;
-    }
-
-    if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['username_invalid'])
-      );
-      return false;
-    }
-    if ($username != $username_now) {
-      if (empty(get_domain_admin_details($username_now)['username']) || !empty(get_domain_admin_details($username)['username'])) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['username_invalid'])
-        );
-        return false;
-      }
-    }
-    try {
-      $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
-      $stmt->execute(array(
-        ':username' => $username_now,
-      ));
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-
-    if (isset($postarray['domain'])) {
-      foreach ($postarray['domain'] as $domain) {
-        try {
-          $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
-            VALUES (:username, :domain, :created, :active)");
-          $stmt->execute(array(
-            ':username' => $username,
-            ':domain' => $domain,
-            ':created' => date('Y-m-d H:i:s'),
-            ':active' => $active
-          ));
-        }
-        catch (PDOException $e) {
-          $_SESSION['return'] = array(
-            'type' => 'danger',
-            'msg' => 'MySQL: '.$e
-          );
-          return false;
-        }
-      }
-    }
-
-    if (!empty($password) && !empty($password2)) {
-      if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['password_complexity'])
-        );
-        return false;
-      }
-      if ($password != $password2) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['password_mismatch'])
-        );
-        return false;
-      }
-      $password_hashed = hash_password($password);
-      try {
-        $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `active` = :active, `password` = :password_hashed WHERE `username` = :username2");
-        $stmt->execute(array(
-          ':password_hashed' => $password_hashed,
-          ':username1' => $username,
-          ':username2' => $username_now,
-          ':active' => $active
-        ));
-        if (isset($postarray['disable_tfa'])) {
-          $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
-          $stmt->execute(array(':username' => $username_now));
-        }
-        else {
-          $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username WHERE `username` = :username_now");
-          $stmt->execute(array(':username' => $username, ':username_now' => $username_now));
-        }
-      }
-      catch (PDOException $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'MySQL: '.$e
-        );
-        return false;
-      }
-    }
-    else {
-      try {
-        $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `active` = :active WHERE `username` = :username2");
-        $stmt->execute(array(
-          ':username1' => $username,
-          ':username2' => $username_now,
-          ':active' => $active
-        ));
-        if (isset($postarray['disable_tfa'])) {
-          $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
-          $stmt->execute(array(':username' => $username));
-        }
-        else {
-          $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username WHERE `username` = :username_now");
-          $stmt->execute(array(':username' => $username, ':username_now' => $username_now));
-        }
-      }
-      catch (PDOException $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'MySQL: '.$e
-        );
-        return false;
-      }
-    }
-    $_SESSION['return'] = array(
-      'type' => 'success',
-      'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars($username))
-    );
-  }
-  // Domain administrator
-  // Can only edit itself
-  elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
-    $username = $_SESSION['mailcow_cc_username'];
-    $password_old		= $postarray['user_old_pass'];
-    $password_new	= $postarray['user_new_pass'];
-    $password_new2	= $postarray['user_new_pass2'];
-
-    $stmt = $pdo->prepare("SELECT `password` FROM `admin`
-        WHERE `username` = :user");
-    $stmt->execute(array(':user' => $username));
-    $row = $stmt->fetch(PDO::FETCH_ASSOC);
-    if (!verify_ssha256($row['password'], $password_old)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-
-    if (!empty($password_new2) && !empty($password_new)) {
-      if ($password_new2 != $password_new) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['password_mismatch'])
-        );
-        return false;
-      }
-      if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password_new)) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['password_complexity'])
-        );
-        return false;
-      }
-      $password_hashed = hash_password($password_new);
-      try {
-        $stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
-        $stmt->execute(array(
-          ':password_hashed' => $password_hashed,
-          ':username' => $username
-        ));
-      }
-      catch (PDOException $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'MySQL: '.$e
-        );
-        return false;
-      }
-    }
-    
-    $_SESSION['return'] = array(
-      'type' => 'success',
-      'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars($username))
-    );
-  }
-}
-function get_admin_details() {
-  // No parameter to be given, only one admin should exist
-	global $pdo;
-	global $lang;
-  $data = array();
-  if ($_SESSION['mailcow_cc_role'] != 'admin') {
-    return false;
-  }
-  try {
-    $stmt = $pdo->prepare("SELECT `username`, `modified`, `created` FROM `admin` WHERE `superadmin`='1' AND active='1'");
-    $stmt->execute();
-    $data = $stmt->fetch(PDO::FETCH_ASSOC);
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-  }
-  return $data;
-}
-function dkim_add_key($postarray) {
-	global $lang;
-	global $pdo;
-	global $redis;
-  if ($_SESSION['mailcow_cc_role'] != "admin") {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['access_denied'])
-    );
-    return false;
-  }
-  // if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-    // $_SESSION['return'] = array(
-      // 'type' => 'danger',
-      // 'msg' => sprintf($lang['danger']['access_denied'])
-    // );
-    // return false;
-  // }
-  $key_length	= intval($postarray['key_size']);
-  $dkim_selector = (isset($postarray['dkim_selector'])) ? $postarray['dkim_selector'] : 'dkim';
-  $domain	= $postarray['domain'];
-  if (!is_valid_domain_name($domain) || !is_numeric($key_length)) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
-    );
-    return false;
-  }
-
-  if (!empty(glob($GLOBALS['MC_DKIM_TXTS'] . '/' . $domain . '.dkim')) ||
-    $redis->hGet('DKIM_PUB_KEYS', $domain)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
-      );
-      return false;
-  }
-
-  if (!ctype_alnum($dkim_selector)) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
-    );
-    return false;
-  }
-
-  $config = array(
-    "digest_alg" => "sha256",
-    "private_key_bits" => $key_length,
-    "private_key_type" => OPENSSL_KEYTYPE_RSA,
-  );
-  if ($keypair_ressource = openssl_pkey_new($config)) {
-    $key_details = openssl_pkey_get_details($keypair_ressource);
-    $pubKey = implode(array_slice(
-        array_filter(
-          explode(PHP_EOL, $key_details['key'])
-        ), 1, -1)
-      );
-    // Save public key and selector to redis
-    try {
-      $redis->hSet('DKIM_PUB_KEYS', $domain, $pubKey);
-      $redis->hSet('DKIM_SELECTORS', $domain, $dkim_selector);
-    }
-    catch (RedisException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'Redis: '.$e
-      );
-      return false;
-    }
-    // Export private key and save private key to redis
-    openssl_pkey_export($keypair_ressource, $privKey);
-    if (isset($privKey) && !empty($privKey)) {
-      try {
-        $redis->hSet('DKIM_PRIV_KEYS', $dkim_selector . '.' . $domain, trim($privKey));
-      }
-      catch (RedisException $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'Redis: '.$e
-        );
-        return false;
-      }
-    }
-    $_SESSION['return'] = array(
-      'type' => 'success',
-      'msg' => sprintf($lang['success']['dkim_added'])
-    );
-    return true;
-  }
-  else {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
-    );
-    return false;
-  }
-}
-function dkim_get_key_details($domain) {
-  global $redis;
-  if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-    return false;
-  }
-  $data = array();
-  if ($redis_dkim_key_data = $redis->hGet('DKIM_PUB_KEYS', $domain)) {
-    $data['pubkey'] = $redis_dkim_key_data;
-    $data['length'] = (strlen($data['pubkey']) < 391) ? 1024 : 2048;
-    $data['dkim_txt'] = 'v=DKIM1;k=rsa;t=s;s=email;p=' . $redis_dkim_key_data;
-    $data['dkim_selector'] = $redis->hGet('DKIM_SELECTORS', $domain);
-  }
-  return $data;
-}
-function dkim_get_blind_keys() {
-  global $redis;
-	global $lang;
-  if ($_SESSION['mailcow_cc_role'] != "admin") {
-    return false;
-  }
-  $domains = array();
-  foreach ($redis->hKeys('DKIM_PUB_KEYS') as $redis_dkim_domain) {
-    $domains[] = $redis_dkim_domain;
-  }
-  return array_diff($domains, array_merge(mailbox_get_domains(), mailbox_get_alias_domains()));
-}
-function dkim_delete_key($postarray) {
-	global $redis;
-	global $lang;
-  if (!is_array($postarray['domains'])) {
-    $domains = array();
-    $domains[] = $postarray['domains'];
-  }
-  else {
-    $domains = $postarray['domains'];
-  }
-  if ($_SESSION['mailcow_cc_role'] != "admin") {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => sprintf($lang['danger']['access_denied'])
-    );
-    return false;
-  }
-  foreach ($domains as $domain) {
-    if (!is_valid_domain_name($domain)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
-      );
-      return false;
-    }
-    try {
-      $selector = $redis->hGet('DKIM_SELECTORS', $domain);
-      $redis->hDel('DKIM_PUB_KEYS', $domain);
-      $redis->hDel('DKIM_PRIV_KEYS', $selector . '.' . $domain);
-      $redis->hDel('DKIM_SELECTORS', $domain);
-    }
-    catch (RedisException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'Redis: '.$e
-      );
-      return false;
-    }
-  }
-  $_SESSION['return'] = array(
-    'type' => 'success',
-    'msg' => sprintf($lang['success']['dkim_removed'], htmlspecialchars(implode(', ', $domains)))
-  );
-  return true;
-}
-function mailbox_add_domain($postarray) {
-  // Array elements
-  // domain                 string
-  // description            string
-  // aliases                int
-  // mailboxes              int
-  // maxquota               int
-  // quota                  int
-  // active                 int
-  // relay_all_recipients   int
-  // backupmx               int
-	global $pdo;
-	global $redis;
-	global $lang;
-	if ($_SESSION['mailcow_cc_role'] != "admin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-	$domain				= idn_to_ascii(strtolower(trim($postarray['domain'])));
-	$description  = $postarray['description'];
-	$aliases			= $postarray['aliases'];
-	$mailboxes    = $postarray['mailboxes'];
-	$maxquota			= $postarray['maxquota'];
-	$quota				= $postarray['quota'];
-
-	if ($maxquota > $quota) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['mailbox_quota_exceeds_domain_quota'])
-		);
-		return false;
-	}
-
-	if ($maxquota == "0" || empty($maxquota)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['maxquota_empty'])
-		);
-		return false;
-	}
-
-  $active = intval($postarray['active']);
-  $relay_all_recipients = intval($postarray['relay_all_recipients']);
-  $backupmx = intval($postarray['backupmx']);
-  ($relay_all_recipients == 1) ? $backupmx = '1' : null;
-
-	if (!is_valid_domain_name($domain)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['domain_invalid'])
-		);
-		return false;
-	}
-
-	foreach (array($quota, $maxquota, $mailboxes, $aliases) as $data) {
-		if (!is_numeric($data)) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['object_is_not_numeric'], htmlspecialchars($data))
-			);
-			return false;
-		}
-	}
-
-	try {
-		$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
-			WHERE `domain` = :domain");
-		$stmt->execute(array(':domain' => $domain));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-		$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
-			WHERE `alias_domain` = :domain");
-		$stmt->execute(array(':domain' => $domain));
-		$num_results = $num_results + count($stmt->fetchAll(PDO::FETCH_ASSOC));
-	}
-	catch(PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-	if ($num_results != 0) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['domain_exists'], htmlspecialchars($domain))
-		);
-		return false;
-	}
-
-	try {
-		$stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `maxquota`, `quota`, `transport`, `backupmx`, `active`, `relay_all_recipients`)
-			VALUES (:domain, :description, :aliases, :mailboxes, :maxquota, :quota, 'virtual', :backupmx, :active, :relay_all_recipients)");
-		$stmt->execute(array(
-			':domain' => $domain,
-			':description' => $description,
-			':aliases' => $aliases,
-			':mailboxes' => $mailboxes,
-			':maxquota' => $maxquota,
-			':quota' => $quota,
-			':backupmx' => $backupmx,
-			':active' => $active,
-			':relay_all_recipients' => $relay_all_recipients
-		));
-    try {
-      $redis->hSet('DOMAIN_MAP', $domain, 1);
-    }
-    catch (RedisException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'Redis: '.$e
-      );
-      return false;
-    }
-		$_SESSION['return'] = array(
-			'type' => 'success',
-			'msg' => sprintf($lang['success']['domain_added'], htmlspecialchars($domain))
-		);
-	}
-	catch (PDOException $e) {
-    mailbox_delete_domain(array('domain' => $domain));
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-}
-function mailbox_add_alias($postarray) {
-  // Array elements
-  // address  string  (separated by " ", "," ";" "\n") - email address or domain
-  // goto     string  (separated by " ", "," ";" "\n")
-  // active   int
-	global $lang;
-	global $pdo;
-	$addresses  = array_map('trim', preg_split( "/( |,|;|\n)/", $postarray['address']));
-	$gotos      = array_map('trim', preg_split( "/( |,|;|\n)/", $postarray['goto']));
-  $active = intval($postarray['active']);
-	if (empty($addresses[0])) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['alias_empty'])
-		);
-		return false;
-	}
-
-	if (empty($gotos[0])) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['goto_empty'])
-		);
-		return false;
-	}
-	foreach ($addresses as $address) {
-		if (empty($address)) {
-			continue;
-		}
-
-    $stmt = $pdo->prepare("SELECT `address` FROM `alias`
-      WHERE `address`= :address");
-    $stmt->execute(array(':address' => $address));
-    $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results != 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['is_alias_or_mailbox'], htmlspecialchars($address))
-      );
-      return false;
-    }
-
-		$domain       = idn_to_ascii(substr(strstr($address, '@'), 1));
-		$local_part   = strstr($address, '@', true);
-		$address      = $local_part.'@'.$domain;
-
-    $domaindata = mailbox_get_domain_details($domain);
-    if (is_array($domaindata) && $domaindata['aliases_left'] == "0") {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['max_alias_exceeded'])
-      );
-      return false;
-    }
-
-		try {
-			$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
-				WHERE `domain`= :domain1 OR `domain` = (SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain2)");
-			$stmt->execute(array(':domain1' => $domain, ':domain2' => $domain));
-			$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-      if ($num_results == 0) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['domain_not_found'], htmlspecialchars($domain))
-        );
-        return false;
-      }
-
-			$stmt = $pdo->prepare("SELECT `address` FROM `alias`
-				WHERE `address`= :address");
-			$stmt->execute(array(':address' => $address));
-			$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-      if ($num_results != 0) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['is_alias_or_mailbox'], htmlspecialchars($address))
-        );
-        return false;
-      }
-
-			$stmt = $pdo->prepare("SELECT `address` FROM `spamalias`
-				WHERE `address`= :address");
-			$stmt->execute(array(':address' => $address));
-			$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-      if ($num_results != 0) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['is_spam_alias'], htmlspecialchars($address))
-        );
-        return false;
-      }
-		}
-		catch(PDOException $e) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => 'MySQL: '.$e
-			);
-			return false;
-		}
-
-		if ((!filter_var($address, FILTER_VALIDATE_EMAIL) === true) && !empty($local_part)) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['alias_invalid'])
-			);
-			return false;
-		}
-
-		if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['access_denied'])
-			);
-			return false;
-		}
-
-		foreach ($gotos as &$goto) {
-			if (empty($goto)) {
-				continue;
-			}
-
-			$goto_domain		= idn_to_ascii(substr(strstr($goto, '@'), 1));
-			$goto_local_part	= strstr($goto, '@', true);
-			$goto				= $goto_local_part.'@'.$goto_domain;
-
-			$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
-				WHERE `kind` REGEXP 'location|thing|group'
-          AND `username`= :goto");
-			$stmt->execute(array(':goto' => $goto));
-			$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-      if ($num_results != 0) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => sprintf($lang['danger']['goto_invalid'])
-				);
-				return false;
-      }
-
-			if (!filter_var($goto, FILTER_VALIDATE_EMAIL) === true) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => sprintf($lang['danger']['goto_invalid'])
-				);
-				return false;
-			}
-			if ($goto == $address) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => sprintf($lang['danger']['alias_goto_identical'])
-				);
-				return false;
-			}
-		}
-
-		$gotos = array_filter($gotos);
-		$goto = implode(",", $gotos);
-
-		try {
-			$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `active`)
-				VALUES (:address, :goto, :domain, :active)");
-
-			if (!filter_var($address, FILTER_VALIDATE_EMAIL) === true) {
-				$stmt->execute(array(
-					':address' => '@'.$domain,
-					':goto' => $goto,
-					':domain' => $domain,
-					':active' => $active
-				));
-			}
-			else {
-				$stmt->execute(array(
-					':address' => $address,
-					':goto' => $goto,
-					':domain' => $domain,
-					':active' => $active
-				));
-			}
-			$_SESSION['return'] = array(
-				'type' => 'success',
-				'msg' => sprintf($lang['success']['alias_added'])
-			);
-		}
-		catch (PDOException $e) {
-      mailbox_delete_alias(array('address' => $address));
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => 'MySQL: '.$e
-			);
-			return false;
-		}
-	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['alias_added'])
-	);
-}
-function mailbox_add_alias_domain($postarray) {
-  // Array elements
-  // active         int
-  // alias_domain   string
-  // target_domain  string
-	global $lang;
-	global $pdo;
-  $active = intval($postarray['active']);
-	$alias_domain     = idn_to_ascii(strtolower(trim($postarray['alias_domain'])));
-	$target_domain    = idn_to_ascii(strtolower(trim($postarray['target_domain'])));
-
-	if (!is_valid_domain_name($alias_domain)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['alias_domain_invalid'])
-		);
-		return false;
-	}
-
-	if (!is_valid_domain_name($target_domain)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['target_domain_invalid'])
-		);
-		return false;
-	}
-
-	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $target_domain)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-
-	if ($alias_domain == $target_domain) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['aliasd_targetd_identical'])
-		);
-		return false;
-	}
-
-	try {
-		$stmt = $pdo->prepare("SELECT `domain` FROM `domain`
-			WHERE `domain`= :target_domain");
-		$stmt->execute(array(':target_domain' => $target_domain));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results == 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['targetd_not_found'])
-      );
-      return false;
-    }
-
-		$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `alias_domain`= :alias_domain
-			UNION
-			SELECT `alias_domain` FROM `alias_domain` WHERE `alias_domain`= :alias_domain_in_domain");
-		$stmt->execute(array(':alias_domain' => $alias_domain, ':alias_domain_in_domain' => $alias_domain));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results != 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['aliasd_exists'])
-      );
-      return false;
-    }
-  }
-	catch(PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-
-	try {
-		$stmt = $pdo->prepare("INSERT INTO `alias_domain` (`alias_domain`, `target_domain`, `active`)
-			VALUES (:alias_domain, :target_domain, :active)");
-		$stmt->execute(array(
-			':alias_domain' => $alias_domain,
-			':target_domain' => $target_domain,
-			':active' => $active
-		));
-		$_SESSION['return'] = array(
-			'type' => 'success',
-			'msg' => sprintf($lang['success']['aliasd_added'], htmlspecialchars($alias_domain))
-		);
-	}
-	catch (PDOException $e) {
-    mailbox_delete_alias_domain(array('alias_domain' => $alias_domain));
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-}
-function mailbox_add_mailbox($postarray) {
-  // Array elements
-  // active             int
-  // local_part         string
-  // domain             string
-  // name               string    (username if empty)
-  // password           string
-  // password2          string
-  // quota              int       (MiB)
-  // active             int
-
-	global $pdo;
-	global $lang;
-	$local_part   = strtolower(trim($postarray['local_part']));
-	$domain       = idn_to_ascii(strtolower(trim($postarray['domain'])));
-  $username     = $local_part . '@' . $domain;
-	if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['mailbox_invalid'])
-		);
-		return false;
-	}
-	if (empty($postarray['local_part'])) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['mailbox_invalid'])
-		);
-		return false;
-	}
-	$password     = $postarray['password'];
-	$password2    = $postarray['password2'];
-	$name         = $postarray['name'];
-  $quota_m			= filter_var($postarray['quota'], FILTER_SANITIZE_NUMBER_FLOAT);
-
-	if (empty($name)) {
-		$name = $local_part;
-	}
-
-  $active = intval($postarray['active']);
-
-	$quota_b		= ($quota_m * 1048576);
-	$maildir		= $domain."/".$local_part."/";
-
-	if (!is_valid_domain_name($domain)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['domain_invalid'])
-		);
-		return false;
-	}
-
-	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-
-	try {
-		$stmt = $pdo->prepare("SELECT `mailboxes`, `maxquota`, `quota` FROM `domain`
-			WHERE `domain` = :domain");
-		$stmt->execute(array(':domain' => $domain));
-		$DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
-
-		$stmt = $pdo->prepare("SELECT 
-			COUNT(*) as count,
-			COALESCE(ROUND(SUM(`quota`)/1048576), 0) as `quota`
-				FROM `mailbox`
-					WHERE `kind` NOT REGEXP 'location|thing|group'
-            AND `domain` = :domain");
-		$stmt->execute(array(':domain' => $domain));
-		$MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
-
-		$stmt = $pdo->prepare("SELECT `local_part` FROM `mailbox` WHERE `local_part` = :local_part and `domain`= :domain");
-		$stmt->execute(array(':local_part' => $local_part, ':domain' => $domain));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results != 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($username))
-      );
-      return false;
-    }
-
-		$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE address= :username");
-		$stmt->execute(array(':username' => $username));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results != 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['is_alias'], htmlspecialchars($username))
-      );
-      return false;
-    }
-
-		$stmt = $pdo->prepare("SELECT `address` FROM `spamalias` WHERE `address`= :username");
-		$stmt->execute(array(':username' => $username));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results != 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['is_spam_alias'], htmlspecialchars($username))
-      );
-      return false;
-    }
-
-		$stmt = $pdo->prepare("SELECT `domain` FROM `domain` WHERE `domain`= :domain");
-		$stmt->execute(array(':domain' => $domain));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results == 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['domain_not_found'], $domain)
-      );
-      return false;
-    }
-  }
-	catch(PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-
-	if (!is_numeric($quota_m) || $quota_m == "0") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['quota_not_0_not_numeric'])
-		);
-		return false;
-	}
-
-	if (!empty($password) && !empty($password2)) {
-    if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['password_complexity'])
-      );
-      return false;
-    }
-		if ($password != $password2) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['password_mismatch'])
-			);
-			return false;
-		}
-		$password_hashed = hash_password($password);
-	}
-	else {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['password_empty'])
-		);
-		return false;
-	}
-
-	if ($MailboxData['count'] >= $DomainData['mailboxes']) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['max_mailbox_exceeded'], $MailboxData['count'], $DomainData['mailboxes'])
-		);
-		return false;
-	}
-
-	if ($quota_m > $DomainData['maxquota']) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['mailbox_quota_exceeded'], $DomainData['maxquota'])
-		);
-		return false;
-	}
-
-	if (($MailboxData['quota'] + $quota_m) > $DomainData['quota']) {
-		$quota_left_m = ($DomainData['quota'] - $MailboxData['quota']);
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['mailbox_quota_left_exceeded'], $quota_left_m)
-		);
-		return false;
-	}
-
-	try {
-		$stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `active`) 
-			VALUES (:username, :password_hashed, :name, :maildir, :quota_b, :local_part, :domain, :active)");
-		$stmt->execute(array(
-			':username' => $username,
-			':password_hashed' => $password_hashed,
-			':name' => $name,
-			':maildir' => $maildir,
-			':quota_b' => $quota_b,
-			':local_part' => $local_part,
-			':domain' => $domain,
-			':active' => $active
-		));
-
-		$stmt = $pdo->prepare("INSERT INTO `quota2` (`username`, `bytes`, `messages`)
-			VALUES (:username, '0', '0')");
-		$stmt->execute(array(':username' => $username));
-
-		$stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `active`)
-			VALUES (:username1, :username2, :domain, :active)");
-		$stmt->execute(array(
-			':username1' => $username,
-			':username2' => $username,
-			':domain' => $domain,
-			':active' => $active
-		));
-
-		$_SESSION['return'] = array(
-			'type' => 'success',
-			'msg' => sprintf($lang['success']['mailbox_added'], htmlspecialchars($username))
-		);
-	}
-	catch (PDOException $e) {
-    mailbox_delete_mailbox(array('username' => $username));
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-}
-function mailbox_add_resource($postarray) {
-  // Array elements
-  // active             int
-  // domain             string
-  // description        string
-  // multiple_bookings  int
-  // kind               string
-
-	global $pdo;
-	global $lang;
-	$domain             = idn_to_ascii(strtolower(trim($postarray['domain'])));
-  $description        = $postarray['description'];
-  $local_part         = preg_replace('/[^\da-z]/i', '', preg_quote($description, '/'));
-  $name               = $local_part . '@' . $domain;
-  $kind               = $postarray['kind'];
-  $active = intval($postarray['active']);
-  $multiple_bookings = intval($postarray['multiple_bookings']);
-
-	if (!filter_var($name, FILTER_VALIDATE_EMAIL)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['resource_invalid'])
-		);
-		return false;
-	}
-
-	if (empty($description)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['description_invalid'])
-		);
-		return false;
-  }
-  
-	if ($kind != 'location' && $kind != 'group' && $kind != 'thing') {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['resource_invalid'])
-		);
-		return false;
-	}
-
-	if (!is_valid_domain_name($domain)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['domain_invalid'])
-		);
-		return false;
-	}
-
-	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-
-	try {
-		$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :name");
-		$stmt->execute(array(':name' => $name));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results != 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($name))
-      );
-      return false;
-    }
-
-		$stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE address= :name");
-		$stmt->execute(array(':name' => $name));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results != 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['is_alias'], htmlspecialchars($name))
-      );
-      return false;
-    }
-
-		$stmt = $pdo->prepare("SELECT `address` FROM `spamalias` WHERE `address`= :name");
-		$stmt->execute(array(':name' => $name));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results != 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['is_spam_alias'], htmlspecialchars($name))
-      );
-      return false;
-    }
-
-		$stmt = $pdo->prepare("SELECT `domain` FROM `domain` WHERE `domain`= :domain");
-		$stmt->execute(array(':domain' => $domain));
-		$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-    if ($num_results == 0) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['domain_not_found'], $domain)
-      );
-      return false;
-    }
-  }
-	catch(PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-
-	try {
-		$stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `active`, `multiple_bookings`, `kind`) 
-			VALUES (:name, 'RESOURCE', :description, 'RESOURCE', 0, :local_part, :domain, :active, :multiple_bookings, :kind)");
-		$stmt->execute(array(
-			':name' => $name,
-			':description' => $description,
-			':local_part' => $local_part,
-			':domain' => $domain,
-			':active' => $active,
-			':kind' => $kind,
-			':multiple_bookings' => $multiple_bookings
-		));
-
-		$_SESSION['return'] = array(
-			'type' => 'success',
-			'msg' => sprintf($lang['success']['resource_added'], htmlspecialchars($name))
-		);
-	}
-	catch (PDOException $e) {
-    mailbox_delete_resource(array('name' => $name));
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-}
-function mailbox_edit_alias_domain($postarray) {
-  // active         int
-  // target_domain  string
-  // alias_domain   string/array of strings
-	global $lang;
-	global $pdo;
-  if (!is_array($postarray['alias_domain'])) {
-    $alias_domains = array();
-    $alias_domains[] = $postarray['alias_domain'];
-  }
-  else {
-    $alias_domains = $postarray['alias_domain'];
-  }
-  foreach ($alias_domains as $alias_domain) {
-    $alias_domain = idn_to_ascii(strtolower(trim($alias_domain)));
-    $is_now = mailbox_get_alias_domain_details($alias_domain);
-    if (!empty($is_now)) {
-      $active         = (isset($postarray['active'])) ? $postarray['active'] : $is_now['active_int'];
-      $target_domain  = (!empty($postarray['target_domain'])) ? idn_to_ascii(strtolower(trim($postarray['target_domain']))) : $is_now['target_domain'];
-    }
-    else {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['alias_domain_invalid'])
-      );
-      return false;
-    }
-    if (!is_valid_domain_name($target_domain)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['target_domain_invalid'])
-      );
-      return false;
-    }
-    if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $target_domain)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    if (empty(mailbox_get_domain_details($target_domain))) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['target_domain_invalid'])
-      );
-      return false;
-    }
-    try {
-      $stmt = $pdo->prepare("UPDATE `alias_domain` SET
-        `target_domain` = :target_domain,
-        `active` = :active
-          WHERE `alias_domain` = :alias_domain");
-      $stmt->execute(array(
-        ':alias_domain' => $alias_domain,
-        ':target_domain' => $target_domain,
-        ':active' => $active
-      ));
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  }
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['aliasd_modified'], htmlspecialchars(implode(', ', $alias_domains)))
-	);
-}
-function mailbox_edit_alias($postarray) {
-  // We can edit multiple addresses at once, but only set one "goto" and/or "active" attribute for all
-  // address    string or array containing strings | email | required
-  // goto       string | separated by " ", "," ";" "\n", email or domain | optional
-  // active     set (active) or unset (inactive)
-	global $lang;
-	global $pdo;
-  if (!is_array($postarray['address'])) {
-    $addresses = array();
-    $addresses[] = $postarray['address'];
-  }
-  else {
-    $addresses = $postarray['address'];
-  }
-  foreach ($addresses as $address) {
-    $is_now = mailbox_get_alias_details($address);
-    if (!empty($is_now)) {
-      $active = (isset($postarray['active'])) ? $postarray['active'] : $is_now['active_int'];
-      $goto   = (!empty($postarray['goto'])) ? $postarray['goto'] : $is_now['goto'];
-    }
-    else {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['alias_invalid'])
-      );
-      return false;
-    }
-    
-    $gotos = array_map('trim', preg_split( "/( |,|;|\n)/", $postarray['goto']));
-    foreach ($gotos as &$goto) {
-      if (empty($goto)) {
-        continue;
-      }
-      if (!filter_var($goto, FILTER_VALIDATE_EMAIL)) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' =>sprintf($lang['danger']['goto_invalid'])
-        );
-        return false;
-      }
-      if ($goto == $address) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['alias_goto_identical'])
-        );
-        return false;
-      }
-    }
-    $gotos = array_filter($gotos);
-    $goto = implode(",", $gotos);
-    
-    $domain = idn_to_ascii(substr(strstr($address, '@'), 1));
-    $local_part = strstr($address, '@', true);
-
-    if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-
-    if ((!filter_var($address, FILTER_VALIDATE_EMAIL) === true) && !empty($local_part)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['alias_invalid'])
-      );
-      return false;
-    }
-
-    try {
-      if (!empty($goto)) {
-        $stmt = $pdo->prepare("UPDATE `alias` SET
-          `goto` = :goto,
-          `active`= :active
-            WHERE `address` = :address");
-        $stmt->execute(array(
-          ':goto' => $goto,
-          ':active' => $active,
-          ':address' => $address
-        ));
-      }
-      else {
-        $stmt = $pdo->prepare("UPDATE `alias` SET
-          `active`= :active
-            WHERE `address` = :address");
-        $stmt->execute(array(
-          ':active' => $active,
-          ':address' => $address
-        ));
-      }
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-	}
-  $_SESSION['return'] = array(
-    'type' => 'success',
-    'msg' => sprintf($lang['success']['alias_modified'], htmlspecialchars(implode(', ', $addresses)))
-  );
-}
-function mailbox_edit_domain($postarray) {
-  // Array elements
-  // domain                 string
-  // description            string
-  // active                 int
-  // relay_all_recipients   int
-  // backupmx               int
-  // aliases                float
-  // mailboxes              float
-  // maxquota               float
-  // quota                  float (Byte)
-  // active                 int
-
-	global $lang;
-	global $pdo;
-  
-  if (!is_array($postarray['domain'])) {
-    $domains = array();
-    $domains[] = $postarray['domain'];
-  }
-  else {
-    $domains = $postarray['domain'];
-  }
-  foreach ($domains as $domain) {
-    $domain = idn_to_ascii($domain);
-    if (!is_valid_domain_name($domain)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['domain_invalid'])
-      );
-      return false;
-    }
-    if ($_SESSION['mailcow_cc_role'] == "domainadmin" &&
-    hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-      $description  = $postarray['description'];
-      $active = intval($postarray['active']);
-      try {
-        $stmt = $pdo->prepare("UPDATE `domain` SET 
-        `description` = :description
-          WHERE `domain` = :domain");
-        $stmt->execute(array(
-          ':description' => $description,
-          ':domain' => $domain
-        ));
-        $_SESSION['return'] = array(
-          'type' => 'success',
-          'msg' => sprintf($lang['success']['domain_modified'], htmlspecialchars($domain))
-        );
-      }
-      catch (PDOException $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'MySQL: '.$e
-        );
-        return false;
-      }
-    }
-    elseif ($_SESSION['mailcow_cc_role'] == "admin") {
-      $is_now = mailbox_get_domain_details($domain);
-      if (!empty($is_now)) {
-        $active               = (isset($postarray['active'])) ? $postarray['active'] : $is_now['active_int'];
-        $backupmx             = (isset($postarray['backupmx'])) ? $postarray['backupmx'] : $is_now['backupmx_int'];
-        $relay_all_recipients = (isset($postarray['relay_all_recipients'])) ? $postarray['relay_all_recipients'] : $is_now['relay_all_recipients_int'];
-        $aliases              = (!empty($postarray['aliases'])) ? $postarray['aliases'] : $is_now['max_num_aliases_for_domain'];
-        $mailboxes            = (!empty($postarray['mailboxes'])) ? $postarray['mailboxes'] : $is_now['max_num_mboxes_for_domain'];
-        $maxquota             = (!empty($postarray['maxquota'])) ? $postarray['maxquota'] : ($is_now['max_new_mailbox_quota'] / 1048576);
-        $quota                = (!empty($postarray['quota'])) ? $postarray['quota'] : ($is_now['max_quota_for_domain'] / 1048576);
-        $description          = (!empty($postarray['description'])) ? $postarray['description'] : $is_now['description'];
-        ($relay_all_recipients == '1') ? $backupmx = '1' : null;
-      }
-      else {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['domain_invalid'])
-        );
-        return false;
-      }
-      try {
-        // todo: should be using api here
-        $stmt = $pdo->prepare("SELECT 
-            COUNT(*) AS count,
-            MAX(COALESCE(ROUND(`quota`/1048576), 0)) AS `biggest_mailbox`,
-            COALESCE(ROUND(SUM(`quota`)/1048576), 0) AS `quota_all`
-              FROM `mailbox`
-                WHERE `kind` NOT REGEXP 'location|thing|group'
-                  AND domain = :domain");
-        $stmt->execute(array(':domain' => $domain));
-        $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
-        // todo: should be using api here
-        $stmt = $pdo->prepare("SELECT COUNT(*) AS `count` FROM `alias`
-            WHERE domain = :domain
-            AND address NOT IN (
-              SELECT `username` FROM `mailbox`
-            )");
-        $stmt->execute(array(':domain' => $domain));
-        $AliasData = $stmt->fetch(PDO::FETCH_ASSOC);
-      }
-      catch(PDOException $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'MySQL: '.$e
-        );
-        return false;
-      }
-
-      if ($maxquota > $quota) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['mailbox_quota_exceeds_domain_quota'])
-        );
-        return false;
-      }
-
-      if ($maxquota == "0" || empty($maxquota)) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['maxquota_empty'])
-        );
-        return false;
-      }
-
-      if ($MailboxData['biggest_mailbox'] > $maxquota) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['max_quota_in_use'], $MailboxData['biggest_mailbox'])
-        );
-        return false;
-      }
-
-      if ($MailboxData['quota_all'] > $quota) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['domain_quota_m_in_use'], $MailboxData['quota_all'])
-        );
-        return false;
-      }
-
-      if ($MailboxData['count'] > $mailboxes) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['mailboxes_in_use'], $MailboxData['count'])
-        );
-        return false;
-      }
-
-      if ($AliasData['count'] > $aliases) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['aliases_in_use'], $AliasData['count'])
-        );
-        return false;
-      }
-      try {
-        $stmt = $pdo->prepare("UPDATE `domain` SET 
-        `relay_all_recipients` = :relay_all_recipients,
-        `backupmx` = :backupmx,
-        `active` = :active,
-        `quota` = :quota,
-        `maxquota` = :maxquota,
-        `mailboxes` = :mailboxes,
-        `aliases` = :aliases,
-        `description` = :description
-          WHERE `domain` = :domain");
-        $stmt->execute(array(
-          ':relay_all_recipients' => $relay_all_recipients,
-          ':backupmx' => $backupmx,
-          ':active' => $active,
-          ':quota' => $quota,
-          ':maxquota' => $maxquota,
-          ':mailboxes' => $mailboxes,
-          ':aliases' => $aliases,
-          ':description' => $description,
-          ':domain' => $domain
-        ));
-        $_SESSION['return'] = array(
-          'type' => 'success',
-          'msg' => sprintf($lang['success']['domain_modified'], htmlspecialchars($domain))
-        );
-      }
-      catch (PDOException $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'MySQL: '.$e
-        );
-        return false;
-      }
-    }
-  }
-}
-function mailbox_edit_mailbox($postarray) {
-	global $lang;
-	global $pdo;
-  if (!is_array($postarray['username'])) {
-    $usernames = array();
-    $usernames[] = $postarray['username'];
-  }
-  else {
-    $usernames = $postarray['username'];
-  }
-  foreach ($usernames as $username) {
-    if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['username_invalid'])
-      );
-      return false;
-    }
-    $is_now = mailbox_get_mailbox_details($username);
-    if (!empty($is_now)) {
-      $active     = (isset($postarray['active'])) ? $postarray['active'] : $is_now['active_int'];
-      $name       = (!empty($postarray['name'])) ? $postarray['name'] : $is_now['name'];
-      $domain     = $is_now['domain'];
-      $quota_m    = (!empty($postarray['quota'])) ? $postarray['quota'] : ($is_now['quota'] / 1048576);
-      $quota_b    = $quota_m * 1048576;
-      $password   = (!empty($postarray['password'])) ? $postarray['password'] : null;
-      $password2  = (!empty($postarray['password2'])) ? $postarray['password2'] : null; 
-    }
-    else {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    try {
-      $stmt = $pdo->prepare("SELECT `quota`, `maxquota`
-        FROM `domain`
-          WHERE `domain` = :domain");
-      $stmt->execute(array(':domain' => $domain));
-      $DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
-    }
-    catch(PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-
-    if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    if (!is_numeric($quota_m) || $quota_m == "0") {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['quota_not_0_not_numeric'], htmlspecialchars($quota_m))
-      );
-      return false;
-    }
-    if ($quota_m > $DomainData['maxquota']) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['mailbox_quota_exceeded'], $DomainData['maxquota'])
-      );
-      return false;
-    }
-    if (((($is_now['quota_used'] / 1048576) - $quota_m) + $quota_m) > $DomainData['quota']) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['mailbox_quota_left_exceeded'], ($is_now['max_new_quota'] / 1048576))
-      );
-      return false;
-    }
-
-    if (isset($postarray['sender_acl'])) {
-      // Get sender_acl items set by admin
-      $sender_acl_admin = array_merge(
-        mailbox_get_sender_acl_handles($username)['sender_acl_domains']['ro'],
-        mailbox_get_sender_acl_handles($username)['sender_acl_addresses']['ro']
-      );
-
-      // Get sender_acl items from POST array
-      $sender_acl_domain_admin = ($postarray['sender_acl'] == "0") ? array() : $postarray['sender_acl'];
-
-      if (!empty($sender_acl_domain_admin) || !empty($sender_acl_admin)) {
-        // Check items in POST array
-        foreach ($sender_acl_domain_admin as $sender_acl) {
-          if (!filter_var($sender_acl, FILTER_VALIDATE_EMAIL) && !is_valid_domain_name(ltrim($sender_acl, '@'))) {
-              $_SESSION['return'] = array(
-                'type' => 'danger',
-                'msg' => sprintf($lang['danger']['sender_acl_invalid'])
-              );
-              return false;
-          }
-          if (is_valid_domain_name(ltrim($sender_acl, '@'))) {
-            if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], ltrim($sender_acl, '@'))) {
-              $_SESSION['return'] = array(
-                'type' => 'danger',
-                'msg' => sprintf($lang['danger']['sender_acl_invalid'])
-              );
-              return false;
-            }
-          }
-          if (filter_var($sender_acl, FILTER_VALIDATE_EMAIL)) {
-            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $sender_acl)) {
-              $_SESSION['return'] = array(
-                'type' => 'danger',
-                'msg' => sprintf($lang['danger']['sender_acl_invalid'])
-              );
-              return false;
-            }
-          }
-        }
-
-        // Merge both arrays
-        $sender_acl_merged = array_merge($sender_acl_domain_admin, $sender_acl_admin);
-
-        try {
-          $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
-          $stmt->execute(array(
-            ':username' => $username
-          ));
-        }
-        catch (PDOException $e) {
-          $_SESSION['return'] = array(
-            'type' => 'danger',
-            'msg' => 'MySQL: '.$e
-          );
-          return false;
-        }
-
-        foreach ($sender_acl_merged as $sender_acl) {
-          $domain = ltrim($sender_acl, '@');
-          if (is_valid_domain_name($domain)) {
-            $sender_acl = '@' . $domain;
-          }
-          try {
-            $stmt = $pdo->prepare("INSERT INTO `sender_acl` (`send_as`, `logged_in_as`)
-              VALUES (:sender_acl, :username)");
-            $stmt->execute(array(
-              ':sender_acl' => $sender_acl,
-              ':username' => $username
-            ));
-          }
-          catch (PDOException $e) {
-            $_SESSION['return'] = array(
-              'type' => 'danger',
-              'msg' => 'MySQL: '.$e
-            );
-            return false;
-          }
-        }
-      }
-      else {
-        try {
-          $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
-          $stmt->execute(array(
-            ':username' => $username
-          ));
-        }
-        catch (PDOException $e) {
-          $_SESSION['return'] = array(
-            'type' => 'danger',
-            'msg' => 'MySQL: '.$e
-          );
-          return false;
-        }
-      }
-    }
-    if (!empty($password) && !empty($password2)) {
-      if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['password_complexity'])
-        );
-        return false;
-      }
-      if ($password != $password2) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['password_mismatch'])
-        );
-        return false;
-      }
-      $password_hashed = hash_password($password);
-      try {
-        $stmt = $pdo->prepare("UPDATE `alias` SET
-            `active` = :active
-              WHERE `address` = :address");
-        $stmt->execute(array(
-          ':address' => $username,
-          ':active' => $active
-        ));
-        $stmt = $pdo->prepare("UPDATE `mailbox` SET
-            `active` = :active,
-            `password` = :password_hashed,
-            `name`= :name,
-            `quota` = :quota_b
-              WHERE `username` = :username");
-        $stmt->execute(array(
-          ':password_hashed' => $password_hashed,
-          ':active' => $active,
-          ':name' => $name,
-          ':quota_b' => $quota_b,
-          ':username' => $username
-        ));
-      }
-      catch (PDOException $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'MySQL: '.$e
-        );
-        return false;
-      }
-    }
-    try {
-      $stmt = $pdo->prepare("UPDATE `alias` SET
-          `active` = :active
-            WHERE `address` = :address");
-      $stmt->execute(array(
-        ':address' => $username,
-        ':active' => $active
-      ));
-      $stmt = $pdo->prepare("UPDATE `mailbox` SET
-          `active` = :active,
-          `name`= :name,
-          `quota` = :quota_b
-            WHERE `username` = :username");
-      $stmt->execute(array(
-        ':active' => $active,
-        ':name' => $name,
-        ':quota_b' => $quota_b,
-        ':username' => $username
-      ));
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  }
-  $_SESSION['return'] = array(
-    'type' => 'success',
-    'msg' => sprintf($lang['success']['mailbox_modified'], implode(', ', $usernames))
-  );
-  return true;
-}
-function mailbox_edit_resource($postarray) {
-	global $lang;
-	global $pdo;
-
-  if (!is_array($postarray['name'])) {
-    $names = array();
-    $names[] = $postarray['name'];
-  }
-  else {
-    $names = $postarray['name'];
-  }
-  foreach ($names as $name) {
-    $is_now = mailbox_get_resource_details($name);
-    if (!empty($is_now)) {
-      $active             = (isset($postarray['active'])) ? $postarray['active'] : $is_now['active_int'];
-      $multiple_bookings  = (isset($postarray['multiple_bookings'])) ? $postarray['multiple_bookings'] : $is_now['multiple_bookings_int'];
-      $description        = (!empty($postarray['description'])) ? $postarray['description'] : $is_now['description'];
-      $kind               = (!empty($postarray['kind'])) ? $postarray['kind'] : $is_now['kind'];
-    }
-    else {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['resource_invalid'])
-      );
-      return false;
-    }
-
-    if (!filter_var($name, FILTER_VALIDATE_EMAIL)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['resource_invalid'])
-      );
-      return false;
-    }
-
-    if (empty($description)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['description_invalid'])
-      );
-      return false;
-    }
-
-    if ($kind != 'location' && $kind != 'group' && $kind != 'thing') {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['resource_invalid'])
-      );
-      return false;
-    }
-
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $name)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-
-    try {
-      $stmt = $pdo->prepare("UPDATE `mailbox` SET
-          `active` = :active,
-          `name`= :description,
-          `kind`= :kind,
-          `multiple_bookings`= :multiple_bookings
-            WHERE `username` = :name");
-      $stmt->execute(array(
-        ':active' => $active,
-        ':description' => $description,
-        ':multiple_bookings' => $multiple_bookings,
-        ':kind' => $kind,
-        ':name' => $name
-      ));
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-	}
-  $_SESSION['return'] = array(
-    'type' => 'success',
-    'msg' => sprintf($lang['success']['resource_modified'], implode(', ', $names))
-  );
-  return true;
-}
-function mailbox_get_mailboxes($domain = null) {
-	global $lang;
-	global $pdo;
-  $mailboxes = array();
-	if (isset($domain) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-		return false;
-	}
-  elseif (isset($domain) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-    try {
-      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` != 'ALL' AND `domain` = :domain");
-      $stmt->execute(array(
-        ':domain' => $domain,
-      ));
-      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-      while($row = array_shift($rows)) {
-        $mailboxes[] = $row['username'];
-      }
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  }
-  else {
-    try {
-      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role");
-      $stmt->execute(array(
-        ':username' => $_SESSION['mailcow_cc_username'],
-        ':role' => $_SESSION['mailcow_cc_role'],
-      ));
-      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-      while($row = array_shift($rows)) {
-        $mailboxes[] = $row['username'];
-      }
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  }
-  return $mailboxes;
-}
-function mailbox_get_resources($domain = null) {
-	global $lang;
-	global $pdo;
-  $resources = array();
-	if (isset($domain) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-		return false;
-	}
-  elseif (isset($domain) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-    try {
-      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `domain` != 'ALL' AND `domain` = :domain");
-      $stmt->execute(array(
-        ':domain' => $domain,
-      ));
-      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-      while($row = array_shift($rows)) {
-        $resources[] = $row['username'];
-      }
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  }
-  else {
-    try {
-      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role");
-      $stmt->execute(array(
-        ':username' => $_SESSION['mailcow_cc_username'],
-        ':role' => $_SESSION['mailcow_cc_role'],
-      ));
-      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-      while($row = array_shift($rows)) {
-        $resources[] = $row['username'];
-      }
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  }
-  return $resources;
-}
-function mailbox_get_alias_domains($domain = null) {
-  // Get all domains assigned to mailcow_cc_username or domain, if set
-  // Domain admin needs to be active
-  // Domain does not need to be active
-	global $lang;
-	global $pdo;
-  $aliasdomains = array();
-	if (isset($domain) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-		return false;
-  }
-  elseif (isset($domain) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-    try {
-      $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = :domain");
-      $stmt->execute(array(
-        ':domain' => $domain,
-      ));
-      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-      while($row = array_shift($rows)) {
-        $aliasdomains[] = $row['alias_domain'];
-      }
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  }
-	else {
-    try {
-      $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role");
-      $stmt->execute(array(
-        ':username' => $_SESSION['mailcow_cc_username'],
-        ':role' => $_SESSION['mailcow_cc_role'],
-      ));
-      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-      while($row = array_shift($rows)) {
-        $aliasdomains[] = $row['alias_domain'];
-      }
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  }
-  return $aliasdomains;
-}
-function mailbox_get_aliases($domain) {
-	global $lang;
-	global $pdo;
-  $aliases = array();
-	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-		return false;
-	}
-
-  try {
-    $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `address` != `goto` AND `domain` = :domain");
-    $stmt->execute(array(
-      ':domain' => $domain,
-    ));
-    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while($row = array_shift($rows)) {
-      $aliases[] = $row['address'];
-    }
-  }
-  catch (PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-  return $aliases;
-}
-function mailbox_get_alias_details($address) {
-	global $lang;
-	global $pdo;
-  $aliasdata = array();
-  try {
-    $stmt = $pdo->prepare("SELECT
-      `domain`,
-      `goto`,
-      `address`,
-      `active` as `active_int`,
-      CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
-      `created`,
-      `modified`
-        FROM `alias`
-            WHERE `address` = :address AND `address` != `goto`");
-    $stmt->execute(array(
-      ':address' => $address,
-    ));
-    $row = $stmt->fetch(PDO::FETCH_ASSOC);
-    $stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain");
-    $stmt->execute(array(
-      ':domain' => $row['domain'],
-    ));
-    $row_alias_domain = $stmt->fetch(PDO::FETCH_ASSOC);
-    if (isset($row_alias_domain['target_domain']) && !empty($row_alias_domain['target_domain'])) {
-      $aliasdata['in_primary_domain'] = $row_alias_domain['target_domain'];
-    }
-    else {
-      $aliasdata['in_primary_domain'] = "";
-    }
-    $aliasdata['domain'] = $row['domain'];
-    $aliasdata['goto'] = $row['goto'];
-    $aliasdata['address'] = $row['address'];
-    (!filter_var($aliasdata['address'], FILTER_VALIDATE_EMAIL)) ? $aliasdata['is_catch_all'] = 1 : $aliasdata['is_catch_all'] = 0;
-    $aliasdata['active'] = $row['active'];
-    $aliasdata['active_int'] = $row['active_int'];
-    $aliasdata['created'] = $row['created'];
-    $aliasdata['modified'] = $row['modified'];
-    if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $aliasdata['domain'])) {
-      return false;
-    }
-  }
-  catch (PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-  return $aliasdata;
-}
-function mailbox_get_alias_domain_details($aliasdomain) {
-	global $lang;
-	global $pdo;
-  $aliasdomaindata = array();
   try {
     $stmt = $pdo->prepare("SELECT
-      `alias_domain`,
-      `target_domain`,
-      `active` AS `active_int`,
-      CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
-      `created`,
-      `modified`
-        FROM `alias_domain`
-            WHERE `alias_domain` = :aliasdomain");
+      `tfa`.`active` AS `tfa_active_int`,
+      CASE `tfa`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `tfa_active`,
+      `domain_admins`.`username`,
+      `domain_admins`.`created`,
+      `domain_admins`.`active` AS `active_int`,
+      CASE `domain_admins`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
+        FROM `domain_admins`
+        LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
+          WHERE `domain_admins`.`username`= :domain_admin");
     $stmt->execute(array(
-      ':aliasdomain' => $aliasdomain,
+      ':domain_admin' => $domain_admin
     ));
     $row = $stmt->fetch(PDO::FETCH_ASSOC);
-    $aliasdomaindata['alias_domain'] = $row['alias_domain'];
-    $aliasdomaindata['target_domain'] = $row['target_domain'];
-    $aliasdomaindata['active'] = $row['active'];
-    $aliasdomaindata['active_int'] = $row['active_int'];
-    $aliasdomaindata['created'] = $row['created'];
-    $aliasdomaindata['modified'] = $row['modified'];
-  }
-  catch (PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-  if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $aliasdomaindata['target_domain'])) {
-    return false;
-  }
-  return $aliasdomaindata;
-}
-function mailbox_get_domains() {
-  // Get all domains assigned to mailcow_cc_username
-  // Domain admin needs to be active
-  // Domain does not need to be active
-	global $lang;
-	global $pdo;
-  $domains = array();
-	if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
-    return false;
-	}
-  try {
+    if (empty($row)) { 
+      return false;
+    }
+    $domainadmindata['username'] = $row['username'];
+    $domainadmindata['tfa_active'] = $row['tfa_active'];
+    $domainadmindata['active'] = $row['active'];
+    $domainadmindata['tfa_active_int'] = $row['tfa_active_int'];
+    $domainadmindata['active_int'] = $row['active_int'];
+    $domainadmindata['modified'] = $row['created'];
+    // GET SELECTED
     $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
-      WHERE (`domain` IN (
-        SELECT `domain` from `domain_admins`
-          WHERE (`active`='1' AND `username` = :username))
-        )
-        OR ('admin'= :role)
-        AND `domain` != 'ALL'");
-    $stmt->execute(array(
-      ':username' => $_SESSION['mailcow_cc_username'],
-      ':role' => $_SESSION['mailcow_cc_role'],
-    ));
+      WHERE `domain` IN (
+        SELECT `domain` FROM `domain_admins`
+          WHERE `username`= :domain_admin)");
+    $stmt->execute(array(':domain_admin' => $domain_admin));
+    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+    while($row = array_shift($rows)) {
+      $domainadmindata['selected_domains'][] = $row['domain'];
+    }
+    // GET UNSELECTED
+    $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+      WHERE `domain` NOT IN (
+        SELECT `domain` FROM `domain_admins`
+          WHERE `username`= :domain_admin)");
+    $stmt->execute(array(':domain_admin' => $domain_admin));
     $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
     while($row = array_shift($rows)) {
-      $domains[] = $row['domain'];
+      $domainadmindata['unselected_domains'][] = $row['domain'];
+    }
+    if (!isset($domainadmindata['unselected_domains'])) {
+      $domainadmindata['unselected_domains'] = "";
     }
   }
-  catch (PDOException $e) {
+  catch(PDOException $e) {
     $_SESSION['return'] = array(
       'type' => 'danger',
       'msg' => 'MySQL: '.$e
     );
-    return false;
   }
-  return $domains;
+  return $domainadmindata;
 }
-function mailbox_get_domain_details($domain) {
+function set_tfa($postarray) {
 	global $lang;
 	global $pdo;
+	global $yubi;
+	global $u2f;
+	global $tfa;
 
-  $domaindata = array();
-	$domain = idn_to_ascii(strtolower(trim($domain)));
-
-	if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
-		return false;
-	}
-
-  try {
-    $stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` =  :domain");
-    $stmt->execute(array(
-      ':domain' => $domain
-    ));
-    $row = $stmt->fetch(PDO::FETCH_ASSOC);
-    if (!empty($row)) { 
-      $domain = $row['target_domain'];
-    }
-
-    $stmt = $pdo->prepare("SELECT 
-        `domain`,
-        `description`,
-        `aliases`,
-        `mailboxes`, 
-        `maxquota`,
-        `quota`,
-        `relay_all_recipients` as `relay_all_recipients_int`,
-        `backupmx` as `backupmx_int`,
-        `active` as `active_int`,
-        CASE `relay_all_recipients` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `relay_all_recipients`,
-        CASE `backupmx` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `backupmx`,
-        CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
-          FROM `domain` WHERE `domain`= :domain");
-    $stmt->execute(array(
-      ':domain' => $domain
-    ));
-    $row = $stmt->fetch(PDO::FETCH_ASSOC);
-    if (empty($row)) { 
+  if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
+    $_SESSION['mailcow_cc_role'] != "admin") {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
       return false;
-    }
-
-    $stmt = $pdo->prepare("SELECT COUNT(*) AS `count`,
-      COALESCE(SUM(`quota`), 0) AS `in_use`
-        FROM `mailbox`
-          WHERE `kind` NOT REGEXP 'location|thing|group'
-            AND `domain` = :domain");
-    $stmt->execute(array(':domain' => $row['domain']));
-    $MailboxDataDomain	= $stmt->fetch(PDO::FETCH_ASSOC);
-
-    $domaindata['max_new_mailbox_quota']	= ($row['quota'] * 1048576) - $MailboxDataDomain['in_use'];
-    if ($domaindata['max_new_mailbox_quota'] > ($row['maxquota'] * 1048576)) {
-      $domaindata['max_new_mailbox_quota'] = ($row['maxquota'] * 1048576);
-    }
-    $domaindata['quota_used_in_domain'] = $MailboxDataDomain['in_use'];
-    $domaindata['mboxes_in_domain'] = $MailboxDataDomain['count'];
-    $domaindata['mboxes_left'] = $row['mailboxes']	- $MailboxDataDomain['count'];
-    $domaindata['domain_name'] = $row['domain'];
-    $domaindata['description'] = $row['description'];
-    $domaindata['max_num_aliases_for_domain'] = $row['aliases'];
-    $domaindata['max_num_mboxes_for_domain'] = $row['mailboxes'];
-    $domaindata['max_quota_for_mbox'] = $row['maxquota'] * 1048576;
-    $domaindata['max_quota_for_domain'] = $row['quota'] * 1048576;
-    $domaindata['backupmx'] = $row['backupmx'];
-    $domaindata['backupmx_int'] = $row['backupmx_int'];
-    $domaindata['active'] = $row['active'];
-    $domaindata['active_int'] = $row['active_int'];
-    $domaindata['relay_all_recipients'] = $row['relay_all_recipients'];
-    $domaindata['relay_all_recipients_int'] = $row['relay_all_recipients_int'];
-
-    $stmt = $pdo->prepare("SELECT COUNT(*) AS `alias_count` FROM `alias`
-      WHERE (`domain`= :domain OR `domain` IN (SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = :domain2))
-        AND `address` NOT IN (
-          SELECT `username` FROM `mailbox`
-        )");
-    $stmt->execute(array(
-      ':domain' => $domain,
-      ':domain2' => $domain
-    ));
-    $AliasDataDomain = $stmt->fetch(PDO::FETCH_ASSOC);
-    (isset($AliasDataDomain['alias_count'])) ? $domaindata['aliases_in_domain'] = $AliasDataDomain['alias_count'] : $domaindata['aliases_in_domain'] = "0";
-    $domaindata['aliases_left'] = $row['aliases']	- $AliasDataDomain['alias_count'];
   }
-  catch (PDOException $e) {
+  $username = $_SESSION['mailcow_cc_username'];
+  
+  $stmt = $pdo->prepare("SELECT `password` FROM `admin`
+      WHERE `username` = :user");
+  $stmt->execute(array(':user' => $username));
+  $row = $stmt->fetch(PDO::FETCH_ASSOC);
+  if (!verify_ssha256($row['password'], $postarray["confirm_password"])) {
     $_SESSION['return'] = array(
       'type' => 'danger',
-      'msg' => 'MySQL: '.$e
+      'msg' => sprintf($lang['danger']['access_denied'])
     );
     return false;
   }
+  
+	switch ($postarray["tfa_method"]) {
+		case "yubi_otp":
+      $key_id = (!isset($postarray["key_id"])) ? 'unidentified' : $postarray["key_id"];
+      $yubico_id = $postarray['yubico_id'];
+      $yubico_key = $postarray['yubico_key'];
+      $yubi = new Auth_Yubico($yubico_id, $yubico_key);
+      if (!$yubi) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+			if (!ctype_alnum($postarray["otp_token"]) || strlen($postarray["otp_token"]) != 44) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => sprintf($lang['danger']['tfa_token_invalid'])
+				);
+				return false;
+			}
+      $yauth = $yubi->verify($postarray["otp_token"]);
+      if (PEAR::isError($yauth)) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'Yubico API: ' . $yauth->getMessage()
+				);
+				return false;
+      }
+			try {
+        // We could also do a modhex translation here
+        $yubico_modhex_id = substr($postarray["otp_token"], 0, 12);
+        $stmt = $pdo->prepare("DELETE FROM `tfa` 
+          WHERE `username` = :username
+            AND (`authmech` != 'yubi_otp')
+            OR (`authmech` = 'yubi_otp' AND `secret` LIKE :modhex)");
+				$stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
+				$stmt = $pdo->prepare("INSERT INTO `tfa` (`key_id`, `username`, `authmech`, `active`, `secret`) VALUES
+					(:key_id, :username, 'yubi_otp', '1', :secret)");
+				$stmt->execute(array(':key_id' => $key_id, ':username' => $username, ':secret' => $yubico_id . ':' . $yubico_key . ':' . $yubico_modhex_id));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['object_modified'], htmlspecialchars($username))
+			);
+		break;
 
-  return $domaindata;
-}
-function mailbox_get_mailbox_details($mailbox) {
-	global $lang;
-	global $pdo;
-  if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $mailbox)) {
-    return false;
-  }
-  $mailboxdata = array();
-  try {
-    $stmt = $pdo->prepare("SELECT
-        `domain`.`backupmx`,
-        `mailbox`.`username`,
-        `mailbox`.`name`,
-        `mailbox`.`active` AS `active_int`,
-        CASE `mailbox`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
-        `mailbox`.`domain`,
-        `mailbox`.`quota`,
-        `quota2`.`bytes`,
-        `quota2`.`messages`
-          FROM `mailbox`, `quota2`, `domain`
-            WHERE `mailbox`.`kind` NOT REGEXP 'location|thing|group' AND `mailbox`.`username` = `quota2`.`username` AND `domain`.`domain` = `mailbox`.`domain` AND `mailbox`.`username` = :mailbox");
-    $stmt->execute(array(
-      ':mailbox' => $mailbox,
-    ));
-    $row = $stmt->fetch(PDO::FETCH_ASSOC);
-
-    $stmt = $pdo->prepare("SELECT `maxquota`, `quota` FROM  `domain` WHERE `domain` = :domain");
-    $stmt->execute(array(':domain' => $row['domain']));
-    $DomainQuota  = $stmt->fetch(PDO::FETCH_ASSOC);
-
-    $stmt = $pdo->prepare("SELECT COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` = :domain AND `username` != :username");
-    $stmt->execute(array(':domain' => $row['domain'], ':username' => $mailbox));
-    $MailboxUsage	= $stmt->fetch(PDO::FETCH_ASSOC);
-
-    $stmt = $pdo->prepare("SELECT IFNULL(COUNT(`address`), 0) AS `sa_count` FROM `spamalias` WHERE `goto` = :address AND `validity` >= :unixnow");
-    $stmt->execute(array(':address' => $mailbox, ':unixnow' => time()));
-    $SpamaliasUsage	= $stmt->fetch(PDO::FETCH_ASSOC);
+		case "u2f":
+      $key_id = (!isset($postarray["key_id"])) ? 'unidentified' : $postarray["key_id"];
+      try {
+        $reg = $u2f->doRegister(json_decode($_SESSION['regReq']), json_decode($postarray['token']));
+        $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `authmech` != 'u2f'");
+				$stmt->execute(array(':username' => $username));
+        $stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `keyHandle`, `publicKey`, `certificate`, `counter`, `active`) VALUES (?, ?, 'u2f', ?, ?, ?, ?, '1')");
+        $stmt->execute(array($username, $key_id, $reg->keyHandle, $reg->publicKey, $reg->certificate, $reg->counter));
+        $_SESSION['return'] = array(
+          'type' => 'success',
+          'msg' => sprintf($lang['success']['object_modified'], $username)
+        );
+        $_SESSION['regReq'] = null;
+      }
+      catch (Exception $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => "U2F: " . $e->getMessage()
+        );
+        $_SESSION['regReq'] = null;
+        return false;
+      }
+		break;
 
-    $mailboxdata['max_new_quota'] = ($DomainQuota['quota'] * 1048576) - $MailboxUsage['in_use'];
-    if ($mailboxdata['max_new_quota'] > ($DomainQuota['maxquota'] * 1048576)) {
-      $mailboxdata['max_new_quota'] = ($DomainQuota['maxquota'] * 1048576);
-    }
+		case "totp":
+      $key_id = (!isset($postarray["key_id"])) ? 'unidentified' : $postarray["key_id"];
+      if ($tfa->verifyCode($_POST['totp_secret'], $_POST['totp_confirm_token']) === true) {
+        try {
+        $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
+        $stmt->execute(array(':username' => $username));
+        $stmt = $pdo->prepare("INSERT INTO `tfa` (`username`, `key_id`, `authmech`, `secret`, `active`) VALUES (?, ?, 'totp', ?, '1')");
+        $stmt->execute(array($username, $key_id, $_POST['totp_secret']));
+        }
+        catch (PDOException $e) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => 'MySQL: '.$e
+          );
+          return false;
+        }
+        $_SESSION['return'] = array(
+          'type' => 'success',
+          'msg' => sprintf($lang['success']['object_modified'], $username)
+        );
+      }
+      else {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'TOTP verification failed'
+        );
+      }
+		break;
 
-    $mailboxdata['username'] = $row['username'];
-    $mailboxdata['is_relayed'] = $row['backupmx'];
-    $mailboxdata['name'] = $row['name'];
-    $mailboxdata['active'] = $row['active'];
-    $mailboxdata['active_int'] = $row['active_int'];
-    $mailboxdata['domain'] = $row['domain'];
-    $mailboxdata['quota'] = $row['quota'];
-    $mailboxdata['quota_used'] = intval($row['bytes']);
-    $mailboxdata['percent_in_use'] = round((intval($row['bytes']) / intval($row['quota'])) * 100);
-    $mailboxdata['messages'] = $row['messages'];
-    $mailboxdata['spam_aliases'] = $SpamaliasUsage['sa_count'];
-    if ($mailboxdata['percent_in_use'] >= 90) {
-      $mailboxdata['percent_class'] = "danger";
-    }
-    elseif ($mailboxdata['percent_in_use'] >= 75) {
-      $mailboxdata['percent_class'] = "warning";
+		case "none":
+			try {
+				$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
+				$stmt->execute(array(':username' => $username));
+			}
+			catch (PDOException $e) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'MySQL: '.$e
+				);
+				return false;
+			}
+			$_SESSION['return'] = array(
+				'type' => 'success',
+				'msg' => sprintf($lang['success']['object_modified'], htmlspecialchars($username))
+			);
+		break;
+	}
+}
+function unset_tfa_key($postarray) {
+  // Can only unset own keys
+  // Needs at least one key left
+  global $pdo;
+  global $lang;
+  $id = intval($postarray['unset_tfa_key']);
+  if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
+    $_SESSION['mailcow_cc_role'] != "admin") {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
+  }
+  $username = $_SESSION['mailcow_cc_username'];
+  try {
+    if (!is_numeric($id)) {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['access_denied'])
+      );
+      return false;
     }
-    else {
-      $mailboxdata['percent_class'] = "success";
+    $stmt = $pdo->prepare("SELECT COUNT(*) AS `keys` FROM `tfa`
+      WHERE `username` = :username AND `active` = '1'");
+    $stmt->execute(array(':username' => $username));
+    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+    if ($row['keys'] == "1") {
+      $_SESSION['return'] = array(
+        'type' => 'danger',
+        'msg' => sprintf($lang['danger']['last_key'])
+      );
+      return false;
     }
+    $stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username AND `id` = :id");
+    $stmt->execute(array(':username' => $username, ':id' => $id));
+    $_SESSION['return'] = array(
+      'type' => 'success',
+      'msg' => sprintf($lang['success']['object_modified'], $username)
+    );
   }
   catch (PDOException $e) {
     $_SESSION['return'] = array(
@@ -4438,141 +898,149 @@ function mailbox_get_mailbox_details($mailbox) {
     );
     return false;
   }
-  return $mailboxdata;
 }
-function mailbox_get_resource_details($resource) {
-	global $lang;
+function get_tfa($username = null) {
 	global $pdo;
-  $resourcedata = array();
-  if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $resource)) {
-    return false;
-  }
-  try {
-    $stmt = $pdo->prepare("SELECT
-        `username`,
-        `name`,
-        `kind`,
-        `multiple_bookings` AS `multiple_bookings_int`,
-        `local_part`,
-        `active` AS `active_int`,
-        CASE `multiple_bookings` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `multiple_bookings`,
-        CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
-        `domain`
-          FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `username` = :resource");
-    $stmt->execute(array(
-      ':resource' => $resource,
-    ));
-    $row = $stmt->fetch(PDO::FETCH_ASSOC);
-    $resourcedata['name'] = $row['username'];
-    $resourcedata['kind'] = $row['kind'];
-    $resourcedata['multiple_bookings'] = $row['multiple_bookings'];
-    $resourcedata['multiple_bookings_int'] = $row['multiple_bookings_int'];
-    $resourcedata['description'] = $row['name'];
-    $resourcedata['active'] = $row['active'];
-    $resourcedata['active_int'] = $row['active_int'];
-    $resourcedata['domain'] = $row['domain'];
-    $resourcedata['local_part'] = $row['local_part'];
-  }
-  catch (PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
+  if (isset($_SESSION['mailcow_cc_username'])) {
+    $username = $_SESSION['mailcow_cc_username'];
   }
-  if (!isset($resourcedata['domain']) ||
-    (isset($resourcedata['domain']) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $resourcedata['domain']))) {
+  elseif (empty($username)) {
     return false;
   }
+
+  $stmt = $pdo->prepare("SELECT * FROM `tfa`
+      WHERE `username` = :username AND `active` = '1'");
+  $stmt->execute(array(':username' => $username));
+  $row = $stmt->fetch(PDO::FETCH_ASSOC);
   
-  return $resourcedata;
+	switch ($row["authmech"]) {
+		case "yubi_otp":
+      $data['name'] = "yubi_otp";
+      $data['pretty'] = "Yubico OTP";
+      $stmt = $pdo->prepare("SELECT `id`, `key_id`, RIGHT(`secret`, 12) AS 'modhex' FROM `tfa` WHERE `authmech` = 'yubi_otp' AND `username` = :username");
+      $stmt->execute(array(
+        ':username' => $username,
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $data['additional'][] = $row;
+      }
+      return $data;
+    break;
+		case "u2f":
+      $data['name'] = "u2f";
+      $data['pretty'] = "Fido U2F";
+      $stmt = $pdo->prepare("SELECT `id`, `key_id` FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = :username");
+      $stmt->execute(array(
+        ':username' => $username,
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $data['additional'][] = $row;
+      }
+      return $data;
+    break;
+		case "hotp":
+      $data['name'] = "hotp";
+      $data['pretty'] = "HMAC-based OTP";
+      return $data;
+		break;
+ 		case "totp":
+      $data['name'] = "totp";
+      $data['pretty'] = "Time-based OTP";
+      $stmt = $pdo->prepare("SELECT `id`, `key_id`, `secret` FROM `tfa` WHERE `authmech` = 'totp' AND `username` = :username");
+      $stmt->execute(array(
+        ':username' => $username,
+      ));
+      $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+      while($row = array_shift($rows)) {
+        $data['additional'][] = $row;
+      }
+      return $data;
+      break;
+    default:
+      $data['name'] = 'none';
+      $data['pretty'] = "-";
+      return $data;
+    break;
+	}
 }
-function mailbox_delete_domain($postarray) {
-	global $lang;
-	global $redis;
+function verify_tfa_login($username, $token) {
 	global $pdo;
-  if (!is_array($postarray['domain'])) {
-    $domains = array();
-    $domains[] = $postarray['domain'];
-  }
-  else {
-    $domains = $postarray['domain'];
-  }
-	if ($_SESSION['mailcow_cc_role'] != "admin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-  foreach ($domains as $domain) {
-    if (!is_valid_domain_name($domain)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['domain_invalid'])
-      );
-      return false;
-    }
-    $domain	= idn_to_ascii(strtolower(trim($domain)));
+	global $lang;
+	global $yubi;
+	global $u2f;
+	global $tfa;
 
+  $stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
+      WHERE `username` = :username AND `active` = '1'");
+  $stmt->execute(array(':username' => $username));
+  $row = $stmt->fetch(PDO::FETCH_ASSOC);
+  
+	switch ($row["authmech"]) {
+		case "yubi_otp":
+			if (!ctype_alnum($token) || strlen($token) != 44) {
+        return false;
+      }
+      $yubico_modhex_id = substr($token, 0, 12);
+      $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
+          WHERE `username` = :username
+          AND `authmech` = 'yubi_otp'
+          AND `active`='1'
+          AND `secret` LIKE :modhex");
+      $stmt->execute(array(':username' => $username, ':modhex' => '%' . $yubico_modhex_id));
+      $row = $stmt->fetch(PDO::FETCH_ASSOC);
+      $yubico_auth = explode(':', $row['secret']);
+      $yubi = new Auth_Yubico($yubico_auth[0], $yubico_auth[1]);
+      $yauth = $yubi->verify($token);
+      if (PEAR::isError($yauth)) {
+				$_SESSION['return'] = array(
+					'type' => 'danger',
+					'msg' => 'Yubico Authentication error: ' . $yauth->getMessage()
+				);
+				return false;
+      }
+      else {
+        $_SESSION['tfa_id'] = $row['id'];
+        return true;
+      }
+    return false;
+  break;
+  case "u2f":
     try {
-      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
-        WHERE `domain` = :domain");
-      $stmt->execute(array(':domain' => $domain));
-      $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+      $reg = $u2f->doAuthenticate(json_decode($_SESSION['authReq']), get_u2f_registrations($username), json_decode($token));
+      $stmt = $pdo->prepare("UPDATE `tfa` SET `counter` = ? WHERE `id` = ?");
+      $stmt->execute(array($reg->counter, $reg->id));
+      $_SESSION['tfa_id'] = $reg->id;
+      $_SESSION['authReq'] = null;
+      return true;
     }
-    catch(PDOException $e) {
+    catch (Exception $e) {
       $_SESSION['return'] = array(
         'type' => 'danger',
-        'msg' => 'MySQL: '.$e
+        'msg' => "U2F: " . $e->getMessage()
       );
+      $_SESSION['regReq'] = null;
       return false;
     }
-    if ($num_results != 0 || !empty($num_results)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['domain_not_empty'])
-      );
+    return false;
+  break;
+  case "hotp":
       return false;
-    }
-
+  break;
+  case "totp":
     try {
-      $stmt = $pdo->prepare("DELETE FROM `domain` WHERE `domain` = :domain");
-      $stmt->execute(array(
-        ':domain' => $domain,
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `domain` = :domain");
-      $stmt->execute(array(
-        ':domain' => $domain,
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `alias` WHERE `domain` = :domain");
-      $stmt->execute(array(
-        ':domain' => $domain,
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `alias_domain` WHERE `target_domain` = :domain");
-      $stmt->execute(array(
-        ':domain' => $domain,
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `mailbox` WHERE `domain` = :domain");
-      $stmt->execute(array(
-        ':domain' => $domain,
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` LIKE :domain");
-      $stmt->execute(array(
-        ':domain' => '%@'.$domain,
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `quota2` WHERE `username` = :domain");
-      $stmt->execute(array(
-        ':domain' => '%@'.$domain,
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `address` = :domain");
-      $stmt->execute(array(
-        ':domain' => '%@'.$domain,
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :domain");
-      $stmt->execute(array(
-        ':domain' => '%@'.$domain,
-      ));
+      $stmt = $pdo->prepare("SELECT `id`, `secret` FROM `tfa`
+          WHERE `username` = :username
+          AND `authmech` = 'totp'
+          AND `active`='1'");
+      $stmt->execute(array(':username' => $username));
+      $row = $stmt->fetch(PDO::FETCH_ASSOC);
+      if ($tfa->verifyCode($row['secret'], $_POST['token']) === true) {
+        $_SESSION['tfa_id'] = $row['id'];
+        return true;
+      }
+      return false;
     }
     catch (PDOException $e) {
       $_SESSION['return'] = array(
@@ -4581,62 +1049,72 @@ function mailbox_delete_domain($postarray) {
       );
       return false;
     }
-    try {
-      $redis->hDel('DOMAIN_MAP', $domain);
-    }
-    catch (RedisException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'Redis: '.$e
-      );
+  break;
+  default:
       return false;
-    }
-  }
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['domain_removed'], htmlspecialchars($domain))
-	);
-	return true;
+  break;
+	}
+  return false;
 }
-function mailbox_delete_alias($postarray) {
-  // $postarray['address'] can be a single element or an array
+function edit_domain_admin($postarray) {
 	global $lang;
 	global $pdo;
-  if (!is_array($postarray['address'])) {
-    $addresses = array();
-    $addresses[] = $postarray['address'];
-  }
-  else {
-    $addresses = $postarray['address'];
-  }
-  foreach ($addresses as $address) {
-    $local_part		= strstr($address, '@', true);
-    $domain = mailbox_get_alias_details($address)['domain'];
-    try {
-      $stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :address");
-      $stmt->execute(array(':address' => $address));
-      $gotos = $stmt->fetch(PDO::FETCH_ASSOC);
+
+	if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
+		$_SESSION['return'] = array(
+			'type' => 'danger',
+			'msg' => sprintf($lang['danger']['access_denied'])
+		);
+		return false;
+	}
+	// Administrator
+  if ($_SESSION['mailcow_cc_role'] == "admin") {
+    $username     = $postarray['username'];
+    $username_now = $postarray['username_now'];
+    $password     = $postarray['password'];
+    $password2    = $postarray['password2'];
+    $active       = intval($postarray['active']);
+
+    if(isset($postarray['domain'])) {
+      foreach ($postarray['domain'] as $domain) {
+        if (!is_valid_domain_name($domain)) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => sprintf($lang['danger']['domain_invalid'])
+          );
+          return false;
+        }
+      }
     }
-    catch(PDOException $e) {
+
+    if (empty($postarray['domain'])) {
       $_SESSION['return'] = array(
         'type' => 'danger',
-        'msg' => 'MySQL: '.$e
+        'msg' => sprintf($lang['danger']['domain_invalid'])
       );
       return false;
     }
-    $goto_array = explode(',', $gotos['goto']);
 
-    if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+    if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
       $_SESSION['return'] = array(
         'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
+        'msg' => sprintf($lang['danger']['username_invalid'])
       );
       return false;
     }
+    if ($username != $username_now) {
+      if (empty(get_domain_admin_details($username_now)['username']) || !empty(get_domain_admin_details($username)['username'])) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['username_invalid'])
+        );
+        return false;
+      }
+    }
     try {
-      $stmt = $pdo->prepare("DELETE FROM `alias` WHERE `address` = :address AND `address` NOT IN (SELECT `username` FROM `mailbox`)");
+      $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
       $stmt->execute(array(
-        ':address' => $address
+        ':username' => $username_now,
       ));
     }
     catch (PDOException $e) {
@@ -4646,406 +1124,347 @@ function mailbox_delete_alias($postarray) {
       );
       return false;
     }
-  }
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['alias_removed'], htmlspecialchars(implode(', ', $addresses)))
-	);
 
-}
-function mailbox_delete_alias_domain($postarray) {
-	global $lang;
-	global $pdo;
-  if (!is_array($postarray['alias_domain'])) {
-    $alias_domains = array();
-    $alias_domains[] = $postarray['alias_domain'];
-  }
-  else {
-    $alias_domains = $postarray['alias_domain'];
-  }
-  foreach ($alias_domains as $alias_domain) {
-    if (!is_valid_domain_name($alias_domain)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['domain_invalid'])
-      );
-      return false;
+    if (isset($postarray['domain'])) {
+      foreach ($postarray['domain'] as $domain) {
+        try {
+          $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
+            VALUES (:username, :domain, :created, :active)");
+          $stmt->execute(array(
+            ':username' => $username,
+            ':domain' => $domain,
+            ':created' => date('Y-m-d H:i:s'),
+            ':active' => $active
+          ));
+        }
+        catch (PDOException $e) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => 'MySQL: '.$e
+          );
+          return false;
+        }
+      }
     }
-    try {
-      $stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain`
-        WHERE `alias_domain`= :alias_domain");
-      $stmt->execute(array(':alias_domain' => $alias_domain));
-      $DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
+
+    if (!empty($password) && !empty($password2)) {
+      if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['password_complexity'])
+        );
+        return false;
+      }
+      if ($password != $password2) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['password_mismatch'])
+        );
+        return false;
+      }
+      $password_hashed = hash_password($password);
+      try {
+        $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `active` = :active, `password` = :password_hashed WHERE `username` = :username2");
+        $stmt->execute(array(
+          ':password_hashed' => $password_hashed,
+          ':username1' => $username,
+          ':username2' => $username_now,
+          ':active' => $active
+        ));
+        if (isset($postarray['disable_tfa'])) {
+          $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
+          $stmt->execute(array(':username' => $username_now));
+        }
+        else {
+          $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username WHERE `username` = :username_now");
+          $stmt->execute(array(':username' => $username, ':username_now' => $username_now));
+        }
+      }
+      catch (PDOException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'MySQL: '.$e
+        );
+        return false;
+      }
     }
-    catch(PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
+    else {
+      try {
+        $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username1, `active` = :active WHERE `username` = :username2");
+        $stmt->execute(array(
+          ':username1' => $username,
+          ':username2' => $username_now,
+          ':active' => $active
+        ));
+        if (isset($postarray['disable_tfa'])) {
+          $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
+          $stmt->execute(array(':username' => $username));
+        }
+        else {
+          $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username WHERE `username` = :username_now");
+          $stmt->execute(array(':username' => $username, ':username_now' => $username_now));
+        }
+      }
+      catch (PDOException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'MySQL: '.$e
+        );
+        return false;
+      }
     }
-    if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $DomainData['target_domain'])) {
+    $_SESSION['return'] = array(
+      'type' => 'success',
+      'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars($username))
+    );
+  }
+  // Domain administrator
+  // Can only edit itself
+  elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
+    $username = $_SESSION['mailcow_cc_username'];
+    $password_old		= $postarray['user_old_pass'];
+    $password_new	= $postarray['user_new_pass'];
+    $password_new2	= $postarray['user_new_pass2'];
+
+    $stmt = $pdo->prepare("SELECT `password` FROM `admin`
+        WHERE `username` = :user");
+    $stmt->execute(array(':user' => $username));
+    $row = $stmt->fetch(PDO::FETCH_ASSOC);
+    if (!verify_ssha256($row['password'], $password_old)) {
       $_SESSION['return'] = array(
         'type' => 'danger',
         'msg' => sprintf($lang['danger']['access_denied'])
       );
       return false;
     }
-    try {
-      $stmt = $pdo->prepare("DELETE FROM `alias_domain` WHERE `alias_domain` = :alias_domain");
-      $stmt->execute(array(
-        ':alias_domain' => $alias_domain,
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `alias` WHERE `domain` = :alias_domain");
-      $stmt->execute(array(
-        ':alias_domain' => $alias_domain,
-      ));
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
+
+    if (!empty($password_new2) && !empty($password_new)) {
+      if ($password_new2 != $password_new) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['password_mismatch'])
+        );
+        return false;
+      }
+      if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password_new)) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['password_complexity'])
+        );
+        return false;
+      }
+      $password_hashed = hash_password($password_new);
+      try {
+        $stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
+        $stmt->execute(array(
+          ':password_hashed' => $password_hashed,
+          ':username' => $username
+        ));
+      }
+      catch (PDOException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'MySQL: '.$e
+        );
+        return false;
+      }
     }
-	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['alias_domain_removed'], htmlspecialchars(implode(', ', $alias_domains)))
-	);
+    
+    $_SESSION['return'] = array(
+      'type' => 'success',
+      'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars($username))
+    );
+  }
 }
-function mailbox_delete_mailbox($postarray) {
-	global $lang;
+function get_admin_details() {
+  // No parameter to be given, only one admin should exist
 	global $pdo;
-  if (!is_array($postarray['username'])) {
-    $usernames = array();
-    $usernames[] = $postarray['username'];
+	global $lang;
+  $data = array();
+  if ($_SESSION['mailcow_cc_role'] != 'admin') {
+    return false;
   }
-  else {
-    $usernames = $postarray['username'];
+  try {
+    $stmt = $pdo->prepare("SELECT `username`, `modified`, `created` FROM `admin` WHERE `superadmin`='1' AND active='1'");
+    $stmt->execute();
+    $data = $stmt->fetch(PDO::FETCH_ASSOC);
+  }
+  catch(PDOException $e) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => 'MySQL: '.$e
+    );
+  }
+  return $data;
+}
+function dkim_add_key($postarray) {
+	global $lang;
+	global $pdo;
+	global $redis;
+  if ($_SESSION['mailcow_cc_role'] != "admin") {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  // if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+    // $_SESSION['return'] = array(
+      // 'type' => 'danger',
+      // 'msg' => sprintf($lang['danger']['access_denied'])
+    // );
+    // return false;
+  // }
+  $key_length	= intval($postarray['key_size']);
+  $dkim_selector = (isset($postarray['dkim_selector'])) ? $postarray['dkim_selector'] : 'dkim';
+  $domain	= $postarray['domain'];
+  if (!is_valid_domain_name($domain) || !is_numeric($key_length)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
+    );
+    return false;
   }
-  foreach ($usernames as $username) {
-    if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
 
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+  if (!empty(glob($GLOBALS['MC_DKIM_TXTS'] . '/' . $domain . '.dkim')) ||
+    $redis->hGet('DKIM_PUB_KEYS', $domain)) {
       $_SESSION['return'] = array(
         'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
+        'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
       );
       return false;
-    }
+  }
+
+  if (!ctype_alnum($dkim_selector)) {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
+    );
+    return false;
+  }
 
+  $config = array(
+    "digest_alg" => "sha256",
+    "private_key_bits" => $key_length,
+    "private_key_type" => OPENSSL_KEYTYPE_RSA,
+  );
+  if ($keypair_ressource = openssl_pkey_new($config)) {
+    $key_details = openssl_pkey_get_details($keypair_ressource);
+    $pubKey = implode(array_slice(
+        array_filter(
+          explode(PHP_EOL, $key_details['key'])
+        ), 1, -1)
+      );
+    // Save public key and selector to redis
     try {
-      $stmt = $pdo->prepare("DELETE FROM `alias` WHERE `goto` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `quota2` WHERE `username` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `mailbox` WHERE `username` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `goto` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `imapsync` WHERE `user2` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_user_profile` WHERE `c_uid` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_cache_folder` WHERE `c_uid` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $username . "/%' OR `c_uid` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_quick_contact` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_quick_appointment` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_folder_info` WHERE `c_path2` = :username");
-      $stmt->execute(array(
-        ':username' => $username
-      ));
-      $stmt = $pdo->prepare("SELECT `address`, `goto` FROM `alias`
-          WHERE `goto` REGEXP :username");
-      $stmt->execute(array(':username' => '(^|,)'.$username.'($|,)'));
-      $GotoData = $stmt->fetchAll(PDO::FETCH_ASSOC);
-      foreach ($GotoData as $gotos) {
-        $goto_exploded = explode(',', $gotos['goto']);
-        if (($key = array_search($username, $goto_exploded)) !== false) {
-          unset($goto_exploded[$key]);
-        }
-        $gotos_rebuild = implode(',', $goto_exploded);
-        $stmt = $pdo->prepare("UPDATE `alias` SET
-          `goto` = :goto
-            WHERE `address` = :address");
-        $stmt->execute(array(
-          ':goto' => $gotos_rebuild,
-          ':address' => $gotos['address']
-        ));
-      }
+      $redis->hSet('DKIM_PUB_KEYS', $domain, $pubKey);
+      $redis->hSet('DKIM_SELECTORS', $domain, $dkim_selector);
     }
-    catch (PDOException $e) {
+    catch (RedisException $e) {
       $_SESSION['return'] = array(
         'type' => 'danger',
-        'msg' => 'MySQL: '.$e
+        'msg' => 'Redis: '.$e
       );
       return false;
     }
-	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['mailbox_removed'], htmlspecialchars(implode(', ', $usernames)))
-	);
+    // Export private key and save private key to redis
+    openssl_pkey_export($keypair_ressource, $privKey);
+    if (isset($privKey) && !empty($privKey)) {
+      try {
+        $redis->hSet('DKIM_PRIV_KEYS', $dkim_selector . '.' . $domain, trim($privKey));
+      }
+      catch (RedisException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'Redis: '.$e
+        );
+        return false;
+      }
+    }
+    $_SESSION['return'] = array(
+      'type' => 'success',
+      'msg' => sprintf($lang['success']['dkim_added'])
+    );
+    return true;
+  }
+  else {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
+    );
+    return false;
+  }
+}
+function dkim_get_key_details($domain) {
+  global $redis;
+  if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+    return false;
+  }
+  $data = array();
+  if ($redis_dkim_key_data = $redis->hGet('DKIM_PUB_KEYS', $domain)) {
+    $data['pubkey'] = $redis_dkim_key_data;
+    $data['length'] = (strlen($data['pubkey']) < 391) ? 1024 : 2048;
+    $data['dkim_txt'] = 'v=DKIM1;k=rsa;t=s;s=email;p=' . $redis_dkim_key_data;
+    $data['dkim_selector'] = $redis->hGet('DKIM_SELECTORS', $domain);
+  }
+  return $data;
 }
-function mailbox_reset_eas($username) {
+function dkim_get_blind_keys() {
+  global $redis;
 	global $lang;
-	global $pdo;
-
-  (isset($postarray['username'])) ? $username = $postarray['username'] : $username = $_SESSION['mailcow_cc_username'];
-
-	if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-
-	if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-
-	try {
-    $stmt = $pdo->prepare("DELETE FROM `sogo_cache_folder` WHERE `c_uid` = :username");
-    $stmt->execute(array(
-      ':username' => $username
-    ));
-	}
-	catch (PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['eas_reset'], htmlspecialchars($username))
-	);
+  if ($_SESSION['mailcow_cc_role'] != "admin") {
+    return false;
+  }
+  $domains = array();
+  foreach ($redis->hKeys('DKIM_PUB_KEYS') as $redis_dkim_domain) {
+    $domains[] = $redis_dkim_domain;
+  }
+  return array_diff($domains, array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')));
 }
-function mailbox_delete_resource($postarray) {
+function dkim_delete_key($postarray) {
+	global $redis;
 	global $lang;
-	global $pdo;
-  if (!is_array($postarray['name'])) {
-    $names = array();
-    $names[] = $postarray['name'];
+  if (!is_array($postarray['domains'])) {
+    $domains = array();
+    $domains[] = $postarray['domains'];
   }
   else {
-    $names = $postarray['name'];
+    $domains = $postarray['domains'];
   }
-  foreach ($names as $name) {
-    if (!filter_var($name, FILTER_VALIDATE_EMAIL)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $name)) {
+  if ($_SESSION['mailcow_cc_role'] != "admin") {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
+  }
+  foreach ($domains as $domain) {
+    if (!is_valid_domain_name($domain)) {
       $_SESSION['return'] = array(
         'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
+        'msg' => sprintf($lang['danger']['dkim_domain_or_sel_invalid'])
       );
       return false;
     }
     try {
-      $stmt = $pdo->prepare("DELETE FROM `mailbox` WHERE `username` = :username");
-      $stmt->execute(array(
-        ':username' => $name
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_user_profile` WHERE `c_uid` = :username");
-      $stmt->execute(array(
-        ':username' => $name
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_cache_folder` WHERE `c_uid` = :username");
-      $stmt->execute(array(
-        ':username' => $name
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $name . "/%' OR `c_uid` = :username");
-      $stmt->execute(array(
-        ':username' => $name
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
-      $stmt->execute(array(
-        ':username' => $name
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_quick_contact` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
-      $stmt->execute(array(
-        ':username' => $name
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_quick_appointment` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
-      $stmt->execute(array(
-        ':username' => $name
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `sogo_folder_info` WHERE `c_path2` = :username");
-      $stmt->execute(array(
-        ':username' => $name
-      ));
+      $selector = $redis->hGet('DKIM_SELECTORS', $domain);
+      $redis->hDel('DKIM_PUB_KEYS', $domain);
+      $redis->hDel('DKIM_PRIV_KEYS', $selector . '.' . $domain);
+      $redis->hDel('DKIM_SELECTORS', $domain);
     }
-    catch (PDOException $e) {
+    catch (RedisException $e) {
       $_SESSION['return'] = array(
         'type' => 'danger',
-        'msg' => 'MySQL: '.$e
+        'msg' => 'Redis: '.$e
       );
       return false;
-    }
-	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['resource_removed'], htmlspecialchars(implode(', ', $names)))
-	);
-}
-function mailbox_get_sender_acl_handles($mailbox) {
-	global $pdo;
-	global $lang;
-	if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
-    return false;
-	}
-
-  $data['sender_acl_domains']['ro']               = array();
-  $data['sender_acl_domains']['rw']               = array();
-  $data['sender_acl_domains']['selectable']       = array();
-  $data['sender_acl_addresses']['ro']             = array();
-  $data['sender_acl_addresses']['rw']             = array();
-  $data['sender_acl_addresses']['selectable']     = array();
-  $data['fixed_sender_aliases']                   = array();
-  
-  try {
-    // Fixed addresses
-    $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` REGEXP :goto AND `address` NOT LIKE '@%'");
-    $stmt->execute(array(':goto' => '(^|,)'.$mailbox.'($|,)'));
-    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row = array_shift($rows)) {
-      $data['fixed_sender_aliases'][] = $row['address'];
-    }
-    $stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `alias_domain_alias` FROM `mailbox`, `alias_domain`
-      WHERE `alias_domain`.`target_domain` = `mailbox`.`domain`
-      AND `mailbox`.`username` = :username");
-    $stmt->execute(array(':username' => $mailbox));
-    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row = array_shift($rows)) {
-      if (!empty($row['alias_domain_alias'])) {
-        $data['fixed_sender_aliases'][] = $row['alias_domain_alias'];
-      }
-    }
-
-    // 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 '@%'");
-    $stmt->execute(array(':logged_in_as' => $mailbox));
-    $domain_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($domain_row = array_shift($domain_rows)) {
-      if (is_valid_domain_name($domain_row['send_as']) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain_row['send_as'])) {
-        $data['sender_acl_domains']['ro'][] = $domain_row['send_as'];
-        continue;
-      }
-      if (is_valid_domain_name($domain_row['send_as']) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain_row['send_as'])) {
-        $data['sender_acl_domains']['rw'][] = $domain_row['send_as'];
-        continue;
-      }
-    }
-
-    $stmt = $pdo->prepare("SELECT `send_as` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as AND `send_as` NOT LIKE '@%'");
-    $stmt->execute(array(':logged_in_as' => $mailbox));
-    $address_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($address_row = array_shift($address_rows)) {
-      if (filter_var($address_row['send_as'], FILTER_VALIDATE_EMAIL) && !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $address_row['send_as'])) {
-        $data['sender_acl_addresses']['ro'][] = $address_row['send_as'];
-        continue;
-      }
-      if (filter_var($address_row['send_as'], FILTER_VALIDATE_EMAIL) && hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $address_row['send_as'])) {
-        $data['sender_acl_addresses']['rw'][] = $address_row['send_as'];
-        continue;
-      }
-    }
-
-    $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
-      WHERE `domain` NOT IN (
-        SELECT REPLACE(`send_as`, '@', '') FROM `sender_acl` 
-          WHERE `logged_in_as` = :logged_in_as
-            AND `send_as` LIKE '@%')");
-    $stmt->execute(array(
-      ':logged_in_as' => $mailbox,
-    ));
-    $rows_domain = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row_domain = array_shift($rows_domain)) {
-      if (is_valid_domain_name($row_domain['domain']) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row_domain['domain'])) {
-        $data['sender_acl_domains']['selectable'][] = $row_domain['domain'];
-      }
-    }
-
-    $stmt = $pdo->prepare("SELECT `address` FROM `alias`
-      WHERE `goto` != :goto
-        AND `address` NOT IN (
-          SELECT `send_as` FROM `sender_acl` 
-            WHERE `logged_in_as` = :logged_in_as
-              AND `send_as` NOT LIKE '@%')");
-    $stmt->execute(array(
-      ':logged_in_as' => $mailbox,
-      ':goto' => $mailbox
-    ));
-    $rows_mbox = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row = array_shift($rows_mbox)) {
-      if (filter_var($row['address'], FILTER_VALIDATE_EMAIL) && hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['address'])) {
-        $data['sender_acl_addresses']['selectable'][] = $row['address'];
-      }
     }
   }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-    return false;
-  }
-  return $data;
+  $_SESSION['return'] = array(
+    'type' => 'success',
+    'msg' => sprintf($lang['success']['dkim_removed'], htmlspecialchars(implode(', ', $domains)))
+  );
+  return true;
 }
 function get_u2f_registrations($username) {
   global $pdo;
@@ -5220,6 +1639,19 @@ function get_logs($container, $lines = 100) {
       return $data_array;
     }
   }
+  if ($container == "rspamd-history") {
+    $curl = curl_init();
+    curl_setopt($curl, CURLOPT_URL,"http://rspamd-mailcow:11334/history");
+    curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+    $history = curl_exec($curl);
+    if (!curl_errno($ch)) {
+      $data_array = json_decode($history, true);
+      curl_close($curl);
+      return $data_array['rows'];
+    }
+    curl_close($curl);
+    return false;
+  }
   return false;
 }
 ?>
diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php
new file mode 100644
index 00000000..53a93c53
--- /dev/null
+++ b/data/web/inc/functions.mailbox.inc.php
@@ -0,0 +1,3116 @@
+<?php
+function mailbox($_action, $_type, $_data = null) {
+	global $pdo;
+	global $redis;
+	global $lang;
+  switch ($_action) {
+    case 'add':
+      switch ($_type) {
+        case 'time_limited_alias':
+          if (isset($_data['username']) && filter_var($_data['username'], FILTER_VALIDATE_EMAIL)) {
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data['username'])) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            else {
+              $username = $_data['username'];
+            }
+          }
+          else {
+            $username = $_SESSION['mailcow_cc_username'];
+          }
+          if (!is_numeric($_data["validity"]) || $_data["validity"] > 672) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['validity_missing'])
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `domain` FROM `mailbox` WHERE `username` = :username");
+            $stmt->execute(array(':username' => $_SESSION['mailcow_cc_username']));
+            $domain = $stmt->fetch(PDO::FETCH_ASSOC)['domain'];
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          $validity = strtotime("+".$_data["validity"]." hour"); 
+          $letters = 'abcefghijklmnopqrstuvwxyz1234567890';
+          $random_name = substr(str_shuffle($letters), 0, 24);
+          try {
+            $stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `goto`, `validity`) VALUES
+              (:address, :goto, :validity)");
+            $stmt->execute(array(
+              ':address' => $random_name . '@' . $domain,
+              ':goto' => $username,
+              ':validity' => $validity
+            ));
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($usernames))
+          );
+        break;
+        case 'syncjob':
+          if (isset($_data['username']) && filter_var($_data['username'], FILTER_VALIDATE_EMAIL)) {
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data['username'])) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            else {
+              $username = $_data['username'];
+            }
+          }
+          else {
+            $username = $_SESSION['mailcow_cc_username'];
+          }
+          $active  = intval($_data['active']);
+          $delete2duplicates = intval($_data['delete2duplicates']);
+          $delete1  = intval($_data['delete1']);
+          $port1            = $_data['port1'];
+          $host1            = $_data['host1'];
+          $password1        = $_data['password1'];
+          $exclude          = $_data['exclude'];
+          $maxage           = $_data['maxage'];
+          $subfolder2       = $_data['subfolder2'];
+          $user1            = $_data['user1'];
+          $mins_interval    = $_data['mins_interval'];
+          $enc1             = $_data['enc1'];
+          if (empty($subfolder2)) {
+            $subfolder2 = "";
+          }
+          if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) {
+            $maxage = "0";
+          }
+          if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          if (!filter_var($mins_interval, FILTER_VALIDATE_INT, array('options' => array('min_range' => 10, 'max_range' => 3600)))) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          if (!is_valid_domain_name($host1)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          if ($enc1 != "TLS" && $enc1 != "SSL" && $enc1 != "PLAIN") {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          if (@preg_match("/" . $exclude . "/", null) === false) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `user2`, `user1` FROM `imapsync`
+              WHERE `user2` = :user2 AND `user1` = :user1");
+            $stmt->execute(array(':user1' => $user1, ':user2' => $username));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          if ($num_results != 0) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($host1 . ' / ' . $user1))
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("INSERT INTO `imapsync` (`user2`, `exclude`, `delete1`, `maxage`, `subfolder2`, `host1`, `authmech1`, `user1`, `password1`, `mins_interval`, `port1`, `enc1`, `delete2duplicates`, `active`)
+              VALUES (:user2, :exclude, :maxage, :delete1, :subfolder2, :host1, :authmech1, :user1, :password1, :mins_interval, :port1, :enc1, :delete2duplicates, :active)");
+            $stmt->execute(array(
+              ':user2' => $username,
+              ':exclude' => $exclude,
+              ':maxage' => $maxage,
+              ':delete1' => $delete1,
+              ':subfolder2' => $subfolder2,
+              ':host1' => $host1,
+              ':authmech1' => 'PLAIN',
+              ':user1' => $user1,
+              ':password1' => $password1,
+              ':mins_interval' => $mins_interval,
+              ':port1' => $port1,
+              ':enc1' => $enc1,
+              ':delete2duplicates' => $delete2duplicates,
+              ':active' => $active,
+            ));
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_modified'], $username)
+          );
+          return true;
+        break;
+        case 'domain':
+          if ($_SESSION['mailcow_cc_role'] != "admin") {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          $domain				= idn_to_ascii(strtolower(trim($_data['domain'])));
+          $description  = $_data['description'];
+          $aliases			= $_data['aliases'];
+          $mailboxes    = $_data['mailboxes'];
+          $maxquota			= $_data['maxquota'];
+          $quota				= $_data['quota'];
+          if ($maxquota > $quota) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['mailbox_quota_exceeds_domain_quota'])
+            );
+            return false;
+          }
+          if ($maxquota == "0" || empty($maxquota)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['maxquota_empty'])
+            );
+            return false;
+          }
+          $active = intval($_data['active']);
+          $relay_all_recipients = intval($_data['relay_all_recipients']);
+          $backupmx = intval($_data['backupmx']);
+          ($relay_all_recipients == 1) ? $backupmx = '1' : null;
+          if (!is_valid_domain_name($domain)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['domain_invalid'])
+            );
+            return false;
+          }
+          foreach (array($quota, $maxquota, $mailboxes, $aliases) as $data) {
+            if (!is_numeric($data)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['object_is_not_numeric'], htmlspecialchars($data))
+              );
+              return false;
+            }
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+              WHERE `domain` = :domain");
+            $stmt->execute(array(':domain' => $domain));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
+              WHERE `alias_domain` = :domain");
+            $stmt->execute(array(':domain' => $domain));
+            $num_results = $num_results + count($stmt->fetchAll(PDO::FETCH_ASSOC));
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          if ($num_results != 0) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['domain_exists'], htmlspecialchars($domain))
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("INSERT INTO `domain` (`domain`, `description`, `aliases`, `mailboxes`, `maxquota`, `quota`, `transport`, `backupmx`, `active`, `relay_all_recipients`)
+              VALUES (:domain, :description, :aliases, :mailboxes, :maxquota, :quota, 'virtual', :backupmx, :active, :relay_all_recipients)");
+            $stmt->execute(array(
+              ':domain' => $domain,
+              ':description' => $description,
+              ':aliases' => $aliases,
+              ':mailboxes' => $mailboxes,
+              ':maxquota' => $maxquota,
+              ':quota' => $quota,
+              ':backupmx' => $backupmx,
+              ':active' => $active,
+              ':relay_all_recipients' => $relay_all_recipients
+            ));
+            try {
+              $redis->hSet('DOMAIN_MAP', $domain, 1);
+            }
+            catch (RedisException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'Redis: '.$e
+              );
+              return false;
+            }
+            $_SESSION['return'] = array(
+              'type' => 'success',
+              'msg' => sprintf($lang['success']['domain_added'], htmlspecialchars($domain))
+            );
+          }
+          catch (PDOException $e) {
+            mailbox('delete', 'domain', array('domain' => $domain));
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+        break;
+        case 'alias':
+          $addresses  = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['address']));
+          $gotos      = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['goto']));
+          $active = intval($_data['active']);
+          if (empty($addresses[0])) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['alias_empty'])
+            );
+            return false;
+          }
+          if (empty($gotos[0])) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['goto_empty'])
+            );
+            return false;
+          }
+          foreach ($addresses as $address) {
+            if (empty($address)) {
+              continue;
+            }
+            $stmt = $pdo->prepare("SELECT `address` FROM `alias`
+              WHERE `address`= :address");
+            $stmt->execute(array(':address' => $address));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results != 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['is_alias_or_mailbox'], htmlspecialchars($address))
+              );
+              return false;
+            }
+            $domain       = idn_to_ascii(substr(strstr($address, '@'), 1));
+            $local_part   = strstr($address, '@', true);
+            $address      = $local_part.'@'.$domain;
+            $domaindata = mailbox('get', 'domain_details', $domain);
+            if (is_array($domaindata) && $domaindata['aliases_left'] == "0") {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['max_alias_exceeded'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+                WHERE `domain`= :domain1 OR `domain` = (SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain2)");
+              $stmt->execute(array(':domain1' => $domain, ':domain2' => $domain));
+              $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+              if ($num_results == 0) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['domain_not_found'], htmlspecialchars($domain))
+                );
+                return false;
+              }
+              $stmt = $pdo->prepare("SELECT `address` FROM `alias`
+                WHERE `address`= :address");
+              $stmt->execute(array(':address' => $address));
+              $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+              if ($num_results != 0) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['is_alias_or_mailbox'], htmlspecialchars($address))
+                );
+                return false;
+              }
+              $stmt = $pdo->prepare("SELECT `address` FROM `spamalias`
+                WHERE `address`= :address");
+              $stmt->execute(array(':address' => $address));
+              $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+              if ($num_results != 0) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['is_spam_alias'], htmlspecialchars($address))
+                );
+                return false;
+              }
+            }
+            catch(PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+            if ((!filter_var($address, FILTER_VALIDATE_EMAIL) === true) && !empty($local_part)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['alias_invalid'])
+              );
+              return false;
+            }
+            if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            foreach ($gotos as &$goto) {
+              if (empty($goto)) {
+                continue;
+              }
+              $goto_domain		= idn_to_ascii(substr(strstr($goto, '@'), 1));
+              $goto_local_part	= strstr($goto, '@', true);
+              $goto				= $goto_local_part.'@'.$goto_domain;
+              $stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
+                WHERE `kind` REGEXP 'location|thing|group'
+                  AND `username`= :goto");
+              $stmt->execute(array(':goto' => $goto));
+              $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+              if ($num_results != 0) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['goto_invalid'])
+                );
+                return false;
+              }
+              if (!filter_var($goto, FILTER_VALIDATE_EMAIL) === true) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['goto_invalid'])
+                );
+                return false;
+              }
+              if ($goto == $address) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['alias_goto_identical'])
+                );
+                return false;
+              }
+            }
+            $gotos = array_filter($gotos);
+            $goto = implode(",", $gotos);
+            try {
+              $stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `active`)
+                VALUES (:address, :goto, :domain, :active)");
+              if (!filter_var($address, FILTER_VALIDATE_EMAIL) === true) {
+                $stmt->execute(array(
+                  ':address' => '@'.$domain,
+                  ':goto' => $goto,
+                  ':domain' => $domain,
+                  ':active' => $active
+                ));
+              }
+              else {
+                $stmt->execute(array(
+                  ':address' => $address,
+                  ':goto' => $goto,
+                  ':domain' => $domain,
+                  ':active' => $active
+                ));
+              }
+              $_SESSION['return'] = array(
+                'type' => 'success',
+                'msg' => sprintf($lang['success']['alias_added'])
+              );
+            }
+            catch (PDOException $e) {
+              mailbox('delete', 'alias', array('address' => $address));
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['alias_added'])
+          );
+        break;
+        case 'alias_domain':
+          $active = intval($_data['active']);
+          $alias_domain     = idn_to_ascii(strtolower(trim($_data['alias_domain'])));
+          $target_domain    = idn_to_ascii(strtolower(trim($_data['target_domain'])));
+          if (!is_valid_domain_name($alias_domain)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['alias_domain_invalid'])
+            );
+            return false;
+          }
+          if (!is_valid_domain_name($target_domain)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['target_domain_invalid'])
+            );
+            return false;
+          }
+          if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $target_domain)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          if ($alias_domain == $target_domain) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['aliasd_targetd_identical'])
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+              WHERE `domain`= :target_domain");
+            $stmt->execute(array(':target_domain' => $target_domain));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results == 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['targetd_not_found'])
+              );
+              return false;
+            }
+            $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `alias_domain`= :alias_domain
+              UNION
+              SELECT `alias_domain` FROM `alias_domain` WHERE `alias_domain`= :alias_domain_in_domain");
+            $stmt->execute(array(':alias_domain' => $alias_domain, ':alias_domain_in_domain' => $alias_domain));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results != 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['aliasd_exists'])
+              );
+              return false;
+            }
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("INSERT INTO `alias_domain` (`alias_domain`, `target_domain`, `active`)
+              VALUES (:alias_domain, :target_domain, :active)");
+            $stmt->execute(array(
+              ':alias_domain' => $alias_domain,
+              ':target_domain' => $target_domain,
+              ':active' => $active
+            ));
+            $_SESSION['return'] = array(
+              'type' => 'success',
+              'msg' => sprintf($lang['success']['aliasd_added'], htmlspecialchars($alias_domain))
+            );
+          }
+          catch (PDOException $e) {
+            mailbox('delete', 'alias_domain', array('alias_domain' => $alias_domain));
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+        break;
+        case 'mailbox':
+          $local_part   = strtolower(trim($_data['local_part']));
+          $domain       = idn_to_ascii(strtolower(trim($_data['domain'])));
+          $username     = $local_part . '@' . $domain;
+          if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['mailbox_invalid'])
+            );
+            return false;
+          }
+          if (empty($_data['local_part'])) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['mailbox_invalid'])
+            );
+            return false;
+          }
+          $password     = $_data['password'];
+          $password2    = $_data['password2'];
+          $name         = $_data['name'];
+          $quota_m			= filter_var($_data['quota'], FILTER_SANITIZE_NUMBER_FLOAT);
+          if (empty($name)) {
+            $name = $local_part;
+          }
+          $active = intval($_data['active']);
+          $quota_b		= ($quota_m * 1048576);
+          $maildir		= $domain."/".$local_part."/";
+          if (!is_valid_domain_name($domain)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['domain_invalid'])
+            );
+            return false;
+          }
+          if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `mailboxes`, `maxquota`, `quota` FROM `domain`
+              WHERE `domain` = :domain");
+            $stmt->execute(array(':domain' => $domain));
+            $DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
+            $stmt = $pdo->prepare("SELECT 
+              COUNT(*) as count,
+              COALESCE(ROUND(SUM(`quota`)/1048576), 0) as `quota`
+                FROM `mailbox`
+                  WHERE `kind` NOT REGEXP 'location|thing|group'
+                    AND `domain` = :domain");
+            $stmt->execute(array(':domain' => $domain));
+            $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
+            $stmt = $pdo->prepare("SELECT `local_part` FROM `mailbox` WHERE `local_part` = :local_part and `domain`= :domain");
+            $stmt->execute(array(':local_part' => $local_part, ':domain' => $domain));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results != 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($username))
+              );
+              return false;
+            }
+            $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE address= :username");
+            $stmt->execute(array(':username' => $username));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results != 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['is_alias'], htmlspecialchars($username))
+              );
+              return false;
+            }
+            $stmt = $pdo->prepare("SELECT `address` FROM `spamalias` WHERE `address`= :username");
+            $stmt->execute(array(':username' => $username));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results != 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['is_spam_alias'], htmlspecialchars($username))
+              );
+              return false;
+            }
+            $stmt = $pdo->prepare("SELECT `domain` FROM `domain` WHERE `domain`= :domain");
+            $stmt->execute(array(':domain' => $domain));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results == 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['domain_not_found'], $domain)
+              );
+              return false;
+            }
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          if (!is_numeric($quota_m) || $quota_m == "0") {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['quota_not_0_not_numeric'])
+            );
+            return false;
+          }
+          if (!empty($password) && !empty($password2)) {
+            if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['password_complexity'])
+              );
+              return false;
+            }
+            if ($password != $password2) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['password_mismatch'])
+              );
+              return false;
+            }
+            $password_hashed = hash_password($password);
+          }
+          else {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['password_empty'])
+            );
+            return false;
+          }
+          if ($MailboxData['count'] >= $DomainData['mailboxes']) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['max_mailbox_exceeded'], $MailboxData['count'], $DomainData['mailboxes'])
+            );
+            return false;
+          }
+          if ($quota_m > $DomainData['maxquota']) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['mailbox_quota_exceeded'], $DomainData['maxquota'])
+            );
+            return false;
+          }
+          if (($MailboxData['quota'] + $quota_m) > $DomainData['quota']) {
+            $quota_left_m = ($DomainData['quota'] - $MailboxData['quota']);
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['mailbox_quota_left_exceeded'], $quota_left_m)
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `active`) 
+              VALUES (:username, :password_hashed, :name, :maildir, :quota_b, :local_part, :domain, :active)");
+            $stmt->execute(array(
+              ':username' => $username,
+              ':password_hashed' => $password_hashed,
+              ':name' => $name,
+              ':maildir' => $maildir,
+              ':quota_b' => $quota_b,
+              ':local_part' => $local_part,
+              ':domain' => $domain,
+              ':active' => $active
+            ));
+            $stmt = $pdo->prepare("INSERT INTO `quota2` (`username`, `bytes`, `messages`)
+              VALUES (:username, '0', '0')");
+            $stmt->execute(array(':username' => $username));
+            $stmt = $pdo->prepare("INSERT INTO `alias` (`address`, `goto`, `domain`, `active`)
+              VALUES (:username1, :username2, :domain, :active)");
+            $stmt->execute(array(
+              ':username1' => $username,
+              ':username2' => $username,
+              ':domain' => $domain,
+              ':active' => $active
+            ));
+            $_SESSION['return'] = array(
+              'type' => 'success',
+              'msg' => sprintf($lang['success']['mailbox_added'], htmlspecialchars($username))
+            );
+          }
+          catch (PDOException $e) {
+            mailbox('delete', 'mailbox', array('username' => $username));
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+        break;
+        case 'resource':
+          $domain             = idn_to_ascii(strtolower(trim($_data['domain'])));
+          $description        = $_data['description'];
+          $local_part         = preg_replace('/[^\da-z]/i', '', preg_quote($description, '/'));
+          $name               = $local_part . '@' . $domain;
+          $kind               = $_data['kind'];
+          $active = intval($_data['active']);
+          $multiple_bookings = intval($_data['multiple_bookings']);
+          if (!filter_var($name, FILTER_VALIDATE_EMAIL)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['resource_invalid'])
+            );
+            return false;
+          }
+          if (empty($description)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['description_invalid'])
+            );
+            return false;
+          }
+          
+          if ($kind != 'location' && $kind != 'group' && $kind != 'thing') {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['resource_invalid'])
+            );
+            return false;
+          }
+          if (!is_valid_domain_name($domain)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['domain_invalid'])
+            );
+            return false;
+          }
+          if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :name");
+            $stmt->execute(array(':name' => $name));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results != 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($name))
+              );
+              return false;
+            }
+            $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE address= :name");
+            $stmt->execute(array(':name' => $name));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results != 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['is_alias'], htmlspecialchars($name))
+              );
+              return false;
+            }
+            $stmt = $pdo->prepare("SELECT `address` FROM `spamalias` WHERE `address`= :name");
+            $stmt->execute(array(':name' => $name));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results != 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['is_spam_alias'], htmlspecialchars($name))
+              );
+              return false;
+            }
+            $stmt = $pdo->prepare("SELECT `domain` FROM `domain` WHERE `domain`= :domain");
+            $stmt->execute(array(':domain' => $domain));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results == 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['domain_not_found'], $domain)
+              );
+              return false;
+            }
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `maildir`, `quota`, `local_part`, `domain`, `active`, `multiple_bookings`, `kind`) 
+              VALUES (:name, 'RESOURCE', :description, 'RESOURCE', 0, :local_part, :domain, :active, :multiple_bookings, :kind)");
+            $stmt->execute(array(
+              ':name' => $name,
+              ':description' => $description,
+              ':local_part' => $local_part,
+              ':domain' => $domain,
+              ':active' => $active,
+              ':kind' => $kind,
+              ':multiple_bookings' => $multiple_bookings
+            ));
+            $_SESSION['return'] = array(
+              'type' => 'success',
+              'msg' => sprintf($lang['success']['resource_added'], htmlspecialchars($name))
+            );
+          }
+          catch (PDOException $e) {
+            mailbox('delete', 'resource', array('name' => $name));
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+        break;
+      }
+    break;
+    case 'edit':
+      switch ($_type) {
+        case 'alias_domain':
+          if (!is_array($_data['alias_domain'])) {
+            $alias_domains = array();
+            $alias_domains[] = $_data['alias_domain'];
+          }
+          else {
+            $alias_domains = $_data['alias_domain'];
+          }
+          foreach ($alias_domains as $alias_domain) {
+            $alias_domain = idn_to_ascii(strtolower(trim($alias_domain)));
+            $is_now = mailbox('get', 'alias_domain_details', $alias_domain);
+            if (!empty($is_now)) {
+              $active         = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
+              $target_domain  = (!empty($_data['target_domain'])) ? idn_to_ascii(strtolower(trim($_data['target_domain']))) : $is_now['target_domain'];
+            }
+            else {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['alias_domain_invalid'])
+              );
+              return false;
+            }
+            if (!is_valid_domain_name($target_domain)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['target_domain_invalid'])
+              );
+              return false;
+            }
+            if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $target_domain)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if (empty(mailbox('get', 'domain_details', $target_domain))) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['target_domain_invalid'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("UPDATE `alias_domain` SET
+                `target_domain` = :target_domain,
+                `active` = :active
+                  WHERE `alias_domain` = :alias_domain");
+              $stmt->execute(array(
+                ':alias_domain' => $alias_domain,
+                ':target_domain' => $target_domain,
+                ':active' => $active
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['aliasd_modified'], htmlspecialchars(implode(', ', $alias_domains)))
+          );
+        break;
+        case 'tls_policy':
+          if (!is_array($_data['username'])) {
+            $usernames = array();
+            $usernames[] = $_data['username'];
+          }
+          else {
+            $usernames = $_data['username'];
+          }
+          foreach ($usernames as $username) {
+            if (!filter_var($username, FILTER_VALIDATE_EMAIL) || !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            $tls_enforce_out = intval($_data['tls_enforce_out']);
+            $tls_enforce_in = intval($_data['tls_enforce_in']);
+            $is_now = mailbox('get', 'tls_policy', $username);
+            if (!empty($is_now)) {
+              $tls_enforce_in = (isset($_data['tls_enforce_in'])) ? $_data['tls_enforce_in'] : $is_now['tls_enforce_in'];
+              $tls_enforce_out = (isset($_data['tls_enforce_out'])) ? $_data['tls_enforce_out'] : $is_now['tls_enforce_out'];
+            }
+            else {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("UPDATE `mailbox` SET `tls_enforce_out` = :tls_out, `tls_enforce_in` = :tls_in WHERE `username` = :username");
+              $stmt->execute(array(
+                ':tls_out' => $tls_enforce_out,
+                ':tls_in' => $tls_enforce_in,
+                ':username' => $username
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_modified'], implode(', ', $usernames))
+          );
+        break;
+        case 'spam_score':
+          if (!is_array($_data['username'])) {
+            $usernames = array();
+            $usernames[] = $_data['username'];
+          }
+          else {
+            $usernames = $_data['username'];
+          }
+          foreach ($usernames as $username) {
+            $lowspamlevel	= explode(',', $_data['spam_score'])[0];
+            $highspamlevel	= explode(',', $_data['spam_score'])[1];
+            if (!is_numeric($lowspamlevel) || !is_numeric($highspamlevel)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username
+                AND (`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option`, `value`)
+                VALUES (:username, 'highspamlevel', :highspamlevel)");
+              $stmt->execute(array(
+                ':username' => $username,
+                ':highspamlevel' => $highspamlevel
+              ));
+              $stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option`, `value`)
+                VALUES (:username, 'lowspamlevel', :lowspamlevel)");
+              $stmt->execute(array(
+                ':username' => $username,
+                ':lowspamlevel' => $lowspamlevel
+              ));
+            }
+            catch (PDOException $e) {
+              $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username
+                AND (`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_modified'], implode(', ', $usernames))
+          );
+        break;
+        case 'time_limited_alias':
+          if (!is_array($_data['address'])) {
+            $addresses = array();
+            $addresses[] = $_data['address'];
+          }
+          else {
+            $addresses = $_data['address'];
+          }
+          foreach ($addresses as $address) {
+            try {
+              $stmt = $pdo->prepare("SELECT `goto` FROM `spamalias` WHERE `address` = :address");
+              $stmt->execute(array(':address' => $address));
+              $goto = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $goto)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = (`validity` + 3600) WHERE 
+                `address` = :address AND
+                `validity` >= :validity");
+              $stmt->execute(array(
+                ':address' => $address,
+                ':validity' => time()
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars(implode(', ', $usernames)))
+          );
+        break;
+        case 'delimiter_action':
+          if (!is_array($_data['username'])) {
+            $usernames = array();
+            $usernames[] = $_data['username'];
+          }
+          else {
+            $usernames = $_data['username'];
+          }
+          foreach ($usernames as $username) {
+            if (!filter_var($username, FILTER_VALIDATE_EMAIL) || !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if (isset($_data['tagged_mail_handler']) && $_data['tagged_mail_handler'] == "subject") {
+              try {
+                $redis->hSet('RCPT_WANTS_SUBJECT_TAG', $username, 1);
+              }
+              catch (RedisException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'Redis: '.$e
+                );
+                return false;
+              }
+            }
+            else {
+              try {
+                $redis->hDel('RCPT_WANTS_SUBJECT_TAG', $username);
+              }
+              catch (RedisException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'Redis: '.$e
+                );
+                return false;
+              }
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_modified'], implode(', ', $usernames))
+          );
+        break;
+        case 'syncjob':
+          if (!is_array($_data['id'])) {
+            $ids = array();
+            $ids[] = $_data['id'];
+          }
+          else {
+            $ids = $_data['id'];
+          }
+          foreach ($ids as $id) {
+            $is_now = mailbox('get', 'syncjob_details', $id);
+            if (!empty($is_now)) {
+              $username = $is_now['user2'];
+              $user1 = (!empty($_data['user1'])) ? $_data['user1'] : $is_now['user1'];
+              $active = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
+              $delete2duplicates = (isset($_data['delete2duplicates'])) ? $_data['delete2duplicates'] : $is_now['delete2duplicates'];
+              $delete1 = (isset($_data['delete1'])) ? $_data['delete1'] : $is_now['delete1'];
+              $port1 = (!empty($_data['port1'])) ? $_data['port1'] : $is_now['port1'];
+              $password1 = (!empty($_data['password1'])) ? $_data['password1'] : $is_now['password1'];
+              $host1 = (!empty($_data['host1'])) ? $_data['host1'] : $is_now['host1'];
+              $subfolder2 = (!empty($_data['subfolder2'])) ? $_data['subfolder2'] : $is_now['subfolder2'];
+              $enc1 = (!empty($_data['enc1'])) ? $_data['enc1'] : $is_now['enc1'];
+              $mins_interval = (!empty($_data['mins_interval'])) ? $_data['mins_interval'] : $is_now['mins_interval'];
+              $exclude = (!empty($_data['exclude'])) ? $_data['exclude'] : $is_now['exclude'];
+              $maxage = (!empty($_data['maxage'])) ? $_data['maxage'] : $is_now['maxage'];
+            }
+            else {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if (empty($subfolder2)) {
+              $subfolder2 = "";
+            }
+            if (!isset($maxage) || !filter_var($maxage, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 32767)))) {
+              $maxage = "0";
+            }
+            if (!filter_var($port1, FILTER_VALIDATE_INT, array('options' => array('min_range' => 1, 'max_range' => 65535)))) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if (!filter_var($mins_interval, FILTER_VALIDATE_INT, array('options' => array('min_range' => 10, 'max_range' => 3600)))) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if (!is_valid_domain_name($host1)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if ($enc1 != "TLS" && $enc1 != "SSL" && $enc1 != "PLAIN") {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if (@preg_match("/" . $exclude . "/", null) === false) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("UPDATE `imapsync` SET `delete1` = :delete1, `maxage` = :maxage, `subfolder2` = :subfolder2, `exclude` = :exclude, `host1` = :host1, `user1` = :user1, `password1` = :password1, `mins_interval` = :mins_interval, `port1` = :port1, `enc1` = :enc1, `delete2duplicates` = :delete2duplicates, `active` = :active
+                WHERE `id` = :id");
+              $stmt->execute(array(
+                ':delete1' => $delete1,
+                ':id' => $id,
+                ':exclude' => $exclude,
+                ':maxage' => $maxage,
+                ':subfolder2' => $subfolder2,
+                ':host1' => $host1,
+                ':user1' => $user1,
+                ':password1' => $password1,
+                ':mins_interval' => $mins_interval,
+                ':port1' => $port1,
+                ':enc1' => $enc1,
+                ':delete2duplicates' => $delete2duplicates,
+                ':active' => $active,
+              ));
+            }
+            catch(PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_modified'], $username)
+          );
+          return true;
+        break;
+        case 'alias':
+          if (!is_array($_data['address'])) {
+            $addresses = array();
+            $addresses[] = $_data['address'];
+          }
+          else {
+            $addresses = $_data['address'];
+          }
+          foreach ($addresses as $address) {
+            $is_now = mailbox('get', 'alias_details', $address);
+            if (!empty($is_now)) {
+              $active = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
+              $goto   = (!empty($_data['goto'])) ? $_data['goto'] : $is_now['goto'];
+            }
+            else {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['alias_invalid'])
+              );
+              return false;
+            }
+            
+            $gotos = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['goto']));
+            foreach ($gotos as &$goto) {
+              if (empty($goto)) {
+                continue;
+              }
+              if (!filter_var($goto, FILTER_VALIDATE_EMAIL)) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' =>sprintf($lang['danger']['goto_invalid'])
+                );
+                return false;
+              }
+              if ($goto == $address) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['alias_goto_identical'])
+                );
+                return false;
+              }
+            }
+            $gotos = array_filter($gotos);
+            $goto = implode(",", $gotos);
+            
+            $domain = idn_to_ascii(substr(strstr($address, '@'), 1));
+            $local_part = strstr($address, '@', true);
+            if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if ((!filter_var($address, FILTER_VALIDATE_EMAIL) === true) && !empty($local_part)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['alias_invalid'])
+              );
+              return false;
+            }
+            try {
+              if (!empty($goto)) {
+                $stmt = $pdo->prepare("UPDATE `alias` SET
+                  `goto` = :goto,
+                  `active`= :active
+                    WHERE `address` = :address");
+                $stmt->execute(array(
+                  ':goto' => $goto,
+                  ':active' => $active,
+                  ':address' => $address
+                ));
+              }
+              else {
+                $stmt = $pdo->prepare("UPDATE `alias` SET
+                  `active`= :active
+                    WHERE `address` = :address");
+                $stmt->execute(array(
+                  ':active' => $active,
+                  ':address' => $address
+                ));
+              }
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['alias_modified'], htmlspecialchars(implode(', ', $addresses)))
+          );
+        break;
+        case 'domain':
+          if (!is_array($_data['domain'])) {
+            $domains = array();
+            $domains[] = $_data['domain'];
+          }
+          else {
+            $domains = $_data['domain'];
+          }
+          foreach ($domains as $domain) {
+            $domain = idn_to_ascii($domain);
+            if (!is_valid_domain_name($domain)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['domain_invalid'])
+              );
+              return false;
+            }
+            if ($_SESSION['mailcow_cc_role'] == "domainadmin" &&
+            hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+              $description  = $_data['description'];
+              $active = intval($_data['active']);
+              try {
+                $stmt = $pdo->prepare("UPDATE `domain` SET 
+                `description` = :description
+                  WHERE `domain` = :domain");
+                $stmt->execute(array(
+                  ':description' => $description,
+                  ':domain' => $domain
+                ));
+                $_SESSION['return'] = array(
+                  'type' => 'success',
+                  'msg' => sprintf($lang['success']['domain_modified'], htmlspecialchars($domain))
+                );
+              }
+              catch (PDOException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'MySQL: '.$e
+                );
+                return false;
+              }
+            }
+            elseif ($_SESSION['mailcow_cc_role'] == "admin") {
+              $is_now = mailbox('get', 'domain_details', $domain);
+              if (!empty($is_now)) {
+                $active               = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
+                $backupmx             = (isset($_data['backupmx'])) ? $_data['backupmx'] : $is_now['backupmx_int'];
+                $relay_all_recipients = (isset($_data['relay_all_recipients'])) ? $_data['relay_all_recipients'] : $is_now['relay_all_recipients_int'];
+                $aliases              = (!empty($_data['aliases'])) ? $_data['aliases'] : $is_now['max_num_aliases_for_domain'];
+                $mailboxes            = (!empty($_data['mailboxes'])) ? $_data['mailboxes'] : $is_now['max_num_mboxes_for_domain'];
+                $maxquota             = (!empty($_data['maxquota'])) ? $_data['maxquota'] : ($is_now['max_new_mailbox_quota'] / 1048576);
+                $quota                = (!empty($_data['quota'])) ? $_data['quota'] : ($is_now['max_quota_for_domain'] / 1048576);
+                $description          = (!empty($_data['description'])) ? $_data['description'] : $is_now['description'];
+                ($relay_all_recipients == '1') ? $backupmx = '1' : null;
+              }
+              else {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['domain_invalid'])
+                );
+                return false;
+              }
+              try {
+                // todo: should be using api here
+                $stmt = $pdo->prepare("SELECT 
+                    COUNT(*) AS count,
+                    MAX(COALESCE(ROUND(`quota`/1048576), 0)) AS `biggest_mailbox`,
+                    COALESCE(ROUND(SUM(`quota`)/1048576), 0) AS `quota_all`
+                      FROM `mailbox`
+                        WHERE `kind` NOT REGEXP 'location|thing|group'
+                          AND domain = :domain");
+                $stmt->execute(array(':domain' => $domain));
+                $MailboxData = $stmt->fetch(PDO::FETCH_ASSOC);
+                // todo: should be using api here
+                $stmt = $pdo->prepare("SELECT COUNT(*) AS `count` FROM `alias`
+                    WHERE domain = :domain
+                    AND address NOT IN (
+                      SELECT `username` FROM `mailbox`
+                    )");
+                $stmt->execute(array(':domain' => $domain));
+                $AliasData = $stmt->fetch(PDO::FETCH_ASSOC);
+              }
+              catch(PDOException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'MySQL: '.$e
+                );
+                return false;
+              }
+              if ($maxquota > $quota) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['mailbox_quota_exceeds_domain_quota'])
+                );
+                return false;
+              }
+              if ($maxquota == "0" || empty($maxquota)) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['maxquota_empty'])
+                );
+                return false;
+              }
+              if ($MailboxData['biggest_mailbox'] > $maxquota) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['max_quota_in_use'], $MailboxData['biggest_mailbox'])
+                );
+                return false;
+              }
+              if ($MailboxData['quota_all'] > $quota) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['domain_quota_m_in_use'], $MailboxData['quota_all'])
+                );
+                return false;
+              }
+              if ($MailboxData['count'] > $mailboxes) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['mailboxes_in_use'], $MailboxData['count'])
+                );
+                return false;
+              }
+              if ($AliasData['count'] > $aliases) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['aliases_in_use'], $AliasData['count'])
+                );
+                return false;
+              }
+              try {
+                $stmt = $pdo->prepare("UPDATE `domain` SET 
+                `relay_all_recipients` = :relay_all_recipients,
+                `backupmx` = :backupmx,
+                `active` = :active,
+                `quota` = :quota,
+                `maxquota` = :maxquota,
+                `mailboxes` = :mailboxes,
+                `aliases` = :aliases,
+                `description` = :description
+                  WHERE `domain` = :domain");
+                $stmt->execute(array(
+                  ':relay_all_recipients' => $relay_all_recipients,
+                  ':backupmx' => $backupmx,
+                  ':active' => $active,
+                  ':quota' => $quota,
+                  ':maxquota' => $maxquota,
+                  ':mailboxes' => $mailboxes,
+                  ':aliases' => $aliases,
+                  ':description' => $description,
+                  ':domain' => $domain
+                ));
+              }
+              catch (PDOException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'MySQL: '.$e
+                );
+                return false;
+              }
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['domain_modified'], htmlspecialchars(implode(', ', $domains)))
+          );
+        break;
+        case 'mailbox':
+          if (!is_array($_data['username'])) {
+            $usernames = array();
+            $usernames[] = $_data['username'];
+          }
+          else {
+            $usernames = $_data['username'];
+          }
+          foreach ($usernames as $username) {
+            if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['username_invalid'])
+              );
+              return false;
+            }
+            $is_now = mailbox('get', 'mailbox_details', $username);
+            if (!empty($is_now)) {
+              $active     = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
+              $name       = (!empty($_data['name'])) ? $_data['name'] : $is_now['name'];
+              $domain     = $is_now['domain'];
+              $quota_m    = (!empty($_data['quota'])) ? $_data['quota'] : ($is_now['quota'] / 1048576);
+              $quota_b    = $quota_m * 1048576;
+              $password   = (!empty($_data['password'])) ? $_data['password'] : null;
+              $password2  = (!empty($_data['password2'])) ? $_data['password2'] : null; 
+            }
+            else {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("SELECT `quota`, `maxquota`
+                FROM `domain`
+                  WHERE `domain` = :domain");
+              $stmt->execute(array(':domain' => $domain));
+              $DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
+            }
+            catch(PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+            if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if (!is_numeric($quota_m) || $quota_m == "0") {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['quota_not_0_not_numeric'], htmlspecialchars($quota_m))
+              );
+              return false;
+            }
+            if ($quota_m > $DomainData['maxquota']) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['mailbox_quota_exceeded'], $DomainData['maxquota'])
+              );
+              return false;
+            }
+            if (((($is_now['quota_used'] / 1048576) - $quota_m) + $quota_m) > $DomainData['quota']) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['mailbox_quota_left_exceeded'], ($is_now['max_new_quota'] / 1048576))
+              );
+              return false;
+            }
+            if (isset($_data['sender_acl'])) {
+              // Get sender_acl items set by admin
+              $sender_acl_admin = array_merge(
+                mailbox('get', 'sender_acl_handles', $username)['sender_acl_domains']['ro'],
+                mailbox('get', 'sender_acl_handles', $username)['sender_acl_addresses']['ro']
+              );
+              // Get sender_acl items from POST array
+              $sender_acl_domain_admin = ($_data['sender_acl'] == "0") ? array() : $_data['sender_acl'];
+              if (!empty($sender_acl_domain_admin) || !empty($sender_acl_admin)) {
+                // Check items in POST array
+                foreach ($sender_acl_domain_admin as $sender_acl) {
+                  if (!filter_var($sender_acl, FILTER_VALIDATE_EMAIL) && !is_valid_domain_name(ltrim($sender_acl, '@'))) {
+                      $_SESSION['return'] = array(
+                        'type' => 'danger',
+                        'msg' => sprintf($lang['danger']['sender_acl_invalid'])
+                      );
+                      return false;
+                  }
+                  if (is_valid_domain_name(ltrim($sender_acl, '@'))) {
+                    if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], ltrim($sender_acl, '@'))) {
+                      $_SESSION['return'] = array(
+                        'type' => 'danger',
+                        'msg' => sprintf($lang['danger']['sender_acl_invalid'])
+                      );
+                      return false;
+                    }
+                  }
+                  if (filter_var($sender_acl, FILTER_VALIDATE_EMAIL)) {
+                    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $sender_acl)) {
+                      $_SESSION['return'] = array(
+                        'type' => 'danger',
+                        'msg' => sprintf($lang['danger']['sender_acl_invalid'])
+                      );
+                      return false;
+                    }
+                  }
+                }
+                // Merge both arrays
+                $sender_acl_merged = array_merge($sender_acl_domain_admin, $sender_acl_admin);
+                try {
+                  $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
+                  $stmt->execute(array(
+                    ':username' => $username
+                  ));
+                }
+                catch (PDOException $e) {
+                  $_SESSION['return'] = array(
+                    'type' => 'danger',
+                    'msg' => 'MySQL: '.$e
+                  );
+                  return false;
+                }
+                foreach ($sender_acl_merged as $sender_acl) {
+                  $domain = ltrim($sender_acl, '@');
+                  if (is_valid_domain_name($domain)) {
+                    $sender_acl = '@' . $domain;
+                  }
+                  try {
+                    $stmt = $pdo->prepare("INSERT INTO `sender_acl` (`send_as`, `logged_in_as`)
+                      VALUES (:sender_acl, :username)");
+                    $stmt->execute(array(
+                      ':sender_acl' => $sender_acl,
+                      ':username' => $username
+                    ));
+                  }
+                  catch (PDOException $e) {
+                    $_SESSION['return'] = array(
+                      'type' => 'danger',
+                      'msg' => 'MySQL: '.$e
+                    );
+                    return false;
+                  }
+                }
+              }
+              else {
+                try {
+                  $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
+                  $stmt->execute(array(
+                    ':username' => $username
+                  ));
+                }
+                catch (PDOException $e) {
+                  $_SESSION['return'] = array(
+                    'type' => 'danger',
+                    'msg' => 'MySQL: '.$e
+                  );
+                  return false;
+                }
+              }
+            }
+            if (!empty($password) && !empty($password2)) {
+              if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['password_complexity'])
+                );
+                return false;
+              }
+              if ($password != $password2) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['password_mismatch'])
+                );
+                return false;
+              }
+              $password_hashed = hash_password($password);
+              try {
+                $stmt = $pdo->prepare("UPDATE `alias` SET
+                    `active` = :active
+                      WHERE `address` = :address");
+                $stmt->execute(array(
+                  ':address' => $username,
+                  ':active' => $active
+                ));
+                $stmt = $pdo->prepare("UPDATE `mailbox` SET
+                    `active` = :active,
+                    `password` = :password_hashed,
+                    `name`= :name,
+                    `quota` = :quota_b
+                      WHERE `username` = :username");
+                $stmt->execute(array(
+                  ':password_hashed' => $password_hashed,
+                  ':active' => $active,
+                  ':name' => $name,
+                  ':quota_b' => $quota_b,
+                  ':username' => $username
+                ));
+              }
+              catch (PDOException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'MySQL: '.$e
+                );
+                return false;
+              }
+            }
+            try {
+              $stmt = $pdo->prepare("UPDATE `alias` SET
+                  `active` = :active
+                    WHERE `address` = :address");
+              $stmt->execute(array(
+                ':address' => $username,
+                ':active' => $active
+              ));
+              $stmt = $pdo->prepare("UPDATE `mailbox` SET
+                  `active` = :active,
+                  `name`= :name,
+                  `quota` = :quota_b
+                    WHERE `username` = :username");
+              $stmt->execute(array(
+                ':active' => $active,
+                ':name' => $name,
+                ':quota_b' => $quota_b,
+                ':username' => $username
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_modified'], implode(', ', $usernames))
+          );
+        break;
+        case 'resource':
+          if (!is_array($_data['name'])) {
+            $names = array();
+            $names[] = $_data['name'];
+          }
+          else {
+            $names = $_data['name'];
+          }
+          foreach ($names as $name) {
+            $is_now = mailbox('get', 'resource_details', $name);
+            if (!empty($is_now)) {
+              $active             = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
+              $multiple_bookings  = (isset($_data['multiple_bookings'])) ? $_data['multiple_bookings'] : $is_now['multiple_bookings_int'];
+              $description        = (!empty($_data['description'])) ? $_data['description'] : $is_now['description'];
+              $kind               = (!empty($_data['kind'])) ? $_data['kind'] : $is_now['kind'];
+            }
+            else {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['resource_invalid'])
+              );
+              return false;
+            }
+            if (!filter_var($name, FILTER_VALIDATE_EMAIL)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['resource_invalid'])
+              );
+              return false;
+            }
+            if (empty($description)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['description_invalid'])
+              );
+              return false;
+            }
+            if ($kind != 'location' && $kind != 'group' && $kind != 'thing') {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['resource_invalid'])
+              );
+              return false;
+            }
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $name)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("UPDATE `mailbox` SET
+                  `active` = :active,
+                  `name`= :description,
+                  `kind`= :kind,
+                  `multiple_bookings`= :multiple_bookings
+                    WHERE `username` = :name");
+              $stmt->execute(array(
+                ':active' => $active,
+                ':description' => $description,
+                ':multiple_bookings' => $multiple_bookings,
+                ':kind' => $kind,
+                ':name' => $name
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['resource_modified'], implode(', ', $names))
+          );
+        break;
+      }
+    break;
+    case 'get':
+      switch ($_type) {
+        case 'sender_acl_handles':
+          if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
+            return false;
+          }
+          $data['sender_acl_domains']['ro']               = array();
+          $data['sender_acl_domains']['rw']               = array();
+          $data['sender_acl_domains']['selectable']       = array();
+          $data['sender_acl_addresses']['ro']             = array();
+          $data['sender_acl_addresses']['rw']             = array();
+          $data['sender_acl_addresses']['selectable']     = array();
+          $data['fixed_sender_aliases']                   = array();
+          try {
+            // Fixed addresses
+            $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` REGEXP :goto AND `address` NOT LIKE '@%'");
+            $stmt->execute(array(':goto' => '(^|,)'.$_data.'($|,)'));
+            $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            while ($row = array_shift($rows)) {
+              $data['fixed_sender_aliases'][] = $row['address'];
+            }
+            $stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `alias_domain_alias` FROM `mailbox`, `alias_domain`
+              WHERE `alias_domain`.`target_domain` = `mailbox`.`domain`
+              AND `mailbox`.`username` = :username");
+            $stmt->execute(array(':username' => $_data));
+            $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            while ($row = array_shift($rows)) {
+              if (!empty($row['alias_domain_alias'])) {
+                $data['fixed_sender_aliases'][] = $row['alias_domain_alias'];
+              }
+            }
+            // 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 '@%'");
+            $stmt->execute(array(':logged_in_as' => $_data));
+            $domain_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            while ($domain_row = array_shift($domain_rows)) {
+              if (is_valid_domain_name($domain_row['send_as']) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain_row['send_as'])) {
+                $data['sender_acl_domains']['ro'][] = $domain_row['send_as'];
+                continue;
+              }
+              if (is_valid_domain_name($domain_row['send_as']) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain_row['send_as'])) {
+                $data['sender_acl_domains']['rw'][] = $domain_row['send_as'];
+                continue;
+              }
+            }
+            $stmt = $pdo->prepare("SELECT `send_as` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as AND `send_as` NOT LIKE '@%'");
+            $stmt->execute(array(':logged_in_as' => $_data));
+            $address_rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            while ($address_row = array_shift($address_rows)) {
+              if (filter_var($address_row['send_as'], FILTER_VALIDATE_EMAIL) && !hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $address_row['send_as'])) {
+                $data['sender_acl_addresses']['ro'][] = $address_row['send_as'];
+                continue;
+              }
+              if (filter_var($address_row['send_as'], FILTER_VALIDATE_EMAIL) && hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $address_row['send_as'])) {
+                $data['sender_acl_addresses']['rw'][] = $address_row['send_as'];
+                continue;
+              }
+            }
+            $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+              WHERE `domain` NOT IN (
+                SELECT REPLACE(`send_as`, '@', '') FROM `sender_acl` 
+                  WHERE `logged_in_as` = :logged_in_as
+                    AND `send_as` LIKE '@%')");
+            $stmt->execute(array(
+              ':logged_in_as' => $_data,
+            ));
+            $rows_domain = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            while ($row_domain = array_shift($rows_domain)) {
+              if (is_valid_domain_name($row_domain['domain']) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row_domain['domain'])) {
+                $data['sender_acl_domains']['selectable'][] = $row_domain['domain'];
+              }
+            }
+            $stmt = $pdo->prepare("SELECT `address` FROM `alias`
+              WHERE `goto` != :goto
+                AND `address` NOT IN (
+                  SELECT `send_as` FROM `sender_acl` 
+                    WHERE `logged_in_as` = :logged_in_as
+                      AND `send_as` NOT LIKE '@%')");
+            $stmt->execute(array(
+              ':logged_in_as' => $_data,
+              ':goto' => $_data
+            ));
+            $rows_mbox = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            while ($row = array_shift($rows_mbox)) {
+              if (filter_var($row['address'], FILTER_VALIDATE_EMAIL) && hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['address'])) {
+                $data['sender_acl_addresses']['selectable'][] = $row['address'];
+              }
+            }
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          return $data;
+        break;
+        case 'mailboxes':
+          $mailboxes = array();
+          if (isset($_data) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            return false;
+          }
+          elseif (isset($_data) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            try {
+              $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` != 'ALL' AND `domain` = :domain");
+              $stmt->execute(array(
+                ':domain' => $_data,
+              ));
+              $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+              while($row = array_shift($rows)) {
+                $mailboxes[] = $row['username'];
+              }
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          else {
+            try {
+              $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role");
+              $stmt->execute(array(
+                ':username' => $_SESSION['mailcow_cc_username'],
+                ':role' => $_SESSION['mailcow_cc_role'],
+              ));
+              $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+              while($row = array_shift($rows)) {
+                $mailboxes[] = $row['username'];
+              }
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          return $mailboxes;
+        break;
+        case 'tls_policy':
+          $policydata = array();
+          if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+              return false;
+            }
+          }
+          else {
+            $_data = $_SESSION['mailcow_cc_username'];
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `tls_enforce_out`, `tls_enforce_in` FROM `mailbox` WHERE `username` = :username");
+            $stmt->execute(array(':username' => $_data));
+            $policydata = $stmt->fetch(PDO::FETCH_ASSOC);
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          return $policydata;
+        break;
+        case 'syncjob_details':
+          $syncjobdetails = array();
+          if (!is_numeric($_data)) {
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT * FROM `imapsync` WHERE id = :id");
+            $stmt->execute(array(':id' => $_data));
+            $syncjobdetails = $stmt->fetch(PDO::FETCH_ASSOC);
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+          }
+          if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $syncjobdetails['user2'])) {
+            return false;
+          }
+          return $syncjobdetails;
+        break;
+        case 'syncjobs':
+          $syncjobdata = array();
+          if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+              return false;
+            }
+          }
+          else {
+            $_data = $_SESSION['mailcow_cc_username'];
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT *,
+              CONCAT(LEFT(`password1`, 3), '...') AS `password1_short`,
+              `active` AS `active_int`,
+              CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
+                FROM `imapsync`
+                  WHERE `user2` = :username");
+            $stmt->execute(array(':username' => $_data));
+            $syncjobdata = $stmt->fetchAll(PDO::FETCH_ASSOC);
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+          }
+          return $syncjobdata;
+        break;
+        case 'spam_score':
+          $default = "5, 15";
+          $policydata = array();
+          if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+              return false;
+            }
+          }
+          else {
+            $_data = $_SESSION['mailcow_cc_username'];
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `value` FROM `filterconf` WHERE `object` = :username AND
+              (`option` = 'lowspamlevel' OR `option` = 'highspamlevel')");
+            $stmt->execute(array(':username' => $_data));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          if (empty($num_results)) {
+            return $default;
+          }
+          else {
+            try {
+              $stmt = $pdo->prepare("SELECT `value` FROM `filterconf` WHERE `option` = 'highspamlevel' AND `object` = :username");
+              $stmt->execute(array(':username' => $_data));
+              $highspamlevel = $stmt->fetch(PDO::FETCH_ASSOC);
+
+              $stmt = $pdo->prepare("SELECT `value` FROM `filterconf` WHERE `option` = 'lowspamlevel' AND `object` = :username");
+              $stmt->execute(array(':username' => $_data));
+              $lowspamlevel = $stmt->fetch(PDO::FETCH_ASSOC);
+
+              return $lowspamlevel['value'].', '.$highspamlevel['value'];
+            }
+            catch(PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+        break;
+        case 'time_limited_aliases':
+          $tladata = array();
+          if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+              return false;
+            }
+          }
+          else {
+            $_data = $_SESSION['mailcow_cc_username'];
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `address`,
+              `goto`,
+              `validity`
+                FROM `spamalias`
+                  WHERE `goto` = :username
+                    AND `validity` >= :unixnow");
+            $stmt->execute(array(':username' => $_data, ':unixnow' => time()));
+            $tladata = $stmt->fetchAll(PDO::FETCH_ASSOC);
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+          }
+          return $tladata;
+        break;
+        case 'delimiter_action':
+          $policydata = array();
+          if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+              return false;
+            }
+          }
+          else {
+            $_data = $_SESSION['mailcow_cc_username'];
+          }
+          try {
+            if ($redis->hGet('RCPT_WANTS_SUBJECT_TAG', $_data)) {
+              return "subject";
+            }
+            else {
+              return "subfolder";
+            }
+          }
+          catch (RedisException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'Redis: '.$e
+            );
+            return false;
+          }
+        break;
+        case 'resources':
+          $resources = array();
+          if (isset($_data) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            return false;
+          }
+          elseif (isset($_data) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            try {
+              $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `domain` != 'ALL' AND `domain` = :domain");
+              $stmt->execute(array(
+                ':domain' => $_data,
+              ));
+              $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+              while($row = array_shift($rows)) {
+                $resources[] = $row['username'];
+              }
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          else {
+            try {
+              $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role");
+              $stmt->execute(array(
+                ':username' => $_SESSION['mailcow_cc_username'],
+                ':role' => $_SESSION['mailcow_cc_role'],
+              ));
+              $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+              while($row = array_shift($rows)) {
+                $resources[] = $row['username'];
+              }
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          return $resources;
+        break;
+        case 'alias_domains':
+          $aliasdomains = array();
+          if (isset($_data) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            return false;
+          }
+          elseif (isset($_data) && hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            try {
+              $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = :domain");
+              $stmt->execute(array(
+                ':domain' => $_data,
+              ));
+              $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+              while($row = array_shift($rows)) {
+                $aliasdomains[] = $row['alias_domain'];
+              }
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          else {
+            try {
+              $stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role");
+              $stmt->execute(array(
+                ':username' => $_SESSION['mailcow_cc_username'],
+                ':role' => $_SESSION['mailcow_cc_role'],
+              ));
+              $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+              while($row = array_shift($rows)) {
+                $aliasdomains[] = $row['alias_domain'];
+              }
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          return $aliasdomains;
+        break;
+        case 'aliases':
+          $aliases = array();
+          if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `address` != `goto` AND `domain` = :domain");
+            $stmt->execute(array(
+              ':domain' => $_data,
+            ));
+            $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            while($row = array_shift($rows)) {
+              $aliases[] = $row['address'];
+            }
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          return $aliases;
+        break;
+        case 'alias_details':
+          $aliasdata = array();
+          try {
+            $stmt = $pdo->prepare("SELECT
+              `domain`,
+              `goto`,
+              `address`,
+              `active` as `active_int`,
+              CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
+              `created`,
+              `modified`
+                FROM `alias`
+                    WHERE `address` = :address AND `address` != `goto`");
+            $stmt->execute(array(
+              ':address' => $_data,
+            ));
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            $stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain");
+            $stmt->execute(array(
+              ':domain' => $row['domain'],
+            ));
+            $row_alias_domain = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (isset($row_alias_domain['target_domain']) && !empty($row_alias_domain['target_domain'])) {
+              $aliasdata['in_primary_domain'] = $row_alias_domain['target_domain'];
+            }
+            else {
+              $aliasdata['in_primary_domain'] = "";
+            }
+            $aliasdata['domain'] = $row['domain'];
+            $aliasdata['goto'] = $row['goto'];
+            $aliasdata['address'] = $row['address'];
+            (!filter_var($aliasdata['address'], FILTER_VALIDATE_EMAIL)) ? $aliasdata['is_catch_all'] = 1 : $aliasdata['is_catch_all'] = 0;
+            $aliasdata['active'] = $row['active'];
+            $aliasdata['active_int'] = $row['active_int'];
+            $aliasdata['created'] = $row['created'];
+            $aliasdata['modified'] = $row['modified'];
+            if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $aliasdata['domain'])) {
+              return false;
+            }
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          return $aliasdata;
+        break;
+        case 'alias_domain_details':
+          $aliasdomaindata = array();
+          try {
+            $stmt = $pdo->prepare("SELECT
+              `alias_domain`,
+              `target_domain`,
+              `active` AS `active_int`,
+              CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
+              `created`,
+              `modified`
+                FROM `alias_domain`
+                    WHERE `alias_domain` = :aliasdomain");
+            $stmt->execute(array(
+              ':aliasdomain' => $_data,
+            ));
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            $aliasdomaindata['alias_domain'] = $row['alias_domain'];
+            $aliasdomaindata['target_domain'] = $row['target_domain'];
+            $aliasdomaindata['active'] = $row['active'];
+            $aliasdomaindata['active_int'] = $row['active_int'];
+            $aliasdomaindata['created'] = $row['created'];
+            $aliasdomaindata['modified'] = $row['modified'];
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $aliasdomaindata['target_domain'])) {
+            return false;
+          }
+          return $aliasdomaindata;
+        break;
+        case 'domains':
+          $domains = array();
+          if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+              WHERE (`domain` IN (
+                SELECT `domain` from `domain_admins`
+                  WHERE (`active`='1' AND `username` = :username))
+                )
+                OR ('admin'= :role)
+                AND `domain` != 'ALL'");
+            $stmt->execute(array(
+              ':username' => $_SESSION['mailcow_cc_username'],
+              ':role' => $_SESSION['mailcow_cc_role'],
+            ));
+            $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            while($row = array_shift($rows)) {
+              $domains[] = $row['domain'];
+            }
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          return $domains;
+        break;
+        case 'domain_details':
+          $domaindata = array();
+          $_data = idn_to_ascii(strtolower(trim($_data)));
+          if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` =  :domain");
+            $stmt->execute(array(
+              ':domain' => $_data
+            ));
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (!empty($row)) { 
+              $_data = $row['target_domain'];
+            }
+            $stmt = $pdo->prepare("SELECT 
+                `domain`,
+                `description`,
+                `aliases`,
+                `mailboxes`, 
+                `maxquota`,
+                `quota`,
+                `relay_all_recipients` as `relay_all_recipients_int`,
+                `backupmx` as `backupmx_int`,
+                `active` as `active_int`,
+                CASE `relay_all_recipients` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `relay_all_recipients`,
+                CASE `backupmx` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `backupmx`,
+                CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
+                  FROM `domain` WHERE `domain`= :domain");
+            $stmt->execute(array(
+              ':domain' => $_data
+            ));
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            if (empty($row)) { 
+              return false;
+            }
+            $stmt = $pdo->prepare("SELECT COUNT(*) AS `count`,
+              COALESCE(SUM(`quota`), 0) AS `in_use`
+                FROM `mailbox`
+                  WHERE `kind` NOT REGEXP 'location|thing|group'
+                    AND `domain` = :domain");
+            $stmt->execute(array(':domain' => $row['domain']));
+            $MailboxDataDomain	= $stmt->fetch(PDO::FETCH_ASSOC);
+            $domaindata['max_new_mailbox_quota']	= ($row['quota'] * 1048576) - $MailboxDataDomain['in_use'];
+            if ($domaindata['max_new_mailbox_quota'] > ($row['maxquota'] * 1048576)) {
+              $domaindata['max_new_mailbox_quota'] = ($row['maxquota'] * 1048576);
+            }
+            $domaindata['quota_used_in_domain'] = $MailboxDataDomain['in_use'];
+            $domaindata['mboxes_in_domain'] = $MailboxDataDomain['count'];
+            $domaindata['mboxes_left'] = $row['mailboxes']	- $MailboxDataDomain['count'];
+            $domaindata['domain_name'] = $row['domain'];
+            $domaindata['description'] = $row['description'];
+            $domaindata['max_num_aliases_for_domain'] = $row['aliases'];
+            $domaindata['max_num_mboxes_for_domain'] = $row['mailboxes'];
+            $domaindata['max_quota_for_mbox'] = $row['maxquota'] * 1048576;
+            $domaindata['max_quota_for_domain'] = $row['quota'] * 1048576;
+            $domaindata['backupmx'] = $row['backupmx'];
+            $domaindata['backupmx_int'] = $row['backupmx_int'];
+            $domaindata['active'] = $row['active'];
+            $domaindata['active_int'] = $row['active_int'];
+            $domaindata['relay_all_recipients'] = $row['relay_all_recipients'];
+            $domaindata['relay_all_recipients_int'] = $row['relay_all_recipients_int'];
+            $stmt = $pdo->prepare("SELECT COUNT(*) AS `alias_count` FROM `alias`
+              WHERE (`domain`= :domain OR `domain` IN (SELECT `alias_domain` FROM `alias_domain` WHERE `target_domain` = :domain2))
+                AND `address` NOT IN (
+                  SELECT `username` FROM `mailbox`
+                )");
+            $stmt->execute(array(
+              ':domain' => $_data,
+              ':domain2' => $_data
+            ));
+            $AliasDataDomain = $stmt->fetch(PDO::FETCH_ASSOC);
+            (isset($AliasDataDomain['alias_count'])) ? $domaindata['aliases_in_domain'] = $AliasDataDomain['alias_count'] : $domaindata['aliases_in_domain'] = "0";
+            $domaindata['aliases_left'] = $row['aliases']	- $AliasDataDomain['alias_count'];
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          return $domaindata;
+        break;
+        case 'mailbox_details':
+          if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            return false;
+          }
+          $mailboxdata = array();
+          try {
+            $stmt = $pdo->prepare("SELECT
+                `domain`.`backupmx`,
+                `mailbox`.`username`,
+                `mailbox`.`name`,
+                `mailbox`.`active` AS `active_int`,
+                CASE `mailbox`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
+                `mailbox`.`domain`,
+                `mailbox`.`quota`,
+                `quota2`.`bytes`,
+                `quota2`.`messages`
+                  FROM `mailbox`, `quota2`, `domain`
+                    WHERE `mailbox`.`kind` NOT REGEXP 'location|thing|group' AND `mailbox`.`username` = `quota2`.`username` AND `domain`.`domain` = `mailbox`.`domain` AND `mailbox`.`username` = :mailbox");
+            $stmt->execute(array(
+              ':mailbox' => $_data,
+            ));
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            $stmt = $pdo->prepare("SELECT `maxquota`, `quota` FROM  `domain` WHERE `domain` = :domain");
+            $stmt->execute(array(':domain' => $row['domain']));
+            $DomainQuota  = $stmt->fetch(PDO::FETCH_ASSOC);
+            $stmt = $pdo->prepare("SELECT COALESCE(SUM(`quota`), 0) as `in_use` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` = :domain AND `username` != :username");
+            $stmt->execute(array(':domain' => $row['domain'], ':username' => $_data));
+            $MailboxUsage	= $stmt->fetch(PDO::FETCH_ASSOC);
+            $stmt = $pdo->prepare("SELECT IFNULL(COUNT(`address`), 0) AS `sa_count` FROM `spamalias` WHERE `goto` = :address AND `validity` >= :unixnow");
+            $stmt->execute(array(':address' => $_data, ':unixnow' => time()));
+            $SpamaliasUsage	= $stmt->fetch(PDO::FETCH_ASSOC);
+            $mailboxdata['max_new_quota'] = ($DomainQuota['quota'] * 1048576) - $MailboxUsage['in_use'];
+            if ($mailboxdata['max_new_quota'] > ($DomainQuota['maxquota'] * 1048576)) {
+              $mailboxdata['max_new_quota'] = ($DomainQuota['maxquota'] * 1048576);
+            }
+            $mailboxdata['username'] = $row['username'];
+            $mailboxdata['is_relayed'] = $row['backupmx'];
+            $mailboxdata['name'] = $row['name'];
+            $mailboxdata['active'] = $row['active'];
+            $mailboxdata['active_int'] = $row['active_int'];
+            $mailboxdata['domain'] = $row['domain'];
+            $mailboxdata['quota'] = $row['quota'];
+            $mailboxdata['quota_used'] = intval($row['bytes']);
+            $mailboxdata['percent_in_use'] = round((intval($row['bytes']) / intval($row['quota'])) * 100);
+            $mailboxdata['messages'] = $row['messages'];
+            $mailboxdata['spam_aliases'] = $SpamaliasUsage['sa_count'];
+            if ($mailboxdata['percent_in_use'] >= 90) {
+              $mailboxdata['percent_class'] = "danger";
+            }
+            elseif ($mailboxdata['percent_in_use'] >= 75) {
+              $mailboxdata['percent_class'] = "warning";
+            }
+            else {
+              $mailboxdata['percent_class'] = "success";
+            }
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          return $mailboxdata;
+        break;
+        case 'resource_details':
+          $resourcedata = array();
+          if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT
+                `username`,
+                `name`,
+                `kind`,
+                `multiple_bookings` AS `multiple_bookings_int`,
+                `local_part`,
+                `active` AS `active_int`,
+                CASE `multiple_bookings` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `multiple_bookings`,
+                CASE `active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`,
+                `domain`
+                  FROM `mailbox` WHERE `kind` REGEXP 'location|thing|group' AND `username` = :resource");
+            $stmt->execute(array(
+              ':resource' => $_data,
+            ));
+            $row = $stmt->fetch(PDO::FETCH_ASSOC);
+            $resourcedata['name'] = $row['username'];
+            $resourcedata['kind'] = $row['kind'];
+            $resourcedata['multiple_bookings'] = $row['multiple_bookings'];
+            $resourcedata['multiple_bookings_int'] = $row['multiple_bookings_int'];
+            $resourcedata['description'] = $row['name'];
+            $resourcedata['active'] = $row['active'];
+            $resourcedata['active_int'] = $row['active_int'];
+            $resourcedata['domain'] = $row['domain'];
+            $resourcedata['local_part'] = $row['local_part'];
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          if (!isset($resourcedata['domain']) ||
+            (isset($resourcedata['domain']) && !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $resourcedata['domain']))) {
+            return false;
+          }
+          return $resourcedata;
+        break;
+      }
+    break;
+    case 'delete':
+      switch ($_type) {
+        case 'syncjob':
+          if (!is_array($_data['id'])) {
+            $ids = array();
+            $ids[] = $_data['id'];
+          }
+          else {
+            $ids = $_data['id'];
+          }
+          foreach ($ids as $id) {
+            if (!is_numeric($id)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => $id
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("SELECT `user2` FROM `imapsync` WHERE id = :id");
+              $stmt->execute(array(':id' => $id));
+              $user2 = $stmt->fetch(PDO::FETCH_ASSOC)['user2'];
+              if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $user2)) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['access_denied'])
+                );
+                return false;
+              }
+              $stmt = $pdo->prepare("DELETE FROM `imapsync` WHERE `id`= :id");
+              $stmt->execute(array(':id' => $id));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => 'Deleted syncjob id/s ' . implode(', ', $ids)
+          );
+          return true;
+        break;
+        case 'time_limited_alias':
+          if (!is_array($_data['address'])) {
+            $addresses = array();
+            $addresses[] = $_data['address'];
+          }
+          else {
+            $addresses = $_data['address'];
+          }
+          foreach ($addresses as $address) {
+            try {
+              $stmt = $pdo->prepare("SELECT `goto` FROM `spamalias` WHERE `address` = :address");
+              $stmt->execute(array(':address' => $address));
+              $goto = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $goto)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `goto` = :username AND `address` = :item");
+              $stmt->execute(array(
+                ':username' => $goto,
+                ':item' => $address
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }	
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_modified'], htmlspecialchars($usernames))
+          );
+        break;
+        case 'eas_cache':
+          if (!is_array($_data['username'])) {
+            $usernames = array();
+            $usernames[] = $_data['username'];
+          }
+          else {
+            $usernames = $_data['username'];
+          }
+          foreach ($usernames as $username) {
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("DELETE FROM `sogo_cache_folder` WHERE `c_uid` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['eas_reset'], htmlspecialchars(implode(', ', $usernames)))
+          );
+        break;
+        case 'domain':
+          if (!is_array($_data['domain'])) {
+            $domains = array();
+            $domains[] = $_data['domain'];
+          }
+          else {
+            $domains = $_data['domain'];
+          }
+          if ($_SESSION['mailcow_cc_role'] != "admin") {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          foreach ($domains as $domain) {
+            if (!is_valid_domain_name($domain)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['domain_invalid'])
+              );
+              return false;
+            }
+            $domain	= idn_to_ascii(strtolower(trim($domain)));
+            try {
+              $stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
+                WHERE `domain` = :domain");
+              $stmt->execute(array(':domain' => $domain));
+              $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            }
+            catch(PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+            if ($num_results != 0 || !empty($num_results)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['domain_not_empty'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("DELETE FROM `domain` WHERE `domain` = :domain");
+              $stmt->execute(array(
+                ':domain' => $domain,
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `domain` = :domain");
+              $stmt->execute(array(
+                ':domain' => $domain,
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `alias` WHERE `domain` = :domain");
+              $stmt->execute(array(
+                ':domain' => $domain,
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `alias_domain` WHERE `target_domain` = :domain");
+              $stmt->execute(array(
+                ':domain' => $domain,
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `mailbox` WHERE `domain` = :domain");
+              $stmt->execute(array(
+                ':domain' => $domain,
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` LIKE :domain");
+              $stmt->execute(array(
+                ':domain' => '%@'.$domain,
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `quota2` WHERE `username` = :domain");
+              $stmt->execute(array(
+                ':domain' => '%@'.$domain,
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `address` = :domain");
+              $stmt->execute(array(
+                ':domain' => '%@'.$domain,
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :domain");
+              $stmt->execute(array(
+                ':domain' => '%@'.$domain,
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+            try {
+              $redis->hDel('DOMAIN_MAP', $domain);
+            }
+            catch (RedisException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'Redis: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['domain_removed'], htmlspecialchars(implode(', ', $domains)))
+          );
+          return true;
+        break;
+        case 'alias':
+          if (!is_array($_data['address'])) {
+            $addresses = array();
+            $addresses[] = $_data['address'];
+          }
+          else {
+            $addresses = $_data['address'];
+          }
+          foreach ($addresses as $address) {
+            $local_part		= strstr($address, '@', true);
+            $domain = mailbox('get', 'alias_details', $address)['domain'];
+            try {
+              $stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :address");
+              $stmt->execute(array(':address' => $address));
+              $gotos = $stmt->fetch(PDO::FETCH_ASSOC);
+            }
+            catch(PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+            $goto_array = explode(',', $gotos['goto']);
+            if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("DELETE FROM `alias` WHERE `address` = :address AND `address` NOT IN (SELECT `username` FROM `mailbox`)");
+              $stmt->execute(array(
+                ':address' => $address
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['alias_removed'], htmlspecialchars(implode(', ', $addresses)))
+          );
+        break;
+        case 'alias_domain':
+          if (!is_array($_data['alias_domain'])) {
+            $alias_domains = array();
+            $alias_domains[] = $_data['alias_domain'];
+          }
+          else {
+            $alias_domains = $_data['alias_domain'];
+          }
+          foreach ($alias_domains as $alias_domain) {
+            if (!is_valid_domain_name($alias_domain)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['domain_invalid'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain`
+                WHERE `alias_domain`= :alias_domain");
+              $stmt->execute(array(':alias_domain' => $alias_domain));
+              $DomainData = $stmt->fetch(PDO::FETCH_ASSOC);
+            }
+            catch(PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+            if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $DomainData['target_domain'])) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("DELETE FROM `alias_domain` WHERE `alias_domain` = :alias_domain");
+              $stmt->execute(array(
+                ':alias_domain' => $alias_domain,
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `alias` WHERE `domain` = :alias_domain");
+              $stmt->execute(array(
+                ':alias_domain' => $alias_domain,
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['alias_domain_removed'], htmlspecialchars(implode(', ', $alias_domains)))
+          );
+        break;
+        case 'mailbox':
+          if (!is_array($_data['username'])) {
+            $usernames = array();
+            $usernames[] = $_data['username'];
+          }
+          else {
+            $usernames = $_data['username'];
+          }
+          foreach ($usernames as $username) {
+            if (!filter_var($username, FILTER_VALIDATE_EMAIL)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("DELETE FROM `alias` WHERE `goto` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `quota2` WHERE `username` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `mailbox` WHERE `username` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sender_acl` WHERE `logged_in_as` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `spamalias` WHERE `goto` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `imapsync` WHERE `user2` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_user_profile` WHERE `c_uid` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_cache_folder` WHERE `c_uid` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $username . "/%' OR `c_uid` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_quick_contact` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_quick_appointment` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_folder_info` WHERE `c_path2` = :username");
+              $stmt->execute(array(
+                ':username' => $username
+              ));
+              $stmt = $pdo->prepare("SELECT `address`, `goto` FROM `alias`
+                  WHERE `goto` REGEXP :username");
+              $stmt->execute(array(':username' => '(^|,)'.$username.'($|,)'));
+              $GotoData = $stmt->fetchAll(PDO::FETCH_ASSOC);
+              foreach ($GotoData as $gotos) {
+                $goto_exploded = explode(',', $gotos['goto']);
+                if (($key = array_search($username, $goto_exploded)) !== false) {
+                  unset($goto_exploded[$key]);
+                }
+                $gotos_rebuild = implode(',', $goto_exploded);
+                $stmt = $pdo->prepare("UPDATE `alias` SET
+                  `goto` = :goto
+                    WHERE `address` = :address");
+                $stmt->execute(array(
+                  ':goto' => $gotos_rebuild,
+                  ':address' => $gotos['address']
+                ));
+              }
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_removed'], htmlspecialchars(implode(', ', $usernames)))
+          );
+        break;
+        case 'resource':
+          if (!is_array($_data['name'])) {
+            $names = array();
+            $names[] = $_data['name'];
+          }
+          else {
+            $names = $_data['name'];
+          }
+          foreach ($names as $name) {
+            if (!filter_var($name, FILTER_VALIDATE_EMAIL)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $name)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("DELETE FROM `mailbox` WHERE `username` = :username");
+              $stmt->execute(array(
+                ':username' => $name
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_user_profile` WHERE `c_uid` = :username");
+              $stmt->execute(array(
+                ':username' => $name
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_cache_folder` WHERE `c_uid` = :username");
+              $stmt->execute(array(
+                ':username' => $name
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $name . "/%' OR `c_uid` = :username");
+              $stmt->execute(array(
+                ':username' => $name
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+              $stmt->execute(array(
+                ':username' => $name
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_quick_contact` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+              $stmt->execute(array(
+                ':username' => $name
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_quick_appointment` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
+              $stmt->execute(array(
+                ':username' => $name
+              ));
+              $stmt = $pdo->prepare("DELETE FROM `sogo_folder_info` WHERE `c_path2` = :username");
+              $stmt->execute(array(
+                ':username' => $name
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['resource_removed'], htmlspecialchars(implode(', ', $names)))
+          );
+        break;
+      }
+    break;
+  }
+}
\ No newline at end of file
diff --git a/data/web/inc/functions.policy.inc.php b/data/web/inc/functions.policy.inc.php
new file mode 100644
index 00000000..01ef13ab
--- /dev/null
+++ b/data/web/inc/functions.policy.inc.php
@@ -0,0 +1,354 @@
+<?php
+
+function policy($_action, $_scope, $_data = null) {
+	global $pdo;
+	global $redis;
+	global $lang;
+  switch ($_action) {
+    case 'add':
+      switch ($_scope) {
+        case 'domain':
+          $object = $_data['domain'];
+          if (is_valid_domain_name($object)) {
+            if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            $object = idn_to_ascii(strtolower(trim($object)));
+          }
+          else {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          if ($_data['object_list'] == "bl") {
+            $object_list = "blacklist_from";
+          }
+          elseif ($_data['object_list'] == "wl") {
+            $object_list = "whitelist_from";
+          }
+          $object_from = preg_replace('/\.+/', '.', rtrim(preg_replace("/\.\*/", "*", trim(strtolower($_data['object_from']))), '.'));
+          if (!ctype_alnum(str_replace(array('@', '.', '-', '*'), '', $object_from))) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['policy_list_from_invalid'])
+            );
+            return false;
+          }
+          if ($object_list != "blacklist_from" && $object_list != "whitelist_from") {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `object` FROM `filterconf`
+              WHERE (`option` = 'whitelist_from'  OR `option` = 'blacklist_from')
+                AND `object` = :object
+                AND `value` = :object_from");
+            $stmt->execute(array(':object' => $object, ':object_from' => $object_from));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results != 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['policy_list_from_exists'])
+              );
+              return false;
+            }
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option` ,`value`)
+              VALUES (:object, :object_list, :object_from)");
+            $stmt->execute(array(
+              ':object' => $object,
+              ':object_list' => $object_list,
+              ':object_from' => $object_from
+            ));
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['domain_modified'], $object)
+          );
+        break;
+        case 'mailbox':
+          $object = $_data['username'];
+          if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          if ($_data['object_list'] == "bl") {
+            $object_list = "blacklist_from";
+          }
+          elseif ($_data['object_list'] == "wl") {
+            $object_list = "whitelist_from";
+          }
+          $object_from = preg_replace('/\.+/', '.', rtrim(preg_replace("/\.\*/", "*", trim(strtolower($_data['object_from']))), '.'));
+          if (!ctype_alnum(str_replace(array('@', '.', '-', '*'), '', $object_from))) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['policy_list_from_invalid'])
+            );
+            return false;
+          }
+          if ($object_list != "blacklist_from" && $object_list != "whitelist_from") {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("SELECT `object` FROM `filterconf`
+              WHERE (`option` = 'whitelist_from'  OR `option` = 'blacklist_from')
+                AND `object` = :object
+                AND `value` = :object_from");
+            $stmt->execute(array(':object' => $object, ':object_from' => $object_from));
+            $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+            if ($num_results != 0) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['policy_list_from_exists'])
+              );
+              return false;
+            }
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option` ,`value`)
+              VALUES (:object, :object_list, :object_from)");
+            $stmt->execute(array(
+              ':object' => $object,
+              ':object_list' => $object_list,
+              ':object_from' => $object_from
+            ));
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['mailbox_modified'], $object)
+          );
+        break;
+      }
+    break;
+    case 'delete':
+      switch ($_scope) {
+        case 'domain':
+          if (!is_array($_data['prefid'])) {
+            $prefids = array();
+            $prefids[] = $_data['prefid'];
+          }
+          else {
+            $prefids = $_data['prefid'];
+          }
+          foreach ($prefids as $prefid) {
+            if (!is_numeric($prefid)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("SELECT `object` FROM `filterconf` WHERE `prefid` = :prefid");
+              $stmt->execute(array(':prefid' => $prefid));
+              $object = $stmt->fetch(PDO::FETCH_ASSOC)['object'];
+            }
+            catch(PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+            }
+            if (is_valid_domain_name($object)) {
+              if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['access_denied'])
+                );
+                return false;
+              }
+              $object = idn_to_ascii(strtolower(trim($object)));
+            }
+            else {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :object AND `prefid` = :prefid");
+              $stmt->execute(array(
+                ':object' => $object,
+                ':prefid' => $prefid
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['items_deleted'], implode(', ', $prefids))
+          );
+        break;
+        case 'mailbox':
+          if (!is_array($_data['prefid'])) {
+            $prefids = array();
+            $prefids[] = $_data['prefid'];
+          }
+          else {
+            $prefids = $_data['prefid'];
+          }
+          foreach ($prefids as $prefid) {
+            if (!is_numeric($prefid)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("SELECT `object` FROM `filterconf` WHERE `prefid` = :prefid");
+              $stmt->execute(array(':prefid' => $prefid));
+              $object = $stmt->fetch(PDO::FETCH_ASSOC)['object'];
+            }
+            catch(PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+            }
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            try {
+              $stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :object AND `prefid` = :prefid");
+              $stmt->execute(array(
+                ':object' => $object,
+                ':prefid' => $prefid
+              ));
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['items_deleted'], implode(', ', $prefids))
+          );
+        break;
+      }
+    break;
+    case 'get':
+      switch ($_scope) {
+        case 'domain':
+          if (!is_valid_domain_name($_data)) {
+            return false;
+          }
+          else {
+            if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+              return false;
+            }
+            $_data = idn_to_ascii(strtolower(trim($_data)));
+          }
+          try {
+            // WHITELIST
+            $stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='whitelist_from' AND (`object` LIKE :object_mail OR `object` = :object_domain)");
+            $stmt->execute(array(':object_mail' => '%@' . $_data, ':object_domain' => $_data));
+            $rows['whitelist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            // BLACKLIST
+            $stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='blacklist_from' AND (`object` LIKE :object_mail OR `object` = :object_domain)");
+            $stmt->execute(array(':object_mail' => '%@' . $_data, ':object_domain' => $_data));
+            $rows['blacklist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+          }
+          return $rows;
+        break;
+        case 'mailbox':
+          if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
+            if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+              return false;
+            }
+          }
+          else {
+            $_data = $_SESSION['mailcow_cc_username'];
+          }
+          $domain = mailbox('get', 'mailbox_details', $_data)['domain'];
+          if (empty($domain)) {
+            return false;
+          }
+          try {
+            // WHITELIST
+            $stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='whitelist_from' AND (`object` = :username OR `object` = :domain)");
+            $stmt->execute(array(':username' => $_data, ':domain' => $domain));
+            $rows['whitelist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
+            // BLACKLIST
+            $stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='blacklist_from' AND (`object` = :username OR `object` = :domain)");
+            $stmt->execute(array(':username' => $_data, ':domain' => $domain));
+            $rows['blacklist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
+          }
+          catch(PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+          }
+          return $rows;
+        break;
+      }
+    break;
+  }
+}
\ No newline at end of file
diff --git a/data/web/inc/header.inc.php b/data/web/inc/header.inc.php
index 170335e3..7845d75b 100644
--- a/data/web/inc/header.inc.php
+++ b/data/web/inc/header.inc.php
@@ -22,6 +22,7 @@
 <?= (preg_match("/mailbox.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/mailbox.css">' : null; ?>
 <?= (preg_match("/admin.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/admin.css">' : null; ?>
 <?= (preg_match("/user.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/user.css">' : null; ?>
+<?= (preg_match("/edit.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/edit.css">' : null; ?>
 <link rel="shortcut icon" href="/favicon.png" type="image/png">
 <link rel="icon" href="/favicon.png" type="image/png">
 </head>
diff --git a/data/web/inc/prerequisites.inc.php b/data/web/inc/prerequisites.inc.php
index 57ff479a..867bc106 100644
--- a/data/web/inc/prerequisites.inc.php
+++ b/data/web/inc/prerequisites.inc.php
@@ -42,28 +42,24 @@ try {
 }
 catch (PDOException $e) {
 ?>
-<center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>?? Connection failed, database may be in warm-up state, please try again later.<br /><br />The following error was reported:<br/>  <?=$e->getMessage();?></center>
+<center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>Connection failed, database may be in warm-up state, please try again later.<br /><br />The following error was reported:<br/>  <?=$e->getMessage();?></center>
 <?php
 exit;
 }
 
 // Set language
-$_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG));
-
+if (!isset($_SESSION['mailcow_locale'])) {
+  $_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG));
+}
 if (isset($_GET['lang']) && in_array($_GET['lang'], $AVAILABLE_LANGUAGES)) {
   $_SESSION['mailcow_locale'] = $_GET['lang'];
 }
-elseif (isset($_COOKIE['language']) && in_array($_COOKIE['language'], $AVAILABLE_LANGUAGES)) {
-  $_SESSION['mailcow_locale'] = $_COOKIE['language'];
-}
-if (isset($_SESSION['mailcow_locale']) && !file_exists($_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.php')) {
-  $_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG));
-}
-setcookie('language', $_SESSION['mailcow_locale']);
 
 require_once $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en.php';
 include $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.php';
 require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
+require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php';
+require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.policy.inc.php';
 require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/init_db.inc.php';
 require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.inc.php';
 init_db_schema();
diff --git a/data/web/inc/triggers.inc.php b/data/web/inc/triggers.inc.php
index 5d8e7b7d..43e65484 100644
--- a/data/web/inc/triggers.inc.php
+++ b/data/web/inc/triggers.inc.php
@@ -44,10 +44,7 @@ if (isset($_POST["login_user"]) && isset($_POST["pass_user"])) {
 if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
 	if (isset($_GET["duallogin"])) {
     if (filter_var($_GET["duallogin"], FILTER_VALIDATE_EMAIL)) {
-      $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :duallogin");
-      $stmt->execute(array(':duallogin' => $_GET["duallogin"]));
-      $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-      if ($num_results != 0) {
+      if (!empty(mailbox('get', 'mailbox_details', $_GET["duallogin"]))) {
         $_SESSION["dual-login"]["username"] = $_SESSION['mailcow_cc_username'];
         $_SESSION["dual-login"]["role"]     = $_SESSION['mailcow_cc_role'];
         $_SESSION['mailcow_cc_username']    = $_GET["duallogin"];
@@ -83,35 +80,14 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "user
 	if (isset($_POST["edit_user_account"])) {
 		edit_user_account($_POST);
 	}
-	if (isset($_POST["mailbox_reset_eas"])) {
-		mailbox_reset_eas($_POST);
-	}
-	if (isset($_POST["edit_spam_score"])) {
-		edit_spam_score($_POST);
-	}
-	if (isset($_POST["edit_delimiter_action"])) {
-		edit_delimiter_action($_POST);
-	}
 	if (isset($_POST["add_policy_list_item"])) {
-		add_policy_list_item($_POST);
-	}
-	if (isset($_POST["delete_policy_list_item"])) {
-		delete_policy_list_item($_POST);
-	}
-	if (isset($_POST["edit_tls_policy"])) {
-		edit_tls_policy($_POST);
+		policy('add', 'mailbox', $_POST);
 	}
 	if (isset($_POST["add_syncjob"])) {
-		add_syncjob($_POST);
+		mailbox('add', 'syncjob', $_POST);
 	}
 	if (isset($_POST["edit_syncjob"])) {
-		edit_syncjob($_POST);
-	}
-	if (isset($_POST["delete_syncjob"])) {
-		delete_syncjob($_POST);
-	}
-	if (isset($_POST["set_time_limited_aliases"])) {
-		set_time_limited_aliases($_POST);
+		mailbox('edit', 'syncjob', $_POST);
 	}
 }
 if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
@@ -125,55 +101,52 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 		unset_tfa_key($_POST);
 	}
 	if (isset($_POST["add_policy_list_item"])) {
-		add_policy_list_item($_POST);
-	}
-	if (isset($_POST["delete_policy_list_item"])) {
-		delete_policy_list_item($_POST);
+		policy('add', 'domain', $_POST);
 	}
 	if (isset($_POST["mailbox_add_domain"])) {
-		mailbox_add_domain($_POST);
+		mailbox('add', 'domain', $_POST);
 	}
 	if (isset($_POST["mailbox_add_alias"])) {
-		mailbox_add_alias($_POST);
+		mailbox('add', 'alias', $_POST);
 	}
 	if (isset($_POST["mailbox_add_alias_domain"])) {
-		mailbox_add_alias_domain($_POST);
+		mailbox('add', 'alias_domain', $_POST);
 	}
 	if (isset($_POST["mailbox_add_mailbox"])) {
-		mailbox_add_mailbox($_POST);
+		mailbox('add', 'mailbox', $_POST);
 	}
 	if (isset($_POST["mailbox_add_resource"])) {
-		mailbox_add_resource($_POST);
+		mailbox('add', 'resource', $_POST);
 	}
 	if (isset($_POST["mailbox_edit_alias"])) {
-		mailbox_edit_alias($_POST);
+		mailbox('edit', 'alias', $_POST);
 	}
 	if (isset($_POST["mailbox_edit_domain"])) {
-		mailbox_edit_domain($_POST);
+		mailbox('edit', 'domain', $_POST);
 	}
 	if (isset($_POST["mailbox_edit_mailbox"])) {
-		mailbox_edit_mailbox($_POST);
+		mailbox('edit', 'mailbox', $_POST);
 	}
 	if (isset($_POST["mailbox_edit_alias_domain"])) {
-		mailbox_edit_alias_domain($_POST);
+		mailbox('edit', 'alias_domain', $_POST);
 	}
 	if (isset($_POST["mailbox_edit_resource"])) {
-		mailbox_edit_resource($_POST);
+		mailbox('edit', 'resource', $_POST);
 	}
 	if (isset($_POST["mailbox_delete_domain"])) {
-		mailbox_delete_domain($_POST);
+		mailbox('delete', 'domain', $_POST);
 	}
 	if (isset($_POST["mailbox_delete_alias"])) {
-		mailbox_delete_alias($_POST);
+		mailbox('delete', 'delete_alias', $_POST);
 	}
 	if (isset($_POST["mailbox_delete_alias_domain"])) {
-		mailbox_delete_alias_domain($_POST);
+		mailbox('delete', 'alias_domain', $_POST);
 	}
 	if (isset($_POST["mailbox_delete_mailbox"])) {
-		mailbox_delete_mailbox($_POST);
+		mailbox('delete', 'mailbox', $_POST);
 	}
 	if (isset($_POST["mailbox_delete_resource"])) {
-		mailbox_delete_resource($_POST);
+		mailbox('delete', 'resource', $_POST);
 	}
 }
 ?>
diff --git a/data/web/js/admin.js b/data/web/js/admin.js
index 9d6642b9..810653ee 100644
--- a/data/web/js/admin.js
+++ b/data/web/js/admin.js
@@ -1,76 +1,20 @@
-$(document).ready(function() {
-  // Collect values of input fields with name multi_select with same data-id to js array multi_data[data-id]
-  var multi_data = [];
-  $(document).on('change', 'input[name=multi_select]:checkbox', function() {
-    if ($(this).is(':checked') && $(this).data('id')) {
-      var id = $(this).data('id');
-      if (typeof multi_data[id] == "undefined") {
-        multi_data[id] = [];
-      }
-      multi_data[id].push($(this).val());
-    }
-    else {
-      var id = $(this).data('id');
-      multi_data[id].splice($.inArray($(this).val(), multi_data[id]),1);
-    }
-  });
-  // Select checkbox by click on parent tr
-  $(document).on('click', 'tbody>tr', function(e) {
-    if (e.target.type == "checkbox") {
-      e.stopPropagation();
-    } else {
-      var checkbox = $(this).find(':checkbox');
-      checkbox.trigger('click');
-    }
-  });
-  // Select or deselect all checkboxes with same data-id
-  $(document).on('click', '#toggle_multi_select_all', function(e) {
-    e.preventDefault();
-    id = $(this).data("id");
-    multi_data[id] = [];
-    var all_checkboxes = $("input[data-id=" + id + "]:enabled");
-    all_checkboxes.prop("checked", !all_checkboxes.prop("checked")).change();
-  });
-  // General API edit function
-  $(document).on('click', '#delete_selected', function(e) {
-    e.preventDefault();
-    var id = $(this).data('id');
-    if (typeof multi_data[id] == "undefined" || multi_data[id] == "") return;
-    data_array = multi_data[id];
-    api_url = $(this).data('api-url');
-      $(document).on('show.bs.modal','#ConfirmDeleteModal', function () {
-        $("#ItemsToDelete").empty();
-        for (var i in data_array) {
-          $("#ItemsToDelete").append("<li>" + data_array[i] + "</li>");
-        }
-      })
-      $('#ConfirmDeleteModal').modal({
-        backdrop: 'static',
-        keyboard: false
-      })
-      .one('click', '#IsConfirmed', function(e) {
-        $.ajax({
-          type: "POST",
-          dataType: "json",
-          data: { "items": JSON.stringify(data_array), "csrf_token": csrf_token },
-          url: '/api/v1/' + api_url,
-          jsonp: false,
-          complete: function (data) {
-            location.reload(true);
-          }
-        });
-      })
-      .one('click', '#isCanceled', function(e) {
-        $('#ConfirmDeleteModal').modal('hide');
-      });;
-  });
-
-});
 jQuery(function($){
   function unix_time_format(tm) {
     var date = new Date(tm ? tm * 1000 : 0);
     return date.toLocaleString();
   }
+  function humanFileSize(bytes) {
+    if(Math.abs(bytes) < 1024) {
+        return bytes + ' B';
+    }
+    var units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
+    var u = -1;
+    do {
+        bytes /= 1024;
+        ++u;
+    } while(Math.abs(bytes) >= 1024 && u < units.length - 1);
+    return bytes.toFixed(1)+' '+units[u];
+  }
   $("#refresh_postfix_log").on('click', function(e) {
     e.preventDefault();
     draw_postfix_logs();
@@ -83,6 +27,10 @@ jQuery(function($){
     e.preventDefault();
     draw_sogo_logs();
   });
+  $("#refresh_rspamd_history").on('click', function(e) {
+    e.preventDefault();
+    draw_rspamd_history();
+  });
   function draw_postfix_logs() {
     ft_postfix_logs = FooTable.init('#postfix_log', {
       "columns": [
@@ -307,10 +255,185 @@ jQuery(function($){
       }
     });
   }
-
+  function draw_rspamd_history() {
+    ft_postfix_logs = FooTable.init('#rspamd_history', {
+      "columns": [{
+        "name": "message-id",
+        "title": "ID",
+        "breakpoints": "all",
+        "style": {
+          "minWidth": 130,
+          "overflow": "hidden",
+          "textOverflow": "ellipsis",
+          "wordBreak": "break-all",
+          "whiteSpace": "normal"
+        }
+        }, {
+          "name": "ip",
+          "title": "IP address",
+          "breakpoints": "all",
+          "style": {
+            "minWidth": 88
+          }
+        }, {
+          "name": "sender_mime",
+          "title": "From",
+          "breakpoints": "xs sm md",
+          "style": {
+            "minWidth": 100
+          }
+        }, {
+          "name": "rcpt_mime",
+          "title": "To",
+          "breakpoints": "xs sm md",
+          "style": {
+            "minWidth": 100
+          }
+        }, {
+          "name": "subject",
+          "title": "Subject",
+          "breakpoints": "all",
+          "style": {
+            "word-break": "break-all",
+            "minWidth": 150
+          }
+        }, {
+          "name": "action",
+          "title": "Action",
+          "style": {
+            "minwidth": 82
+          }
+        }, {
+          "name": "score",
+          "title": "Score",
+          "style": {
+            "maxWidth": 110
+          },
+        }, {
+          "name": "symbols",
+          "title": "Symbols",
+          "breakpoints": "all",
+        }, {
+          "name": "size",
+          "title": "Msg size",
+          "breakpoints": "all",
+          "style": {
+            "minwidth": 50,
+          },
+          "formatter": function(value) { return humanFileSize(value); }
+        }, {
+          "name": "scan_time",
+          "title": "Scan time",
+          "breakpoints": "all",
+          "style": {
+            "maxWidth": 72
+          },
+        }, {
+          "sorted": true,
+          "breakpoints": "all",
+          "direction": "DESC",
+          "name": "time",
+          "title": "Time",
+        }, {
+          "name": "user",
+          "title": "Authenticated user",
+          "breakpoints": "xs sm md",
+          "style": {
+            "minWidth": 100
+          }
+        }],
+      "rows": $.ajax({
+        dataType: 'json',
+        url: '/api/v1/get/logs/rspamd-history',
+        jsonp: false,
+        error: function () {
+          console.log('Cannot draw rspamd history table');
+        },
+        success: function (data) {
+          $.each(data, function (i, item) {
+            item.rcpt_mime = item.rcpt_mime.join(",&#8203;");
+            Object.keys(item.symbols).map(function(key) {
+              var sym = item.symbols[key];
+              if (sym.score <= 0) {
+                sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)'
+              }
+              else {
+                sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)'
+              }
+              var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
+              if (sym.options) {
+                str += ' [' + sym.options.join(",") + "]";
+              }
+              item.symbols[key].str = str;
+            });
+            item.symbols = Object.keys(item.symbols).
+            map(function(key) {
+              return item.symbols[key];
+            }).sort(function(e1, e2) {
+              return Math.abs(e1.score) < Math.abs(e2.score);
+            }).map(function(e) {
+              return e.str;
+            }).join("<br>\n");
+            item.time = {
+              "value": unix_time_format(item.unix_time),
+              "options": {
+                "sortValue": item.unix_time
+              }
+            };
+            var scan_time = item.time_real.toFixed(3) + ' / ' + item.time_virtual.toFixed(3);
+            item.scan_time = {
+              "options": {
+                "sortValue": item.time_real
+              },
+              "value": scan_time
+            };
+            if (item.action === 'clean' || item.action === 'no action') {
+              item.action = "<div class='label label-success'>" + item.action + "</div>";
+            } else if (item.action === 'rewrite subject' || item.action === 'add header' || item.action === 'probable spam') {
+              item.action = "<div class='label label-warning'>" + item.action + "</div>";
+            } else if (item.action === 'spam' || item.action === 'reject') {
+              item.action = "<div class='label label-danger'>" + item.action + "</div>";
+            } else {
+              item.action = "<div class='label label-info'>" + item.action + "</div>";
+            }
+            var score_content;
+            if (item.score < item.required_score) {
+              score_content = "[ <span class='text-success'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
+            } else {
+              score_content = "[ <span class='text-danger'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
+            }
+            item.score = {
+              "options": {
+                "sortValue": item.score
+              },
+              "value": score_content
+            };
+            if (item.user == null) {
+              item.user = "none";
+            }
+          });
+        }
+      }),
+      "empty": lang.empty,
+      "paging": {
+        "enabled": true,
+        "limit": 5,
+        "size": pagination_size
+      },
+      "filtering": {
+        "enabled": true,
+        "position": "left",
+        "placeholder": lang.filter_table
+      },
+      "sorting": {
+        "enabled": true
+      }
+    });
+  }
   draw_postfix_logs();
   draw_dovecot_logs();
   draw_sogo_logs();
   draw_domain_admins();
   draw_fwd_hosts();
+  draw_rspamd_history();
 });
\ No newline at end of file
diff --git a/data/web/js/api.js b/data/web/js/api.js
new file mode 100644
index 00000000..0343df43
--- /dev/null
+++ b/data/web/js/api.js
@@ -0,0 +1,154 @@
+$(document).ready(function() {
+  // Collect values of input fields with name "multi_select" an same data-id to js array multi_data[data-id]
+  var multi_data = [];
+  $(document).on('change', 'input[name=multi_select]:checkbox', function() {
+    if ($(this).is(':checked') && $(this).data('id')) {
+      var id = $(this).data('id');
+      if (typeof multi_data[id] == "undefined") {
+        multi_data[id] = [];
+      }
+      multi_data[id].push($(this).val());
+    }
+    else {
+      var id = $(this).data('id');
+      multi_data[id].splice($.inArray($(this).val(), multi_data[id]),1);
+    }
+  });
+
+  // Select checkbox by click on parent tr
+  $(document).on('click', 'tbody>tr', function(e) {
+    if (e.target.type == "checkbox") {
+      e.stopPropagation();
+    } else {
+      var checkbox = $(this).find(':checkbox');
+      checkbox.trigger('click');
+    }
+  });
+
+  // Select or deselect all checkboxes with same data-id
+  $(document).on('click', '#toggle_multi_select_all', function(e) {
+    e.preventDefault();
+    id = $(this).data("id");
+    multi_data[id] = [];
+    var all_checkboxes = $("input[data-id=" + id + "]:enabled");
+    all_checkboxes.prop("checked", !all_checkboxes.prop("checked")).change();
+  });
+
+  // General API edit actions
+  $(document).on('click', '#edit_selected', function(e) {
+    e.preventDefault();
+    var id = $(this).data('id');
+    var api_url = $(this).data('api-url');
+    var api_attr = $(this).data('api-attr');
+    // If clicked element #edit_selected is in a form with the same data-id as the button,
+    // we merge all input fields by {"name":"value"} into api-attr
+    if ($(this).closest("form").data('id') == id) {
+      var attr_to_merge = {};
+      $.each($(this).closest("form").serializeArray(), function(i, field) {
+          attr_to_merge[field.name] = field.value;
+      });
+      var api_attr = $.extend(api_attr, attr_to_merge)
+    }
+    // If clicked element #edit_selected has data-item attribute, it is added to "items"
+    if (typeof $(this).data('item') !== 'undefined') {
+      var id = $(this).data('id');
+      if (typeof multi_data[id] == "undefined") {
+        multi_data[id] = [];
+      }
+      multi_data[id].push($(this).data('item'));
+    }
+    if (typeof multi_data[id] == "undefined") return;
+    api_items = multi_data[id];
+    if (Object.keys(api_items).length !== 0) {
+      $.ajax({
+        type: "POST",
+        dataType: "json",
+        data: { "items": JSON.stringify(api_items), "attr": JSON.stringify(api_attr), "csrf_token": csrf_token },
+        url: '/api/v1/' + api_url,
+        jsonp: false,
+        complete: function (data) {
+          // var reponse = (JSON.parse(data.responseText));
+          // console.log(reponse.type);
+          // console.log(reponse.msg);
+          window.location = window.location.href.split("#")[0];
+        }
+      });
+    }
+  });
+
+  // General API add actions
+  $(document).on('click', '#add_item', function(e) {
+    e.preventDefault();
+    var id = $(this).data('id');
+    var api_url = $(this).data('api-url');
+    var api_attr = $(this).data('api-attr');
+    // If clicked button is in a form with the same data-id as the button,
+    // we merge all input fields by {"name":"value"} into api-attr
+    if ($(this).closest("form").data('id') == id) {
+      var attr_to_merge = {};
+      $.each($(this).closest("form").serializeArray(), function(i, field) {
+          attr_to_merge[field.name] = field.value;
+      });
+      var api_attr = $.extend(api_attr, attr_to_merge)
+    }
+    $.ajax({
+      type: "POST",
+      dataType: "json",
+      data: { "attr": JSON.stringify(api_attr), "csrf_token": csrf_token },
+      url: '/api/v1/' + api_url,
+      jsonp: false,
+      complete: function (data) {
+        // var reponse = (JSON.parse(data.responseText));
+        // console.log(reponse.type);
+        // console.log(reponse.msg);
+        window.location = window.location.href.split("#")[0];
+      }
+    });
+  });
+  // General API delete actions
+  $(document).on('click', '#delete_selected', function(e) {
+    e.preventDefault();
+    var id = $(this).data('id');
+    // If clicked element #delete_selected has data-item attribute, it is added to "items"
+    if (typeof $(this).data('item') !== 'undefined') {
+      var id = $(this).data('id');
+      if (typeof multi_data[id] == "undefined") {
+        multi_data[id] = [];
+      }
+      multi_data[id].splice($.inArray($(this).data('item'), multi_data[id]),1);
+      multi_data[id].push($(this).data('item'));
+    }
+    if (typeof $(this).data('text') !== 'undefined') {
+      $("#DeleteText").empty();
+      $("#DeleteText").text($(this).data('text'));
+    }
+    if (typeof multi_data[id] == "undefined" || multi_data[id] == "") return;
+    data_array = multi_data[id];
+    api_url = $(this).data('api-url');
+      $(document).on('show.bs.modal','#ConfirmDeleteModal', function () {
+        $("#ItemsToDelete").empty();
+        for (var i in data_array) {
+          $("#ItemsToDelete").append("<li>" + data_array[i] + "</li>");
+        }
+      })
+      $('#ConfirmDeleteModal').modal({
+        backdrop: 'static',
+        keyboard: false
+      })
+      .one('click', '#IsConfirmed', function(e) {
+        $.ajax({
+          type: "POST",
+          dataType: "json",
+          data: { "items": JSON.stringify(data_array), "csrf_token": csrf_token },
+          url: '/api/v1/' + api_url,
+          jsonp: false,
+          complete: function (data) {
+            window.location = window.location.href.split("#")[0];
+          }
+        });
+      })
+      .one('click', '#isCanceled', function(e) {
+        $('#ConfirmDeleteModal').modal('hide');
+      });;
+  });
+});
\ No newline at end of file
diff --git a/data/web/js/edit.js b/data/web/js/edit.js
new file mode 100644
index 00000000..4b3654b2
--- /dev/null
+++ b/data/web/js/edit.js
@@ -0,0 +1,83 @@
+jQuery(function($){
+  // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
+  function validateEmail(email) {
+    var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+    return re.test(email);
+  }
+  function draw_wl_policy_domain_table() {
+    ft_wl_policy_mailbox_table = FooTable.init('#wl_policy_domain_table', {
+      "columns": [
+        {"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
+        {"name":"prefid","style":{"maxWidth":"40px","width":"40px"},"title":"ID","filterable": false,"sortable": false},
+        {"sorted": true,"name":"value","title":lang.spamfilter_table_rule},
+        {"name":"object","title":"Scope"}
+      ],
+      "empty": lang.empty,
+      "rows": $.ajax({
+        dataType: 'json',
+        url: '/api/v1/get/policy_wl_domain/' + table_for_domain,
+        jsonp: false,
+        error: function () {
+          console.log('Cannot draw mailbox policy wl table');
+        },
+        success: function (data) {
+          $.each(data, function (i, item) {
+            if (!validateEmail(item.object)) {
+              item.chkbox = '<input type="checkbox" data-id="policy_wl_domain" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" disabled title="' + lang.spamfilter_table_domain_policy + '" />';
+            }
+          });
+        }
+      }),
+      "paging": {
+        "enabled": true,
+        "limit": 5,
+        "size": pagination_size
+      },
+      "sorting": {
+        "enabled": true
+      }
+    });
+  }
+  function draw_bl_policy_domain_table() {
+    ft_bl_policy_mailbox_table = FooTable.init('#bl_policy_domain_table', {
+      "columns": [
+        {"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
+        {"name":"prefid","style":{"maxWidth":"40px","width":"40px"},"title":"ID","filterable": false,"sortable": false},
+        {"sorted": true,"name":"value","title":lang.spamfilter_table_rule},
+        {"name":"object","title":"Scope"}
+      ],
+      "empty": lang.empty,
+      "rows": $.ajax({
+        dataType: 'json',
+        url: '/api/v1/get/policy_bl_domain/' + table_for_domain,
+        jsonp: false,
+        error: function () {
+          console.log('Cannot draw mailbox policy bl table');
+        },
+        success: function (data) {
+          $.each(data, function (i, item) {
+            if (!validateEmail(item.object)) {
+              item.chkbox = '<input type="checkbox" data-id="policy_bl_domain" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" disabled tooltip="' + lang.spamfilter_table_domain_policy + '" />';
+            }
+          });
+        }
+      }),
+      "paging": {
+        "enabled": true,
+        "limit": 5,
+        "size": pagination_size
+      },
+      "sorting": {
+        "enabled": true
+      }
+    });
+  }
+  draw_wl_policy_domain_table();
+  draw_bl_policy_domain_table();
+});
\ No newline at end of file
diff --git a/data/web/js/mailbox.js b/data/web/js/mailbox.js
index 100c9235..80d84339 100644
--- a/data/web/js/mailbox.js
+++ b/data/web/js/mailbox.js
@@ -1,95 +1,3 @@
-$(document).ready(function() {
-  // Collect values of input fields with name multi_select with same data-id to js array multi_data[data-id]
-  var multi_data = [];
-  $(document).on('change', 'input[name=multi_select]:checkbox', function() {
-    if ($(this).is(':checked') && $(this).data('id')) {
-      var id = $(this).data('id');
-      if (typeof multi_data[id] == "undefined") {
-        multi_data[id] = [];
-      }
-      multi_data[id].push($(this).val());
-    }
-    else {
-      var id = $(this).data('id');
-      multi_data[id].splice($.inArray($(this).val(), multi_data[id]),1);
-    }
-  });
-  // Select checkbox by click on parent tr
-  $(document).on('click', 'tbody>tr', function(e) {
-    if (e.target.type == "checkbox") {
-      e.stopPropagation();
-    } else {
-      var checkbox = $(this).find(':checkbox');
-      checkbox.trigger('click');
-    }
-  });
-  // Select or deselect all checkboxes with same data-id
-  $(document).on('click', '#toggle_multi_select_all', function(e) {
-    e.preventDefault();
-    id = $(this).data("id");
-    multi_data[id] = [];
-    var all_checkboxes = $("input[data-id=" + id + "]:enabled");
-    all_checkboxes.prop("checked", !all_checkboxes.prop("checked")).change();
-  });
-  // General API edit actions
-  $(document).on('click', '#edit_selected', function(e) {
-    e.preventDefault();
-    var id = $(this).data('id');
-    if (typeof multi_data[id] == "undefined") return;
-    data_array = multi_data[id];
-    api_url = $(this).data('api-url');
-    api_attr = $(this).data('api-attr');
-    if (Object.keys(data_array).length !== 0) {
-      $.ajax({
-        type: "POST",
-        dataType: "json",
-        data: { "items": JSON.stringify(data_array), "attr": JSON.stringify(api_attr), "csrf_token": csrf_token },
-        url: '/api/v1/' + api_url,
-        jsonp: false,
-        complete: function (data) {
-          // var reponse = (JSON.parse(data.responseText));
-          // console.log(reponse.type);
-          // console.log(reponse.msg);
-          location.assign(window.location);
-        }
-      });
-    }
-  });
-  // General API delete actions
-  $(document).on('click', '#delete_selected', function(e) {
-    e.preventDefault();
-    var id = $(this).data('id');
-    if (typeof multi_data[id] == "undefined" || multi_data[id] == "") return;
-    data_array = multi_data[id];
-    api_url = $(this).data('api-url');
-      $(document).on('show.bs.modal','#ConfirmDeleteModal', function () {
-        $("#ItemsToDelete").empty();
-        for (var i in data_array) {
-          $("#ItemsToDelete").append("<li>" + data_array[i] + "</li>");
-        }
-      })
-      $('#ConfirmDeleteModal').modal({
-        backdrop: 'static',
-        keyboard: false
-      })
-      .one('click', '#IsConfirmed', function(e) {
-        $.ajax({
-          type: "POST",
-          dataType: "json",
-          data: { "items": JSON.stringify(data_array), "csrf_token": csrf_token },
-          url: '/api/v1/' + api_url,
-          jsonp: false,
-          complete: function (data) {
-            location.assign(window.location);
-          }
-        });
-      })
-      .one('click', '#isCanceled', function(e) {
-        $('#ConfirmDeleteModal').modal('hide');
-      });;
-  });
-});
-
 jQuery(function($){
   // Calculation human readable file sizes
   function humanFileSize(bytes) {
diff --git a/data/web/js/user.js b/data/web/js/user.js
index 7959cc5a..8e94b85e 100644
--- a/data/web/js/user.js
+++ b/data/web/js/user.js
@@ -1,29 +1,4 @@
 $(document).ready(function() {
-	// Show and activate password fields after box was checked
-	// Hidden by default
-	if ( !$("#togglePwNew").is(':checked') ) {
-		$(".passFields").hide();
-	}
-	$('#togglePwNew').click(function() {
-		$("#user_new_pass").attr("disabled", !this.checked);
-		$("#user_new_pass2").attr("disabled", !this.checked);
-		var $this = $(this);
-		if ($this.is(':checked')) {
-			$(".passFields").slideDown();
-		} else {
-			$(".passFields").slideUp();
-		}
-	});
-	// Show generate button after time selection
-	$('#generate_tla').hide(); 
-	$('#validity').change(function(){
-		$('#generate_tla').show(); 
-	});
-
-	// Init Bootstrap Switch
-	$.fn.bootstrapSwitch.defaults.onColor = 'success';
-	$("#tls_out").bootstrapSwitch();
-	$("#tls_in").bootstrapSwitch();
 
   // Log modal
   $('#logModal').on('show.bs.modal', function(e) {
@@ -31,96 +6,8 @@ $(document).ready(function() {
   $(e.currentTarget).find('#logText').html('<pre style="background:none;font-size:11px;line-height:1.1;border:0px">' + logText + '</pre>');
   });
 
-  // Collect values of input fields with name multi_select with same data-id to js array multi_data[data-id]
-  var multi_data = [];
-  $(document).on('change', 'input[name=multi_select]:checkbox', function() {
-    if ($(this).is(':checked') && $(this).data('id')) {
-      var id = $(this).data('id');
-      if (typeof multi_data[id] == "undefined") {
-        multi_data[id] = [];
-      }
-      multi_data[id].push($(this).val());
-    }
-    else {
-      var id = $(this).data('id');
-      multi_data[id].splice($.inArray($(this).val(), multi_data[id]),1);
-    }
-  });
-  // Select checkbox by click on parent tr
-  $(document).on('click', 'tbody>tr', function(e) {
-    if (e.target.type == "checkbox") {
-      e.stopPropagation();
-    } else {
-      var checkbox = $(this).find(':checkbox');
-      checkbox.trigger('click');
-    }
-  });
-  // Select or deselect all checkboxes with same data-id
-  $(document).on('click', '#toggle_multi_select_all', function(e) {
-    e.preventDefault();
-    id = $(this).data("id");
-    multi_data[id] = [];
-    var all_checkboxes = $("input[data-id=" + id + "]:enabled");
-    all_checkboxes.prop("checked", !all_checkboxes.prop("checked")).change();
-  });
-  // General API edit actions
-  $(document).on('click', '#edit_selected', function(e) {
-    e.preventDefault();
-    var id = $(this).data('id');
-    if (typeof multi_data[id] == "undefined") return;
-    data_array = multi_data[id];
-    api_url = $(this).data('api-url');
-    api_attr = $(this).data('api-attr');
-    if (Object.keys(data_array).length !== 0) {
-      $.ajax({
-        type: "POST",
-        dataType: "json",
-        data: { "items": JSON.stringify(data_array), "attr": JSON.stringify(api_attr), "csrf_token": csrf_token },
-        url: '/api/v1/' + api_url,
-        jsonp: false,
-        complete: function (data) {
-          // var reponse = (JSON.parse(data.responseText));
-          // console.log(reponse.type);
-          // console.log(reponse.msg);
-          location.assign(window.location);
-        }
-      });
-    }
-  });
-  // General API delete actions
-  $(document).on('click', '#delete_selected', function(e) {
-    e.preventDefault();
-    var id = $(this).data('id');
-    if (typeof multi_data[id] == "undefined" || multi_data[id] == "") return;
-    data_array = multi_data[id];
-    api_url = $(this).data('api-url');
-      $(document).on('show.bs.modal','#ConfirmDeleteModal', function () {
-        $("#ItemsToDelete").empty();
-        for (var i in data_array) {
-          $("#ItemsToDelete").append("<li>" + data_array[i] + "</li>");
-        }
-      })
-      $('#ConfirmDeleteModal').modal({
-        backdrop: 'static',
-        keyboard: false
-      })
-      .one('click', '#IsConfirmed', function(e) {
-        $.ajax({
-          type: "POST",
-          dataType: "json",
-          data: { "items": JSON.stringify(data_array), "csrf_token": csrf_token },
-          url: '/api/v1/' + api_url,
-          jsonp: false,
-          complete: function (data) {
-            location.reload(true);
-          }
-        });
-      })
-      .one('click', '#isCanceled', function(e) {
-        $('#ConfirmDeleteModal').modal('hide');
-      });;
-  });
 });
+
 jQuery(function($){
   // http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
   var entityMap = {
@@ -138,11 +25,52 @@ jQuery(function($){
       return entityMap[s];
     });
   }
+  // http://stackoverflow.com/questions/46155/validate-email-address-in-javascript
+  function validateEmail(email) {
+    var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
+    return re.test(email);
+  }
+  function unix_time_format(tm) {
+    var date = new Date(tm ? tm * 1000 : 0);
+    return date.toLocaleString();
+  }
+  function draw_tla_table() {
+    ft_tla_table = FooTable.init('#tla_table', {
+      "columns": [
+        {"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"},
+        {"sorted": true,"name":"address","title":lang.alias},
+        {"name":"validity","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.alias_valid_until,"style":{"width":"170px"}},
+      ],
+      "empty": lang.empty,
+      "rows": $.ajax({
+        dataType: 'json',
+        url: '/api/v1/get/time_limited_aliases',
+        jsonp: false,
+        error: function () {
+          console.log('Cannot draw tla table');
+        },
+        success: function (data) {
+          $.each(data, function (i, item) {
+            item.chkbox = '<input type="checkbox" data-id="tla" name="multi_select" value="' + item.address + '" />';
+          });
+        }
+      }),
+      "paging": {
+        "enabled": true,
+        "limit": 5,
+        "size": pagination_size
+      },
+      "sorting": {
+        "enabled": true
+      }
+    });
+  }
   function draw_sync_job_table() {
-    ft_aliasdomain_table = FooTable.init('#sync_job_table', {
+    ft_syncjob_table = FooTable.init('#sync_job_table', {
       "columns": [
-        {"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
-        {"sorted": true,"name":"server_w_port","title":"Server"},
+        {"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"},
+        {"sorted": true,"name":"id","title":"ID"},
+        {"name":"server_w_port","title":"Server"},
         {"name":"enc1","title":lang.encryption},
         {"name":"user1","title":lang.username},
         {"name":"exclude","title":lang.excludes},
@@ -155,7 +83,7 @@ jQuery(function($){
       "empty": lang.empty,
       "rows": $.ajax({
         dataType: 'json',
-        url: '/api/v1/get/syncjob',
+        url: '/api/v1/get/syncjobs',
         jsonp: false,
         error: function () {
           console.log('Cannot draw sync job table');
@@ -182,5 +110,82 @@ jQuery(function($){
       }
     });
   }
+  function draw_wl_policy_mailbox_table() {
+    ft_wl_policy_mailbox_table = FooTable.init('#wl_policy_mailbox_table', {
+      "columns": [
+        {"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"},
+        {"name":"prefid","style":{"maxWidth":"40px","width":"40px"},"title":"ID","filterable": false,"sortable": false},
+        {"sorted": true,"name":"value","title":lang.spamfilter_table_rule},
+        {"name":"object","title":"Scope"}
+      ],
+      "empty": lang.empty,
+      "rows": $.ajax({
+        dataType: 'json',
+        url: '/api/v1/get/policy_wl_mailbox',
+        jsonp: false,
+        error: function () {
+          console.log('Cannot draw mailbox policy wl table');
+        },
+        success: function (data) {
+          $.each(data, function (i, item) {
+            if (validateEmail(item.object)) {
+              item.chkbox = '<input type="checkbox" data-id="policy_wl_mailbox" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" disabled title="' + lang.spamfilter_table_domain_policy + '" />';
+            }
+          });
+        }
+      }),
+      "paging": {
+        "enabled": true,
+        "limit": 5,
+        "size": pagination_size
+      },
+      "sorting": {
+        "enabled": true
+      }
+    });
+  }
+  function draw_bl_policy_mailbox_table() {
+    ft_bl_policy_mailbox_table = FooTable.init('#bl_policy_mailbox_table', {
+      "columns": [
+        {"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px","text-align":"center"},"filterable": false,"sortable": false,"type":"html"},
+        {"name":"prefid","style":{"maxWidth":"40px","width":"40px"},"title":"ID","filterable": false,"sortable": false},
+        {"sorted": true,"name":"value","title":lang.spamfilter_table_rule},
+        {"name":"object","title":"Scope"}
+      ],
+      "empty": lang.empty,
+      "rows": $.ajax({
+        dataType: 'json',
+        url: '/api/v1/get/policy_bl_mailbox',
+        jsonp: false,
+        error: function () {
+          console.log('Cannot draw mailbox policy bl table');
+        },
+        success: function (data) {
+          $.each(data, function (i, item) {
+            if (validateEmail(item.object)) {
+              item.chkbox = '<input type="checkbox" data-id="policy_bl_mailbox" name="multi_select" value="' + item.prefid + '" />';
+            }
+            else {
+              item.chkbox = '<input type="checkbox" disabled tooltip="' + lang.spamfilter_table_domain_policy + '" />';
+            }
+          });
+        }
+      }),
+      "paging": {
+        "enabled": true,
+        "limit": 5,
+        "size": pagination_size
+      },
+      "sorting": {
+        "enabled": true
+      }
+    });
+  }
   draw_sync_job_table();
+  draw_tla_table();
+  draw_wl_policy_mailbox_table();
+  draw_bl_policy_mailbox_table();
 });
\ No newline at end of file
diff --git a/data/web/json_api.php b/data/web/json_api.php
index a2d08e39..5c9d729f 100644
--- a/data/web/json_api.php
+++ b/data/web/json_api.php
@@ -25,15 +25,52 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
     $extra =      (isset($query[3])) ? $query[3] : null;
 
     switch ($action) {
+      case "add":
+        switch ($category) {
+          case "time_limited_alias":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (mailbox('add', 'time_limited_alias', $attr) === false) {
+                if (isset($_SESSION['return'])) {
+                  echo json_encode($_SESSION['return']);
+                }
+                else {
+                  echo json_encode(array(
+                    'type' => 'error',
+                    'msg' => 'Cannot add item'
+                  ));
+                }
+              }
+              else {
+                if (isset($_SESSION['return'])) {
+                  echo json_encode($_SESSION['return']);
+                }
+                else {
+                  echo json_encode(array(
+                    'type' => 'success',
+                    'msg' => 'Task completed'
+                  ));
+                }
+              }
+            }
+            else {
+              echo json_encode(array(
+                'type' => 'error',
+                'msg' => 'Cannot find attributes in post data'
+              ));
+            }
+          break;
+        }
+      break;
       case "get":
         switch ($category) {
           case "domain":
             switch ($object) {
               case "all":
-                $domains = mailbox_get_domains();
+                $domains = mailbox('get', 'domains');
                 if (!empty($domains)) {
                   foreach ($domains as $domain) {
-                    if ($details = mailbox_get_domain_details($domain)) {
+                    if ($details = mailbox('get', 'domain_details', $domain)) {
                       $data[] = $details;
                     }
                     else {
@@ -53,7 +90,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               break;
 
               default:
-                $data = mailbox_get_domain_details($object);
+                $data = mailbox('get', 'domain_details', $object);
                 if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
@@ -110,18 +147,27 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
                   echo '{}';
                 }
               break;
+              case "rspamd-history":
+                $logs = get_logs('rspamd-history');
+                if (isset($logs) && !empty($logs)) {
+                  echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+                }
+                else {
+                  echo '{}';
+                }
+              break;
             }
           break;
           case "mailbox":
             switch ($object) {
               case "all":
-                $domains = mailbox_get_domains();
+                $domains = mailbox('get', 'domains');
                 if (!empty($domains)) {
                   foreach ($domains as $domain) {
-                    $mailboxes = mailbox_get_mailboxes($domain);
+                    $mailboxes = mailbox('get', 'mailboxes', $domain);
                     if (!empty($mailboxes)) {
                       foreach ($mailboxes as $mailbox) {
-                        if ($details = mailbox_get_mailbox_details($mailbox)) {
+                        if ($details = mailbox('get', 'mailbox_details', $mailbox)) {
                           $data[] = $details;
                         }
                         else {
@@ -143,7 +189,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               break;
 
               default:
-                $data = mailbox_get_mailbox_details($object);
+                $data = mailbox('get', 'mailbox_details', $object);
                 if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
@@ -153,10 +199,75 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               break;
             }
           break;
-          case "syncjob":
+          case "syncjobs":
+            switch ($object) {
+              default:
+                $data = mailbox('get', 'syncjobs', $object);
+                if (!isset($data) || empty($data)) {
+                  echo '{}';
+                }
+                else {
+                  echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+                }
+              break;
+            }
+          break;
+          case "policy_wl_mailbox":
+            switch ($object) {
+              default:
+                $data = policy('get', 'mailbox', $object)['whitelist'];
+                if (!isset($data) || empty($data)) {
+                  echo '{}';
+                }
+                else {
+                  echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+                }
+              break;
+            }
+          break;
+          case "policy_bl_mailbox":
+            switch ($object) {
+              default:
+                $data = policy('get', 'mailbox', $object)['blacklist'];
+                if (!isset($data) || empty($data)) {
+                  echo '{}';
+                }
+                else {
+                  echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+                }
+              break;
+            }
+          break;
+          case "policy_wl_domain":
+            switch ($object) {
+              default:
+                $data = policy('get', 'domain', $object)['whitelist'];
+                if (!isset($data) || empty($data)) {
+                  echo '{}';
+                }
+                else {
+                  echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+                }
+              break;
+            }
+          break;
+          case "policy_bl_domain":
             switch ($object) {
               default:
-                $data = get_syncjobs($object);
+                $data = policy('get', 'domain', $object)['blacklist'];
+                if (!isset($data) || empty($data)) {
+                  echo '{}';
+                }
+                else {
+                  echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+                }
+              break;
+            }
+          break;
+          case "time_limited_aliases":
+            switch ($object) {
+              default:
+                $data = mailbox('get', 'time_limited_aliases', $object);
                 if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
@@ -169,13 +280,13 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
           case "resource":
             switch ($object) {
               case "all":
-                $domains = mailbox_get_domains();
+                $domains = mailbox('get', 'domains');
                 if (!empty($domains)) {
                   foreach ($domains as $domain) {
-                    $resources = mailbox_get_resources($domain);
+                    $resources = mailbox('get', 'resources', $domain);
                     if (!empty($resources)) {
                       foreach ($resources as $resource) {
-                        if ($details = mailbox_get_resource_details($resource)) {
+                        if ($details = mailbox('get', 'resource_details', $resource)) {
                           $data[] = $details;
                         }
                         else {
@@ -196,7 +307,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
                 }
               break;
               default:
-                $data = mailbox_get_resource_details($object);
+                $data = mailbox('get', 'resource_details', $object);
                 if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
@@ -241,13 +352,13 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
           case "alias-domain":
             switch ($object) {
               case "all":
-                $domains = mailbox_get_domains();
+                $domains = mailbox('get', 'domains');
                 if (!empty($domains)) {
                   foreach ($domains as $domain) {
-                    $alias_domains = mailbox_get_alias_domains($domain);
+                    $alias_domains = mailbox('get', 'alias_domains', $domain);
                     if (!empty($alias_domains)) {
                       foreach ($alias_domains as $alias_domain) {
-                        if ($details = mailbox_get_alias_domain_details($alias_domain)) {
+                        if ($details = mailbox('get', 'alias_domain_details', $alias_domain)) {
                           $data[] = $details;
                         }
                         else {
@@ -268,7 +379,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
                 }
               break;
               default:
-                $data = mailbox_get_alias_domains($object);
+                $data = mailbox('get', 'alias_domains', $object);
                 if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
@@ -281,13 +392,13 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
           case "alias":
             switch ($object) {
               case "all":
-                $domains = array_merge(mailbox_get_domains(), mailbox_get_alias_domains());
+                $domains = array_merge(mailbox('get', 'domains'),mailbox('get', 'alias_domains'));
                 if (!empty($domains)) {
                   foreach ($domains as $domain) {
-                    $aliases = mailbox_get_aliases($domain);
+                    $aliases = mailbox('get', 'aliases', $domain);
                     if (!empty($aliases)) {
                       foreach ($aliases as $alias) {
-                        if ($details = mailbox_get_alias_details($alias)) {
+                        if ($details = mailbox('get', 'alias_details', $alias)) {
                           $data[] = $details;
                         }
                         else {
@@ -309,7 +420,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               break;
 
               default:
-                $data = mailbox_get_alias_details($object);
+                $data = mailbox('get', 'alias_details', $object);
                 if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
@@ -389,7 +500,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
             if (isset($_POST['items'])) {
               $items = (array)json_decode($_POST['items'], true);
               if (is_array($items)) {
-                if (mailbox_delete_alias(array('address' => $items)) === false) {
+                if (mailbox('delete', 'alias', array('address' => $items)) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -426,6 +537,47 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               ));
             }
           break;
+          case "syncjob":
+            if (isset($_POST['items'])) {
+              $items = (array)json_decode($_POST['items'], true);
+              if (is_array($items)) {
+                if (mailbox('delete', 'syncjob', array('id' => $items)) === false) {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'error',
+                      'msg' => 'Deletion of items/s failed'
+                    ));
+                  }
+                }
+                else {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'success',
+                      'msg' => 'Task completed'
+                    ));
+                  }
+                }
+              }
+              else {
+                echo json_encode(array(
+                  'type' => 'error',
+                  'msg' => 'Cannot find id array in post data'
+                ));
+              }
+            }
+            else {
+              echo json_encode(array(
+                'type' => 'error',
+                'msg' => 'Cannot find items in post data'
+              ));
+            }
+          break;
           case "fwdhost":
             if (isset($_POST['items'])) {
               $items = (array)json_decode($_POST['items'], true);
@@ -512,7 +664,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
             if (isset($_POST['items'])) {
               $items = (array)json_decode($_POST['items'], true);
               if (is_array($items)) {
-                if (mailbox_delete_domain(array('domain' => $items)) === false) {
+                if (mailbox('delete', 'domain', array('domain' => $items)) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -553,7 +705,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
             if (isset($_POST['items'])) {
               $items = (array)json_decode($_POST['items'], true);
               if (is_array($items)) {
-                if (mailbox_delete_alias_domain(array('alias_domain' => $items)) === false) {
+                if (mailbox('delete', 'alias_domain', array('alias_domain' => $items)) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -594,7 +746,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
             if (isset($_POST['items'])) {
               $items = (array)json_decode($_POST['items'], true);
               if (is_array($items)) {
-                if (mailbox_delete_mailbox(array('username' => $items)) === false) {
+                if (mailbox('delete', 'mailbox', array('username' => $items)) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -635,7 +787,130 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
             if (isset($_POST['items'])) {
               $items = (array)json_decode($_POST['items'], true);
               if (is_array($items)) {
-                if (mailbox_delete_resource(array('name' => $items)) === false) {
+                if (mailbox('delete', 'resource', array('name' => $items)) === false) {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'error',
+                      'msg' => 'Task failed'
+                    ));
+                  }
+                }
+                else {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'success',
+                      'msg' => 'Task completed'
+                    ));
+                  }
+                }
+              }
+              else {
+                echo json_encode(array(
+                  'type' => 'error',
+                  'msg' => 'Cannot find name array in post data'
+                ));
+              }
+            }
+            else {
+              echo json_encode(array(
+                'type' => 'error',
+                'msg' => 'Cannot find items in post data'
+              ));
+            }
+          break;
+          case "policy_mailbox":
+            if (isset($_POST['items'])) {
+              $items = (array)json_decode($_POST['items'], true);
+              if (is_array($items)) {
+                if (policy('delete', 'mailbox', array('prefid' => $items)) === false) {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'error',
+                      'msg' => 'Task failed'
+                    ));
+                  }
+                }
+                else {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'success',
+                      'msg' => 'Task completed'
+                    ));
+                  }
+                }
+              }
+              else {
+                echo json_encode(array(
+                  'type' => 'error',
+                  'msg' => 'Cannot find name array in post data'
+                ));
+              }
+            }
+            else {
+              echo json_encode(array(
+                'type' => 'error',
+                'msg' => 'Cannot find items in post data'
+              ));
+            }
+          break;
+          case "time_limited_alias":
+            if (isset($_POST['items'])) {
+              $items = (array)json_decode($_POST['items'], true);
+              if (is_array($items)) {
+                if (mailbox('delete', 'time_limited_alias', array('address' => $items)) === false) {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'error',
+                      'msg' => 'Task failed'
+                    ));
+                  }
+                }
+                else {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'success',
+                      'msg' => 'Task completed'
+                    ));
+                  }
+                }
+              }
+              else {
+                echo json_encode(array(
+                  'type' => 'error',
+                  'msg' => 'Cannot find name array in post data'
+                ));
+              }
+            }
+            else {
+              echo json_encode(array(
+                'type' => 'error',
+                'msg' => 'Cannot find items in post data'
+              ));
+            }
+          break;
+          case "eas_cache":
+            if (isset($_POST['items'])) {
+              $items = (array)json_decode($_POST['items'], true);
+              if (is_array($items)) {
+                if (mailbox('delete', 'eas_cache', array('username' => $items)) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -682,7 +957,139 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               $attr = (array)json_decode($_POST['attr'], true);
               $postarray = array_merge(array('address' => $items), $attr);
               if (is_array($postarray['address'])) {
-                if (mailbox_edit_alias($postarray) === false) {
+                if (mailbox('edit', 'alias', $postarray) === false) {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'error',
+                      'msg' => 'Edit failed'
+                    ));
+                  }
+                  exit();
+                }
+                else {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'success',
+                      'msg' => 'Task completed'
+                    ));
+                  }
+                }
+              }
+              else {
+                echo json_encode(array(
+                  'type' => 'error',
+                  'msg' => 'Incomplete post data'
+                ));
+              }
+            }
+            else {
+              echo json_encode(array(
+                'type' => 'error',
+                'msg' => 'Incomplete post data'
+              ));
+            }
+          break;
+          case "delimiter_action":
+            if (isset($_POST['items']) && isset($_POST['attr'])) {
+              $items = (array)json_decode($_POST['items'], true);
+              $attr = (array)json_decode($_POST['attr'], true);
+              $postarray = array_merge(array('username' => $items), $attr);
+              if (is_array($postarray['username'])) {
+                if (mailbox('edit', 'delimiter_action', $postarray) === false) {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'error',
+                      'msg' => 'Edit failed'
+                    ));
+                  }
+                  exit();
+                }
+                else {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'success',
+                      'msg' => 'Task completed'
+                    ));
+                  }
+                }
+              }
+              else {
+                echo json_encode(array(
+                  'type' => 'error',
+                  'msg' => 'Incomplete post data'
+                ));
+              }
+            }
+            else {
+              echo json_encode(array(
+                'type' => 'error',
+                'msg' => 'Incomplete post data'
+              ));
+            }
+          break;
+          case "tls_policy":
+            if (isset($_POST['items']) && isset($_POST['attr'])) {
+              $items = (array)json_decode($_POST['items'], true);
+              $attr = (array)json_decode($_POST['attr'], true);
+              $postarray = array_merge(array('username' => $items), $attr);
+              if (is_array($postarray['username'])) {
+                if (mailbox('edit', 'tls_policy', $postarray) === false) {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'error',
+                      'msg' => 'Edit failed'
+                    ));
+                  }
+                  exit();
+                }
+                else {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'success',
+                      'msg' => 'Task completed'
+                    ));
+                  }
+                }
+              }
+              else {
+                echo json_encode(array(
+                  'type' => 'error',
+                  'msg' => 'Incomplete post data'
+                ));
+              }
+            }
+            else {
+              echo json_encode(array(
+                'type' => 'error',
+                'msg' => 'Incomplete post data'
+              ));
+            }
+          break;
+          case "time_limited_alias":
+            if (isset($_POST['items']) && isset($_POST['attr'])) {
+              $items = (array)json_decode($_POST['items'], true);
+              $attr = (array)json_decode($_POST['attr'], true);
+              $postarray = array_merge(array('address' => $items), $attr);
+              if (is_array($postarray['address'])) {
+                if (mailbox('edit', 'time_limited_alias', $postarray) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -726,7 +1133,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               $attr = (array)json_decode($_POST['attr'], true);
               $postarray = array_merge(array('username' => $items), $attr);
               if (is_array($postarray['username'])) {
-                if (mailbox_edit_mailbox($postarray) === false) {
+                if (mailbox('edit', 'mailbox', $postarray) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -770,7 +1177,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               $attr = (array)json_decode($_POST['attr'], true);
               $postarray = array_merge(array('id' => $items), $attr);
               if (is_array($postarray['id'])) {
-                if (edit_syncjob($postarray) === false) {
+                if (mailbox('edit', 'syncjob', $postarray) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -814,7 +1221,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               $attr = (array)json_decode($_POST['attr'], true);
               $postarray = array_merge(array('name' => $items), $attr);
               if (is_array($postarray['name'])) {
-                if (mailbox_edit_resource($postarray) === false) {
+                if (mailbox('edit', 'resource', $postarray) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -858,7 +1265,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               $attr = (array)json_decode($_POST['attr'], true);
               $postarray = array_merge(array('domain' => $items), $attr);
               if (is_array($postarray['domain'])) {
-                if (mailbox_edit_domain($postarray) === false) {
+                if (mailbox('edit', 'domain', $postarray) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -902,7 +1309,51 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               $attr = (array)json_decode($_POST['attr'], true);
               $postarray = array_merge(array('alias_domain' => $items), $attr);
               if (is_array($postarray['alias_domain'])) {
-                if (mailbox_edit_alias_domain($postarray) === false) {
+                if (mailbox('edit', 'alias_domain', $postarray) === false) {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'error',
+                      'msg' => 'Edit failed'
+                    ));
+                  }
+                  exit();
+                }
+                else {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'success',
+                      'msg' => 'Task completed'
+                    ));
+                  }
+                }
+              }
+              else {
+                echo json_encode(array(
+                  'type' => 'error',
+                  'msg' => 'Incomplete post data'
+                ));
+              }
+            }
+            else {
+              echo json_encode(array(
+                'type' => 'error',
+                'msg' => 'Incomplete post data'
+              ));
+            }
+          break;
+          case "spam_score":
+            if (isset($_POST['items']) && isset($_POST['attr'])) {
+              $items = (array)json_decode($_POST['items'], true);
+              $attr = (array)json_decode($_POST['attr'], true);
+              $postarray = array_merge(array('username' => $items), $attr);
+              if (is_array($postarray['username'])) {
+                if (mailbox('edit', 'spam_score', $postarray) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php
index b5d5470e..1aee2c5f 100644
--- a/data/web/lang/lang.de.php
+++ b/data/web/lang/lang.de.php
@@ -10,7 +10,7 @@ $lang['footer']['restart_now'] = 'Jetzt neustarten';
 $lang['footer']['restart_sogo_info'] = 'Einige Änderungen an Domains benötigen einen Neustart SOGos. Hier können Sie SOGo neustarten.<br><br><b>Wichtig:</b> Ein korrekter Neustart SOGos kann eine Weile in Anspruch nehmen, bitte warten Sie, bis der Prozess vollständig beendet wurde.';
 
 $lang['footer']['confirm_delete'] = 'Löschen bestätigen';
-$lang['footer']['delete_these_items'] = 'Sind Sie sicher, dass die folgenden Elemente entfernt werden sollen?';
+$lang['footer']['delete_these_items'] = 'Sind Sie sicher, dass die Änderungen an folgenden Elementen durchgeführt werden sollen?';
 $lang['footer']['delete_now'] = 'Jetzt löschen';
 $lang['footer']['cancel'] = 'Abbrechen';
 
@@ -28,6 +28,7 @@ $lang['danger']['domain_invalid'] = 'Domainname ist ungültig';
 $lang['danger']['mailbox_quota_exceeds_domain_quota'] = 'Maximale Größe für Mailboxen überschreitet das Domain Speicherlimit';
 $lang['danger']['object_is_not_numeric'] = 'Wert %s ist nicht numerisch';
 $lang['success']['domain_added'] = 'Domain %s wurde angelegt';
+$lang['success']['items_deleted'] = "Objekt(e) %s wurde(n) erfolgreich entfernt";
 $lang['danger']['alias_empty'] = 'Alias-Adresse darf nicht leer sein';
 $lang['danger']['goto_empty'] = 'Ziel-Adresse darf nicht leer sein';
 $lang['danger']['policy_list_from_exists'] = 'Ein Eintrag mit diesem Wert existiert bereits';
diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php
index 2763e0f9..5c2936db 100644
--- a/data/web/lang/lang.en.php
+++ b/data/web/lang/lang.en.php
@@ -10,7 +10,7 @@ $lang['footer']['restart_now'] = 'Restart now';
 $lang['footer']['restart_sogo_info'] = 'Some tasks, e.g. adding a domain, require you to restart SOGo to catch changes made in the mailcow UI.<br><br><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'] = 'Are you sure you want to delete the following items?';
+$lang['footer']['delete_these_items'] = 'Please confirm your changes to the following items:';
 $lang['footer']['delete_now'] = 'Delete now';
 $lang['footer']['cancel'] = 'Cancel';
 
@@ -28,6 +28,7 @@ $lang['danger']['domain_invalid'] = "Domain name is invalid";
 $lang['danger']['mailbox_quota_exceeds_domain_quota'] = "Max. quota exceeds domain quota limit";
 $lang['danger']['object_is_not_numeric'] = "Value %s is not numeric";
 $lang['success']['domain_added'] = "Added domain %s";
+$lang['success']['items_deleted'] = "Item %s successfully deleted";
 $lang['danger']['alias_empty'] = "Alias address must not be empty";
 $lang['danger']['last_key'] = 'Last key cannot be deleted';
 $lang['danger']['goto_empty'] = "Goto address must not be empty";
diff --git a/data/web/user.php b/data/web/user.php
index 7a3d6f87..4fa4abc8 100644
--- a/data/web/user.php
+++ b/data/web/user.php
@@ -27,7 +27,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
         <div class="col-sm-9 col-xs-7">
           <p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
             <div id="tfa_additional">
-              <?php if($tfa_data['additional']):
+              <?php if (!empty($tfa_data['additional'])):
               foreach ($tfa_data['additional'] as $key_info): ?>
                 <form style="display:inline;" method="post">
                 <input type="hidden" name="unset_tfa_key" value="<?=$key_info['id'];?>" />
@@ -63,8 +63,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
 	require_once("inc/header.inc.php");
 	$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
 	$username = $_SESSION['mailcow_cc_username'];
-	$get_tls_policy = get_tls_policy($_SESSION['mailcow_cc_username']);
-  $mailboxdata = mailbox_get_mailbox_details($username);
+  $mailboxdata = mailbox('get', 'mailbox_details', $username);
 ?>
 <div class="container">
 <h3><?=$lang['user']['user_settings'];?></h3>
@@ -124,31 +123,64 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
   </div>
   <hr>
   <?php // Show tagging options ?>
-  <form class="form-horizontal" role="form" method="post">
-  <?php $get_tagging_options = get_delimiter_action();?>
+  <?php $get_tagging_options = mailbox('get', 'delimiter_action', $username);?>
   <div class="row">
     <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tag_handling'];?>:</div>
     <div class="col-md-9 col-xs-7">
-    <input type="hidden" name="edit_delimiter_action" value="1">
-    <select name="tagged_mail_handler" class="selectpicker" onchange="this.form.submit()">
-      <option value="subfolder" <?=($get_tagging_options == "subfolder") ? 'selected' : null; ?>><?=$lang['user']['tag_in_subfolder'];?></option>
-      <option value="subject" <?=($get_tagging_options == "subject") ? 'selected' : null; ?>><?=$lang['user']['tag_in_subject'];?></option>
-    </select>
+    <div class="btn-group">
+
+      <button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "subfolder") ? 'active' : null; ?>"
+        id="edit_selected"
+        data-item="<?= $username; ?>"
+        data-id="delimiter_action"
+        data-api-url='edit/delimiter_action'
+        data-api-attr='{"tagged_mail_handler":"subfolder"}'><?=$lang['user']['tag_in_subfolder'];?></button>
+
+      <button type="button" class="btn btn-sm btn-default <?=($get_tagging_options == "subject") ? 'active' : null; ?>"
+        id="edit_selected"
+        data-item="<?= $username; ?>"
+        data-id="delimiter_action"
+        data-api-url='edit/delimiter_action'
+        data-api-attr='{"tagged_mail_handler":"subject"}'><?=$lang['user']['tag_in_subject'];?></button>
+
+    </div>
     <p class="help-block"><?=$lang['user']['tag_help_explain'];?></p>
     <p class="help-block"><?=$lang['user']['tag_help_example'];?></p>
     </div>
   </div>
-  </form>
+  <?php // Show TLS policy options ?>
+  <?php $get_tls_policy = mailbox('get', 'tls_policy', $username); ?>
+  <div class="row">
+    <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['tls_policy'];?>:</div>
+    <div class="col-md-9 col-xs-7">
+    <div class="btn-group">
+
+      <button type="button" class="btn btn-sm btn-default <?=($get_tls_policy['tls_enforce_in'] == "1") ? "active" : null;?>"
+        id="edit_selected"
+        data-item="<?= $username; ?>"
+        data-id="tls_policy"
+        data-api-url='edit/tls_policy'
+        data-api-attr='{"tls_enforce_in":<?=($get_tls_policy['tls_enforce_in'] == "1") ? "0" : "1";?>}'><?=$lang['user']['tls_enforce_in'];?></button>
+
+      <button type="button" class="btn btn-sm btn-default <?=($get_tls_policy['tls_enforce_out'] == "1") ? "active" : null;?>"
+        id="edit_selected"
+        data-item="<?= $username; ?>"
+        data-id="tls_policy"
+        data-api-url='edit/tls_policy'
+        data-api-attr='{"tls_enforce_out":<?=($get_tls_policy['tls_enforce_out'] == "1") ? "0" : "1";?>}'><?=$lang['user']['tls_enforce_out'];?></button>
+
+    </div>
+    <p class="help-block"><?=$lang['user']['tls_policy_warning'];?></p>
+    </div>
+  </div>
   <?php // Rest EAS devices ?>
-  <form class="form-horizontal" role="form" method="post">
   <div class="row">
     <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['eas_reset'];?>:</div>
     <div class="col-md-9 col-xs-7">
-    <button type="submit" name="mailbox_reset_eas" id="mailbox_reset_eas" value="1" class="btn btn-xs btn-default"><?=$lang['user']['eas_reset_now'];?></button>
+    <button class="btn btn-xs btn-default" id="delete_selected" data-text="<?=$lang['user']['eas_reset'];?>?" data-item="<?= $username; ?>" data-id="eas_cache" data-api-url='delete/eas_cache' href="#"><?=$lang['user']['eas_reset_now'];?></button>
     <p class="help-block"><?=$lang['user']['eas_reset_help'];?></p>
     </div>
   </div>
-  </form>
 </div>
 </div>
 
@@ -156,92 +188,50 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
 <ul class="nav nav-pills nav-justified" role="tablist">
 	<li role="presentation" class="active"><a href="#SpamAliases" aria-controls="SpamAliases" role="tab" data-toggle="tab"><?=$lang['user']['spam_aliases'];?></a></li>
 	<li role="presentation"><a href="#Spamfilter" aria-controls="Spamfilter" role="tab" data-toggle="tab"><?=$lang['user']['spamfilter'];?></a></li>
-	<li role="presentation"><a href="#TLSPolicy" aria-controls="TLSPolicy" role="tab" data-toggle="tab"><?=$lang['user']['tls_policy'];?></a></li>
 	<li role="presentation"><a href="#Syncjobs" aria-controls="Syncjobs" role="tab" data-toggle="tab"><?=$lang['user']['sync_jobs'];?></a></li>
 </ul>
 <hr>
 
 <div class="tab-content">
 	<div role="tabpanel" class="tab-pane active" id="SpamAliases">
-		<div class="row">
-			<div class="col-xs-6">
-				<p><b><?=$lang['user']['alias'];?></b></p>
-			</div>
-			<div class="col-xs-2">
-				<p><b><?=$lang['user']['alias_valid_until'];?></b></p>
-			</div>
-			<div class="col-xs-2">
-        <p><b><?=$lang['user']['action'];?></b></p>
-			</div>
-    </div>
-			<?php
-      $get_time_limited_aliases = get_time_limited_aliases($username);
-      if (!empty($get_time_limited_aliases)):
-        foreach ($get_time_limited_aliases as $row):
-        ?>
-		<div class="row">
-      <div class="col-xs-6">
-        <p><?=htmlspecialchars($row['address']);?></p>
-      </div>
-      <div class="col-xs-2">
-        <p><?=htmlspecialchars(date($lang['user']['alias_full_date'], $row['validity']));?></p>
-      </div>
-      <div class="col-xs-1">
-        <form class="form-inline" role="form" method="post">
-          <a class="text-danger" href="#" onclick="$(this).closest('form').submit()"><span class="glyphicon glyphicon-remove"></span></a>
-          <input type="hidden" name="set_time_limited_aliases" value="delete">
-          <input type="hidden" name="item" value="<?=htmlspecialchars($row['address']);?>">
-        </form>
+    <div class="row">
+      <div class="col-md-6 col-sm-12 col-xs-12">
+        <div class="table-responsive">
+          <table class="table table-striped" id="tla_table"></table>
+        </div>
       </div>
-      <div class="col-xs-1">
-        <form class="form-inline" role="form" method="post">
-          <a href="#" onclick="$(this).closest('form').submit()"><span class="glyphicon glyphicon-time"></span> + 1h</a>
-          <input type="hidden" name="set_time_limited_aliases" value="extend">
-          <input type="hidden" name="item" value="<?=htmlspecialchars($row['address']);?>">
-        </form>
+		</div>
+    <div class="mass-actions-user">
+      <div class="btn-group">
+        <div class="btn-group">
+          <a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="tla" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
+          <a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['mailbox']['quick_actions'];?> <span class="caret"></span></a>
+          <ul class="dropdown-menu">
+            <li><a id="edit_selected" data-id="tla" data-api-url='edit/time_limited_alias' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-time"></span> + 1h</a></li>
+            <li role="separator" class="divider"></li>
+            <li><a id="delete_selected" data-id="tla" data-api-url='delete/time_limited_alias' href="#"><?=$lang['mailbox']['remove'];?></a></li>
+          </ul>
+        </div>
+        <div class="btn-group">
+          <a class="btn btn-sm btn-success dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['user']['alias_create_random'];?> <span class="caret"></span></a>
+          <ul class="dropdown-menu">
+            <li><a id="add_item" data-api-url='add/time_limited_alias' data-api-attr='{"validity":"1"}' href="#">1 <?=$lang['user']['hour'];?></a></li>
+            <li><a id="add_item" data-api-url='add/time_limited_alias' data-api-attr='{"validity":"6"}' href="#">6 <?=$lang['user']['hours'];?></a></li>
+            <li><a id="add_item" data-api-url='add/time_limited_alias' data-api-attr='{"validity":"24"}' href="#">1 <?=$lang['user']['day'];?></a></li>
+            <li><a id="add_item" data-api-url='add/time_limited_alias' data-api-attr='{"validity":"168"}' href="#">1 <?=$lang['user']['week'];?></a></li>
+            <li><a id="add_item" data-api-url='add/time_limited_alias' data-api-attr='{"validity":"672"}' href="#">4 <?=$lang['user']['weeks'];?></a></li>
+          </ul>
+        </div>
       </div>
     </div>
-        <?php
-        endforeach;
-			else:
-			?>
-      <div class="col-xs-12">
-        <center><i><?=$lang['user']['no_record'];?></i></center>
-      </div>
-			<?php
-			endif;	
-			?>
-    <form class="form-horizontal" role="form" method="post">
-		<div class="form-group">
-			<div class="col-sm-9">
-				<select id="validity" name="validity" title="<?=$lang['user']['alias_select_validity'];?>">
-					<option value="1">1 <?=$lang['user']['hour'];?></option>
-					<option value="6">6 <?=$lang['user']['hours'];?></option>
-					<option value="24">1 <?=$lang['user']['day'];?></option>
-					<option value="168">1 <?=$lang['user']['week'];?></option>
-					<option value="672">4 <?=$lang['user']['weeks'];?></option>
-				</select>
-				<button type="submit" name="set_time_limited_aliases" id="generate_tla" value="generate" class="btn btn-success"><?=$lang['user']['alias_create_random'];?></button>
-			</div>
-		</div>
-		<div class="form-group">
-			<div class="col-sm-12">
-				<button type="submit" name="set_time_limited_aliases" value="deleteall" class="btn-danger btn btn-sm">
-					<span class="glyphicon glyphicon-remove" aria-hidden="true"></span> <?=$lang['user']['alias_remove_all'];?>
-				</button>
-				<button type="submit" name="set_time_limited_aliases" value="extendall" class="btn-default btn btn-sm">
-					<span class="glyphicon glyphicon-time" aria-hidden="true"></span> <?=$lang['user']['alias_extend_all'];?>
-				</button>
-			</div>
-		</div>
-		</form>
 	</div>
+
 	<div role="tabpanel" class="tab-pane" id="Spamfilter">
 		<h4><?=$lang['user']['spamfilter_behavior'];?></h4>
-		<form class="form-horizontal" role="form" method="post">
+		<form class="form-horizontal" role="form" data-id="spam_score" method="post">
 			<div class="form-group">
 				<div class="col-sm-offset-2 col-sm-10">
-					<input name="score" id="score" type="text" 
+					<input name="spam_score" id="spam_score" type="text" 
 						data-provide="slider"
 						data-slider-min="1"
 						data-slider-max="30"
@@ -249,7 +239,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
 						data-slider-range="true"
 						data-slider-tooltip='always'
 						data-slider-id="slider1"
-						data-slider-value="[<?=get_spam_score($username);?>]"
+						data-slider-value="[<?=mailbox('get', 'spam_score', $username);?>]"
 						data-slider-step="1" />
 					<br /><br />
 					<ul>
@@ -263,149 +253,67 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
 			</div>
 			<div class="form-group">
 				<div class="col-sm-offset-2 col-sm-10">
-					<button type="submit" id="edit_spam_score" name="edit_spam_score" class="btn btn-success"><?=$lang['user']['save_changes'];?></button>
+        <button type="button" class="btn btn-sm btn-success" id="edit_selected"
+          data-item="<?= $username; ?>"
+          data-id="spam_score"
+          data-api-url='edit/spam_score'
+          data-api-attr='{}'><?=$lang['user']['save_changes'];?></button>
 				</div>
 			</div>
 		</form>
 		<hr>
 		<div class="row">
 			<div class="col-sm-6">
-				<h4><span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span> <?=$lang['user']['spamfilter_wl'];?></h4>
-				<p><?=$lang['user']['spamfilter_wl_desc'];?></p>
-				<div class="row">
-					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_rule'];?></b></div>
-					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div>
-				</div>
-				<?php
-        $get_policy_list = get_policy_list($username);
-				if (empty($get_policy_list['whitelist'])):
-				?>
-					<div class="row">
-						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
-					</div>
-				<?php
-				else:
-          foreach($get_policy_list['whitelist'] as $wl):
-          ?>
-          <div class="row striped">
-            <form class="form-inline" method="post">
-            <div class="col-xs-6"><code><?=$wl['value'];?></code></div>
-            <div class="col-xs-6">
-              <input type="hidden" name="delete_prefid" value="<?=$wl['prefid'];?>">
-              <?php
-              if (filter_var($wl['object'], FILTER_VALIDATE_EMAIL)):
-              ?>
-                <input type="hidden" name="delete_policy_list_item">
-                <a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
-              <?php
-              else:
-              ?>
-                <span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
-              <?php
-              endif;
-              ?>
-            </div>
-            </form>
+				<h4><?=$lang['user']['spamfilter_wl'];?></h4>
+        <p><?=$lang['user']['spamfilter_wl_desc'];?></p>
+        <div class="table-responsive">
+          <table class="table table-striped table-condensed" id="wl_policy_mailbox_table"></table>
+        </div>
+        <div class="mass-actions-user">
+          <div class="btn-group">
+            <a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="policy_wl_mailbox" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
+            <a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_wl_mailbox" data-api-url='delete/policy_mailbox' href="#"><?=$lang['mailbox']['remove'];?></a></li>
+            </ul>
           </div>
-          <?php
-          endforeach;
-        endif;
-				?>
-				<hr style="margin:5px 0px 7px 0px">
-				<div class="row">
-					<form class="form-inline" method="post">
-					<div class="col-xs-6">
-						<input type="text" class="form-control input-sm" name="object_from" id="object_from" placeholder="*@example.org" required>
-						<input type="hidden" name="object_list" value="wl">
-					</div>
-					<div class="col-xs-6">
-						<button type="submit" id="add_policy_list_item" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
-					</div>
-					</form>
-				</div>
-			</div>
+        </div>
+        <form class="form-inline" method="post">
+          <div class="input-group">
+            <input type="text" class="form-control" name="object_from" id="object_from" placeholder="*@example.org" required>
+            <input type="hidden" name="username" value="<?= $username ;?>">
+            <input type="hidden" name="object_list" value="wl">
+            <span class="input-group-btn">
+              <button type="submit" id="add_policy_list_item" name="add_policy_list_item" class="btn btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
+            </span>
+          </div>
+        </form>
+      </div>
 			<div class="col-sm-6">
-				<h4><span class="glyphicon glyphicon-thumbs-down" aria-hidden="true"></span> <?=$lang['user']['spamfilter_bl'];?></h4>
-				<p><?=$lang['user']['spamfilter_bl_desc'];?></p>
-				<div class="row">
-					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_rule'];?></b></div>
-					<div class="col-sm-6"><b><?=$lang['user']['spamfilter_table_action'];?></b></div>
-				</div>
-				<?php
-				if (empty($get_policy_list['blacklist'])):
-				?>
-					<div class="row">
-						<div class="col-sm-12"><i><?=$lang['user']['spamfilter_table_empty'];?></i></div>
-					</div>
-				<?php
-				else:
-          foreach($get_policy_list['blacklist'] as $bl):
-          ?>
-          <div class="row striped">
-            <form class="form-inline" method="post">
-            <div class="col-xs-6"><code><?=$bl['value'];?></code></div>
-            <div class="col-xs-6">
-              <?php
-              if (filter_var($bl['object'], FILTER_VALIDATE_EMAIL)):
-              ?>
-                <input type="hidden" name="delete_prefid" value="<?=$bl['prefid'];?>">
-                <input type="hidden" name="delete_policy_list_item">
-                <a href="#" onclick="$(this).closest('form').submit()" data-toggle="tooltip" data-placement="left" title="<?=$lang['user']['delete_now'];?>"><span class="glyphicon glyphicon-remove"></span></a>
-              <?php
-              else:
-              ?>
-                <span style="cursor:not-allowed"><?=$lang['user']['spamfilter_table_domain_policy'];?></span>
-              <?php
-              endif;
-              ?>
-            </div>
-            </form>
+				<h4><?=$lang['user']['spamfilter_bl'];?></h4>
+        <p><?=$lang['user']['spamfilter_bl_desc'];?></p>
+        <div class="table-responsive">
+          <table class="table table-striped table-condensed" id="bl_policy_mailbox_table"></table>
+        </div>
+        <div class="mass-actions-user">
+          <div class="btn-group">
+            <a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="policy_bl_mailbox" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
+            <a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_bl_mailbox" data-api-url='delete/policy_mailbox' href="#"><?=$lang['mailbox']['remove'];?></a></li>
+            </ul>
           </div>
-          <?php
-          endforeach;
-        endif;
-				?>
-				<hr style="margin:5px 0px 7px 0px">
-				<div class="row">
-					<form class="form-inline" method="post">
-					<div class="col-xs-6">
-						<input type="text" class="form-control input-sm" name="object_from" id="object_from" placeholder="*@example.org" required>
-						<input type="hidden" name="object_list" value="bl">
-					</div>
-					<div class="col-xs-6">
-						<button type="submit" id="add_policy_list_item" name="add_policy_list_item" class="btn btn-xs btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
-					</div>
-					</form>
-				</div>
-			</div>
-		</div>
-	</div>
-	<div role="tabpanel" class="tab-pane" id="TLSPolicy">
-		<form class="form-horizontal" role="form" method="post">
-      <input type="hidden" value="0" name="tls_in">
-      <input type="hidden" value="0" name="tls_out">
-			<p class="help-block"><?=$lang['user']['tls_policy_warning'];?></p>
-			<div class="form-group">
-				<div class="col-sm-6">
-					<div class="checkbox">
-						<h4><span class="glyphicon glyphicon-download" aria-hidden="true"></span> <?=$lang['user']['tls_enforce_in'];?></h4>
-						<input type="checkbox" value="1" id="tls_in" name="tls_in" <?=($get_tls_policy['tls_enforce_in'] == "1") ? "checked" : null;?> data-on-text="<?=$lang['user']['on'];?>" data-off-text="<?=$lang['user']['off'];?>">
-					</div>
-				</div>
-				<div class="col-sm-6">
-					<div class="checkbox">
-						<h4><span class="glyphicon glyphicon-upload" aria-hidden="true"></span> <?=$lang['user']['tls_enforce_out'];?></h4>
-						<input type="checkbox" value="1" id="tls_out" name="tls_out" <?=($get_tls_policy['tls_enforce_out'] == "1") ? "checked" : null;?> data-on-text="<?=$lang['user']['on'];?>" data-off-text="<?=$lang['user']['off'];?>">
-					</div>
-				</div>
-			</div>
-			<div class="form-group">
-				<div class="col-sm-12">
-					<button type="submit" id="edit_tls_policy" name="edit_tls_policy" class="btn btn-success"><?=$lang['user']['save_changes'];?></button>
-				</div>
-			</div>
-		</form>
-	</div>
+        </div>
+        <form class="form-inline" method="post">
+          <div class="input-group">
+            <input type="text" class="form-control" name="object_from" id="object_from" placeholder="*@example.org" required>
+            <input type="hidden" name="username" value="<?= $username ;?>">
+            <input type="hidden" name="object_list" value="bl">
+            <span class="input-group-btn">
+              <button type="submit" id="add_policy_list_item" name="add_policy_list_item" class="btn btn-default"><?=$lang['user']['spamfilter_table_add'];?></button>
+            </span>
+          </div>
+        </form>
+      </div>
+    </div>
+  </div>
+
 	<div role="tabpanel" class="tab-pane" id="Syncjobs">
 		<div class="table-responsive">
       <table class="table table-striped" id="sync_job_table"></table>