2
0
andryyy 8 жил өмнө
parent
commit
ce6bf18c2f

+ 0 - 403
data/web/add.php

@@ -1,403 +0,0 @@
-<?php
-require_once("inc/prerequisites.inc.php");
-$AuthUsers = array("admin", "domainadmin", "user");
-if (!isset($_SESSION['mailcow_cc_role']) OR !in_array($_SESSION['mailcow_cc_role'], $AuthUsers)) {
-	header('Location: /');
-	exit();
-}
-require_once("inc/header.inc.php");
-?>
-<div class="container">
-	<div class="row">
-		<div class="col-md-12">
-			<div class="panel panel-default">
-				<div class="panel-heading">
-					<h3 class="panel-title"><?=$lang['add']['title'];?></h3>
-				</div>
-				<div class="panel-body">
-<?php
-if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin"  || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
-	if (isset($_GET['domain']) && $_SESSION['mailcow_cc_role'] == "admin") {
-?>
-				<h4><?=$lang['add']['domain'];?></h4>
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
-					<input type="hidden" value="0" name="active">
-					<input type="hidden" value="0" name="backupmx">
-					<input type="hidden" value="0" name="relay_all_recipients">
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
-						<div class="col-sm-10">
-						<input type="text" autocorrect="off" autocapitalize="none" class="form-control" name="domain" id="domain">
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
-						<div class="col-sm-10">
-						<input type="text" class="form-control" name="description" id="description">
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="aliases"><?=$lang['add']['max_aliases'];?></label>
-						<div class="col-sm-10">
-						<input type="number" class="form-control" name="aliases" id="aliases" value="400">
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="mailboxes"><?=$lang['add']['max_mailboxes'];?></label>
-						<div class="col-sm-10">
-						<input type="number" class="form-control" name="mailboxes" id="mailboxes" value="10">
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="maxquota"><?=$lang['add']['mailbox_quota_m'];?></label>
-						<div class="col-sm-10">
-						<input type="number" class="form-control" name="maxquota" id="maxquota" value="3072">
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="quota"><?=$lang['add']['domain_quota_m'];?></label>
-						<div class="col-sm-10">
-						<input type="number" class="form-control" name="quota" id="quota" value="10240">
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2"><?=$lang['add']['backup_mx_options'];?></label>
-						<div class="col-sm-10">
-							<div class="checkbox">
-							<label><input type="checkbox" value="1" name="backupmx"> <?=$lang['add']['relay_domain'];?></label>
-							<br />
-							<label><input type="checkbox" value="1" name="relay_all_recipients"> <?=$lang['add']['relay_all'];?></label>
-							<p><?=$lang['add']['relay_all_info'];?></p>
-							</div>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<div class="checkbox">
-							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
-							</div>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="mailbox_add_domain" class="btn btn-success"><?=$lang['add']['save'];?></button>
-						</div>
-					</div>
-					<p><span class="glyphicon glyphicon-exclamation-sign text-danger"></span> <?=$lang['add']['restart_sogo_hint'];?></p>
-				</form>
-<?php
-	}
-	elseif (isset($_GET['alias'])) {
-?>
-				<h4><?=$lang['add']['alias'];?></h4>
-				<p><?=$lang['add']['alias_spf_fail'];?></p>
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
-					<input type="hidden" value="0" name="active">
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="address"><?=$lang['add']['alias_address'];?></label>
-						<div class="col-sm-10">
-							<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="5" name="address" id="address"></textarea>
-							<p><?=$lang['add']['alias_address_info'];?></p>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="goto"><?=$lang['add']['target_address'];?></label>
-						<div class="col-sm-10">
-							<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="5" id="goto" name="goto"></textarea>
-							<p><?=$lang['add']['target_address_info'];?></p>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<div class="checkbox">
-							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
-							</div>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="mailbox_add_alias" class="btn btn-success "><?=$lang['add']['save'];?></button>
-						</div>
-					</div>
-				</form>
-	<?php
-	}
-	elseif (isset($_GET['aliasdomain'])) {
-	?>
-				<h4><?=$lang['add']['alias_domain'];?></h4>
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
-					<input type="hidden" value="0" name="active">
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="alias_domain"><?=$lang['add']['alias_domain'];?></label>
-						<div class="col-sm-10">
-							<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="5" name="alias_domain" id="alias_domain"></textarea>
-							<p><?=$lang['add']['alias_domain_info'];?></p>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="target_domain"><?=$lang['add']['target_domain'];?></label>
-						<div class="col-sm-10">
-							<select name="target_domain" id="target_domain" title="<?=$lang['add']['select'];?>" required>
-							<?php
-              foreach (mailbox('get', 'domains') as $domain) {
-								echo "<option>".htmlspecialchars($domain)."</option>";
-							}
-							?>
-							</select>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<div class="checkbox">
-							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
-							</div>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="mailbox_add_alias_domain" class="btn btn-success "><?=$lang['add']['save'];?></button>
-						</div>
-					</div>
-				</form>
-	<?php
-	}
-	elseif (isset($_GET['mailbox'])) {
-	?>
-				<h4><?=$lang['add']['mailbox'];?></h4>
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
-					<input type="hidden" value="0" name="active">
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="local_part"><?=$lang['add']['mailbox_username'];?></label>
-						<div class="col-sm-10">
-							<input type="text" pattern="[A-Za-z0-9\.!#$%&'*+/=?^_`{|}~-]+" autocorrect="off" autocapitalize="none" class="form-control" name="local_part" id="local_part" required>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
-						<div class="col-sm-10">
-							<select id="addSelectDomain" name="domain" id="domain" required>
-							<?php
-              foreach (mailbox('get', 'domains') as $domain) {
-								echo "<option>".htmlspecialchars($domain)."</option>";
-							}
-							?>
-							</select>
-						</div>
-					</div> 
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="name"><?=$lang['add']['full_name'];?></label>
-						<div class="col-sm-10">
-						<input type="text" class="form-control" name="name" id="name">
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="addInputQuota"><?=$lang['add']['quota_mb'];?>
-							<br /><span id="quotaBadge" class="badge">max. - MiB</span>
-						</label>
-						<div class="col-sm-10">
-						<input type="text" class="form-control" name="quota" min="1" max="" id="addInputQuota" disabled value="<?=$lang['add']['select_domain'];?>" required>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="password"><?=$lang['add']['password'];?></label>
-						<div class="col-sm-10">
-						<input type="password" class="form-control" name="password" id="password" placeholder="">
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="password2"><?=$lang['add']['password_repeat'];?></label>
-						<div class="col-sm-10">
-						<input type="password" class="form-control" name="password2" id="password2" placeholder="">
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<div class="checkbox">
-							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
-							</div>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="mailbox_add_mailbox" class="btn btn-success "><?=$lang['add']['save'];?></button>
-						</div>
-					</div>
-				</form>
-	<?php
-	}
-	elseif (isset($_GET['resource'])) {
-	?>
-				<h4><?=$lang['add']['resource'];?></h4>
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
-					<input type="hidden" value="0" name="active">
-					<input type="hidden" value="0" name="multiple_bookings">
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
-						<div class="col-sm-10">
-							<input type="text" class="form-control" name="description" id="description" required>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
-						<div class="col-sm-10">
-							<select name="domain" id="domain" title="<?=$lang['add']['select'];?>" required>
-							<?php
-              foreach (mailbox('get', 'domains') as $domain) {
-								echo "<option>".htmlspecialchars($domain)."</option>";
-							}
-							?>
-							</select>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="domain"><?=$lang['add']['kind'];?>:</label>
-						<div class="col-sm-10">
-							<select name="kind" id="kind" title="<?=$lang['add']['select'];?>" required>
-								<option value="location">Location</option>
-								<option value="group">Group</option>
-								<option value="thing">Thing</option>
-							</select>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<div class="checkbox">
-							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
-							</div>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<div class="checkbox">
-							<label><input type="checkbox" value="1" name="multiple_bookings" checked> <?=$lang['add']['multiple_bookings'];?></label>
-							</div>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="mailbox_add_resource" class="btn btn-success "><?=$lang['add']['save'];?></button>
-						</div>
-					</div>
-				</form>
-	<?php
-	}
-	else {
-	?>
-				<div class="alert alert-info" role="alert"><?=$lang['info']['no_action'];?></div>
-	<?php
-	}
-}
-elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "user")) {
-  if (isset($_GET['syncjob'])) {
-?>
-				<h4><?=$lang['add']['syncjob'];?></h4>
-				<p><?=$lang['add']['syncjob_hint'];?></p>
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
-					<input type="hidden" value="0" name="active">
-					<input type="hidden" value="0" name="delete1">
-					<input type="hidden" value="0" name="delete2duplicates">
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="host1"><?=$lang['add']['hostname'];?></label>
-						<div class="col-sm-10">
-						<input type="text" class="form-control" name="host1" id="host1" required>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="port1"><?=$lang['add']['port'];?></label>
-						<div class="col-sm-10">
-						<input type="number" class="form-control" name="port1" id="port1" min="1" max="65535" value="143" required>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="user1"><?=$lang['add']['username'];?></label>
-						<div class="col-sm-10">
-						<input type="text" class="form-control" name="user1" id="user1" required>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="password1"><?=$lang['add']['password'];?></label>
-						<div class="col-sm-10">
-						<input type="text" class="form-control" name="password1" id="password1" required>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="enc1"><?=$lang['add']['enc_method'];?></label>
-						<div class="col-sm-10">
-							<select name="enc1" id="enc1" title="<?=$lang['add']['select'];?>" required>
-                <option selected>TLS</option>
-                <option>SSL</option>
-                <option>PLAIN</option>
-							</select>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="mins_interval"><?=$lang['add']['mins_interval'];?></label>
-						<div class="col-sm-10">
-              <input type="number" class="form-control" name="mins_interval" min="10" max="3600" value="20" required>
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="subfolder2"><?=$lang['edit']['subfolder2'];?></label>
-						<div class="col-sm-10">
-						<input type="text" class="form-control" name="subfolder2" id="subfolder2" value="External">
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label>
-						<div class="col-sm-10">
-						<input type="number" class="form-control" name="maxage" id="maxage" min="0" max="32000" value="0">
-						</div>
-					</div>
-					<div class="form-group">
-						<label class="control-label col-sm-2" for="exclude"><?=$lang['add']['exclude'];?></label>
-						<div class="col-sm-10">
-						<input type="text" class="form-control" name="exclude" id="exclude" value="(?i)spam|(?i)junk">
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<div class="checkbox">
-							<label><input type="checkbox" value="1" name="delete2duplicates" checked> <?=$lang['add']['delete2duplicates'];?></label>
-							</div>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<div class="checkbox">
-							<label><input type="checkbox" value="1" name="delete1"> <?=$lang['add']['delete1'];?></label>
-							</div>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<div class="checkbox">
-							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
-							</div>
-						</div>
-					</div>
-					<div class="form-group">
-						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="add_syncjob" class="btn btn-success "><?=$lang['add']['save'];?></button>
-						</div>
-					</div>
-				</form>
-	<?php
-	}
-}
-
-else {
-?>
-				<div class="alert alert-danger" role="alert"><?=$lang['danger']['access_denied'];?></div>
-<?php
-}
-?>
-				</div>
-			</div>
-		</div>
-	</div>
-<a href="<?=$_SESSION['return_to'];?>">&#8592; <?=$lang['add']['previous'];?></a>
-</div> <!-- /container -->
-<script src="js/add.js"></script>
-<?php
-require_once("inc/footer.inc.php");
-?>

+ 19 - 13
data/web/admin.php

@@ -176,7 +176,7 @@ $tfa_data = get_tfa();
         </div>
         <?php
         foreach(mailbox('get', 'domains') as $domain) {
-            if (!empty($dkim = dkim_get_key_details($domain))) {
+            if (!empty($dkim = dkim('details', $domain))) {
           ?>
             <div class="row">
               <div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$domain;?>" /></div>
@@ -204,8 +204,8 @@ $tfa_data = get_tfa();
           </div>
           <?php
           }
-          foreach(mailbox('get', 'alias_domains') as $alias_domain) {
-            if (!empty($dkim = dkim_get_key_details($alias_domain))) {
+          foreach(mailbox('get', 'alias_domains', $domain) as $alias_domain) {
+            if (!empty($dkim = dkim('details', $alias_domain))) {
             ?>
               <div class="row">
               <div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$alias_domain;?>" /></div>
@@ -235,8 +235,8 @@ $tfa_data = get_tfa();
             }
           }
         }
-        foreach(dkim_get_blind_keys() as $blind) {
-          if (!empty($dkim = dkim_get_key_details($blind))) {
+        foreach(dkim('blind') as $blind) {
+          if (!empty($dkim = dkim('details', $blind))) {
           ?>
             <div class="row">
               <div class="col-xs-1"><input type="checkbox" data-id="dkim" name="multi_select" value="<?=$blind;?>" /></div>
@@ -257,7 +257,7 @@ $tfa_data = get_tfa();
         ?>
 
         <legend style="margin-top:40px"><?=$lang['admin']['dkim_add_key'];?></legend>
-        <form class="form-inline" role="form" method="post">
+        <form class="form-inline" data-id="dkim" role="form" method="post">
           <div class="form-group">
             <label for="domain">Domain</label>
             <input class="form-control" id="domain" name="domain" placeholder="example.org" required>
@@ -272,7 +272,7 @@ $tfa_data = get_tfa();
               <option data-subtext="bits">2048</option>
             </select>
           </div>
-          <button type="submit" name="dkim_add_key" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
+          <button class="btn btn-default" id="add_item" data-id="dkim" data-api-url='add/dkim' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
         </form>
       </div>
     </div>
@@ -281,18 +281,24 @@ $tfa_data = get_tfa();
       <div class="panel-heading"><?=$lang['admin']['forwarding_hosts'];?></div>
       <div class="panel-body">
         <p style="margin-bottom:40px"><?=$lang['admin']['forwarding_hosts_hint'];?></p>
+        <div class="table-responsive">
+          <table class="table table-striped table-condensed" id="forwardinghoststable"></table>
+        </div>
         <div class="mass-actions-admin">
           <div class="btn-group btn-group-sm">
             <button type="button" id="toggle_multi_select_all" data-id="fwdhosts" class="btn btn-default"><?=$lang['mailbox']['toggle_all'];?></button>
-            <button type="button" id="delete_selected" name="delete_selected" data-id="fwdhosts" data-api-url="delete/fwdhost" class="btn btn-danger"><?=$lang['admin']['remove'];?></button>
+            <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="fwdhosts" data-api-url='edit/fwdhost' data-api-attr='{"keep_spam":"0"}' href="#">Enable spam filter</a></li>
+              <li><a id="edit_selected" data-id="fwdhosts" data-api-url='edit/fwdhost' data-api-attr='{"keep_spam":"1"}' href="#">Disable spam filter</a></li>
+              <li role="separator" class="divider"></li>
+              <li><a id="delete_selected" data-id="fwdhosts" data-api-url='delete/fwdhost' href="#"><?=$lang['admin']['remove'];?></a></li>
+            </ul>
           </div>
         </div>
-        <div class="table-responsive">
-          <table class="table table-striped" id="forwardinghoststable"></table>
-        </div>
         <legend><?=$lang['admin']['add_forwarding_host'];?></legend>
         <p class="help-block"><?=$lang['admin']['forwarding_hosts_add_hint'];?></p>
-        <form class="form-inline" role="form" method="post">
+        <form class="form-inline" data-id="fwdhost" role="form" method="post">
           <div class="form-group">
             <label for="hostname"><?=$lang['admin']['host'];?></label>
             <input class="form-control" id="hostname" name="hostname" placeholder="example.org" required>
@@ -303,7 +309,7 @@ $tfa_data = get_tfa();
               <option value="0"><?=$lang['admin']['inactive'];?></option>
             </select>
           </div>
-          <button type="submit" name="add_forwarding_host" class="btn btn-default"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
+          <button class="btn btn-default" id="add_item" data-id="fwdhost" data-api-url='add/fwdhost' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-plus"></span> <?=$lang['admin']['add'];?></button>
         </form>
       </div>
     </div>

+ 3 - 0
data/web/css/mailbox.css

@@ -27,3 +27,6 @@ table.footable>tbody>tr.footable-empty>td {
   user-select: none;
   padding:10px 0 10px 10px;
 }
+.inputMissingAttr {
+  border-color: #FF4136;
+}

+ 3 - 0
data/web/css/user.css

@@ -22,3 +22,6 @@ table.footable>tbody>tr.footable-empty>td {
   user-select: none;
   padding:10px 0 10px 0;
 }
+.inputMissingAttr {
+  border-color: #FF4136;
+}

+ 10 - 14
data/web/edit.php

@@ -208,12 +208,12 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 					</div>
 				</form>
 				<?php
-        if (!empty($dkim = dkim_get_key_details($domain))) {
+        if (!empty($dkim = dkim('details', $domain))) {
 				?>
         <hr>
         <div class="row">
           <div class="col-xs-2">
-            <p>Domain: <strong><?=htmlspecialchars($result['domain_name']);?></strong> (dkim._domainkey)</p>
+            <p>Domain: <strong><?=htmlspecialchars($result['domain_name']);?></strong> (<?=$dkim['dkim_selector'];?>._domainkey)</p>
           </div>
           <div class="col-xs-10">
             <pre><?=$dkim['dkim_txt'];?></pre>
@@ -233,17 +233,15 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
         <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>
+            <a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_wl_domain" data-api-url='delete/domain-policy' href="#"><?=$lang['mailbox']['remove'];?></a></li>
             </ul>
           </div>
         </div>
-        <form class="form-inline" method="post">
+        <form class="form-inline" data-id="add_wl_policy_domain">
           <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>
+              <button class="btn btn-default" id="add_item" data-id="add_wl_policy_domain" data-api-url='add/domain-policy' data-api-attr='{"domain":"<?= $domain; ?>","object_list":"wl"}' href="#"><?=$lang['user']['spamfilter_table_add'];?></button>
             </span>
           </div>
         </form>
@@ -257,17 +255,15 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
         <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>
+            <a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_bl_domain" data-api-url='delete/domain-policy' href="#"><?=$lang['mailbox']['remove'];?></a></li>
             </ul>
           </div>
         </div>
-        <form class="form-inline" method="post">
+        <form class="form-inline" data-id="add_bl_policy_domain">
           <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>
+              <button class="btn btn-default" id="add_item" data-id="add_bl_policy_domain" data-api-url='add/domain-policy' data-api-attr='{"domain":"<?= $domain; ?>","object_list":"bl"}' href="#"><?=$lang['user']['spamfilter_table_add'];?></button>
             </span>
           </div>
         </form>
@@ -312,12 +308,12 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 					</div>
 				</form>
 				<?php
-        if (!empty($dkim = dkim_get_key_details($alias_domain))) {
+        if (!empty($dkim = dkim('details', $alias_domain))) {
 				?>
         <hr>
         <div class="row">
           <div class="col-xs-2">
-            <p>Domain: <strong><?=htmlspecialchars($result['alias_domain']);?></strong> (dkim._domainkey)</p>
+            <p>Domain: <strong><?=htmlspecialchars($result['alias_domain']);?></strong> (<?=$dkim['dkim_selector'];?>._domainkey)</p>
           </div>
           <div class="col-xs-10">
           <pre><?=$dkim['dkim_txt'];?></pre>

+ 1 - 1
data/web/inc/footer.inc.php

@@ -1,5 +1,5 @@
 <?php
-include 'inc/tfa_modals.php';
+include 'modals/footer.php';
 
 if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin'):
 ?>

+ 151 - 0
data/web/inc/functions.dkim.inc.php

@@ -0,0 +1,151 @@
+<?php
+
+function dkim($_action, $_data = null) {
+	global $redis;
+	global $lang;
+  switch ($_action) {
+    case 'add':
+      if ($_SESSION['mailcow_cc_role'] != "admin") {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+      $key_length	= intval($_data['key_size']);
+      $dkim_selector = (isset($_data['dkim_selector'])) ? $_data['dkim_selector'] : 'dkim';
+      $domain	= $_data['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 ($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;
+      }
+    break;
+    case 'details':
+      if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+        return false;
+      }
+      $dkimdata = array();
+      if ($redis_dkim_key_data = $redis->hGet('DKIM_PUB_KEYS', $_data)) {
+        $dkimdata['pubkey'] = $redis_dkim_key_data;
+        $dkimdata['length'] = (strlen($dkimdata['pubkey']) < 391) ? 1024 : 2048;
+        $dkimdata['dkim_txt'] = 'v=DKIM1;k=rsa;t=s;s=email;p=' . $redis_dkim_key_data;
+        $dkimdata['dkim_selector'] = $redis->hGet('DKIM_SELECTORS', $_data);
+      }
+      return $dkimdata;
+    break;
+    case 'blind':
+      if ($_SESSION['mailcow_cc_role'] != "admin") {
+        return false;
+      }
+      $blinddkim = array();
+      foreach ($redis->hKeys('DKIM_PUB_KEYS') as $redis_dkim_domain) {
+        $blinddkim[] = $redis_dkim_domain;
+      }
+      return array_diff($blinddkim, array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')));
+    break;
+    case 'delete':
+      $domains = (array)$_data['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)))
+      );
+    break;
+  }
+}

+ 171 - 0
data/web/inc/functions.fwdhost.inc.php

@@ -0,0 +1,171 @@
+<?php
+
+function fwdhost($_action, $_data = null) {
+  require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/spf.inc.php';
+	global $redis;
+	global $lang;
+  switch ($_action) {
+    case 'add':
+      global $lang;
+      if ($_SESSION['mailcow_cc_role'] != "admin") {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+      $source = $_data['hostname'];
+      $host = trim($_data['hostname']);
+      $filter_spam = (isset($_data['filter_spam']) && $_data['filter_spam'] == 1) ? 1 : 0;
+      if (preg_match('/^[0-9a-fA-F:\/]+$/', $host)) { // IPv6 address
+        $hosts = array($host);
+      }
+      elseif (preg_match('/^[0-9\.\/]+$/', $host)) { // IPv4 address
+        $hosts = array($host);
+      }
+      else {
+        $hosts = get_outgoing_hosts_best_guess($host);
+      }
+      if (empty($hosts)) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'Invalid host specified: '. htmlspecialchars($host)
+        );
+        return false;
+      }
+      foreach ($hosts as $host) {
+        try {
+          $redis->hSet('WHITELISTED_FWD_HOST', $host, $source);
+          if ($filter_spam == 0) {
+            $redis->hSet('KEEP_SPAM', $host, 1);
+          }
+          elseif ($redis->hGet('KEEP_SPAM', $host)) {
+            $redis->hDel('KEEP_SPAM', $host);
+          }
+        }
+        catch (RedisException $e) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => 'Redis: '.$e
+          );
+          return false;
+        }
+      }
+      $_SESSION['return'] = array(
+        'type' => 'success',
+        'msg' => sprintf($lang['success']['forwarding_host_added'], htmlspecialchars(implode(', ', $hosts)))
+      );
+    break;
+    case 'edit':
+      global $lang;
+      if ($_SESSION['mailcow_cc_role'] != "admin") {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+      $fwdhosts = (array)$_data['fwdhost'];
+      foreach ($fwdhosts as $fwdhost) {
+        $is_now = fwdhost('details', $fwdhost);
+        if (!empty($is_now)) {
+          $keep_spam = (isset($_data['keep_spam'])) ? $_data['keep_spam'] : $is_now['keep_spam'];
+        }
+        else {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => sprintf($lang['danger']['access_denied'])
+          );
+          return false;
+        }
+        try {
+          if ($keep_spam == 1) {
+            $redis->hSet('KEEP_SPAM', $fwdhost, 1);
+          }
+          else {
+            $redis->hDel('KEEP_SPAM', $fwdhost);
+          }
+        }
+        catch (RedisException $e) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => 'Redis: '.$e
+          );
+          return false;
+        }
+      }
+      $_SESSION['return'] = array(
+        'type' => 'success',
+        'msg' => sprintf($lang['success']['object_modified'], htmlspecialchars(implode(', ', $fwdhosts)))
+      );
+    break;
+    case 'delete':
+      $hosts = (array)$_data['forwardinghost'];
+      foreach ($hosts as $host) {
+        try {
+          $redis->hDel('WHITELISTED_FWD_HOST', $host);
+          $redis->hDel('KEEP_SPAM', $host);
+        }
+        catch (RedisException $e) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => 'Redis: '.$e
+          );
+          return false;
+        }
+      }
+      $_SESSION['return'] = array(
+        'type' => 'success',
+        'msg' => sprintf($lang['success']['forwarding_host_removed'], htmlspecialchars(implode(', ', $hosts)))
+      );
+    break;
+    case 'get':
+      if ($_SESSION['mailcow_cc_role'] != "admin") {
+        return false;
+      }
+      $fwdhostsdata = array();
+      try {
+        $fwd_hosts = $redis->hGetAll('WHITELISTED_FWD_HOST');
+        if (!empty($fwd_hosts)) {
+        foreach ($fwd_hosts as $fwd_host => $source) {
+          $keep_spam = ($redis->hGet('KEEP_SPAM', $fwd_host)) ? "yes" : "no";
+          $fwdhostsdata[] = array(
+            'host' => $fwd_host,
+            'source' => $source,
+            'keep_spam' => $keep_spam
+          );
+        }
+        }
+      }
+      catch (RedisException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'Redis: '.$e
+        );
+        return false;
+      }
+      return $fwdhostsdata;
+    break;
+    case 'details':
+      $fwdhostdetails = array();
+      if (!isset($_data) || empty($_data)) {
+        return false;
+      }
+      try {
+        if ($source = $redis->hGet('WHITELISTED_FWD_HOST', $_data)) {
+          $fwdhostdetails['host'] = $_data;
+          $fwdhostdetails['source'] = $source;
+          $fwdhostdetails['keep_spam'] = ($redis->hGet('KEEP_SPAM', $_data)) ? "yes" : "no";
+        }
+      }
+      catch (RedisException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'Redis: '.$e
+        );
+        return false;
+      }
+      return $fwdhostdetails;
+    break;
+  }
+}

+ 0 - 305
data/web/inc/functions.inc.php

@@ -1310,317 +1310,12 @@ function get_admin_details() {
   }
   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 get_u2f_registrations($username) {
   global $pdo;
   $sel = $pdo->prepare("SELECT * FROM `tfa` WHERE `authmech` = 'u2f' AND `username` = ? AND `active` = '1'");
   $sel->execute(array($username));
   return $sel->fetchAll(PDO::FETCH_OBJ);
 }
-function get_forwarding_hosts() {
-	global $redis;
-  $data = array();
-  try {
-    $fwd_hosts = $redis->hGetAll('WHITELISTED_FWD_HOST');
-    if (!empty($fwd_hosts)) {
-      foreach ($fwd_hosts as $fwd_host => $source) {
-        $data[] = $fwd_host;
-      }
-    }
-  }
-  catch (RedisException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'Redis: '.$e
-		);
-		return false;
-  }
-  return $data;
-}
-function get_forwarding_host_details($host) {
-	global $redis;
-  $data = array();
-  if (!isset($host) || empty($host)) {
-    return false;
-  }
-  try {
-    if ($source = $redis->hGet('WHITELISTED_FWD_HOST', $host)) {
-      $data['host'] = $host;
-      $data['source'] = $source;
-      $data['keep_spam'] = ($redis->hGet('KEEP_SPAM', $host)) ? "yes" : "no";
-    }
-  }
-  catch (RedisException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'Redis: '.$e
-		);
-		return false;
-  }
-  return $data;
-}
-function add_forwarding_host($postarray) {
-	require_once 'spf.inc.php';
-	global $redis;
-	global $lang;
-	if ($_SESSION['mailcow_cc_role'] != "admin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-	$source = $postarray['hostname'];
-	$host = trim($postarray['hostname']);
-  $filter_spam = $postarray['filter_spam'];
-  if (isset($postarray['filter_spam']) && $postarray['filter_spam'] == 1) {
-    $filter_spam = 1;
-  }
-  else {
-    $filter_spam = 0;
-  }
-	if (preg_match('/^[0-9a-fA-F:\/]+$/', $host)) { // IPv6 address
-		$hosts = array($host);
-	}
-	elseif (preg_match('/^[0-9\.\/]+$/', $host)) { // IPv4 address
-		$hosts = array($host);
-	}
-	else {
-		$hosts = get_outgoing_hosts_best_guess($host);
-	}
-	if (empty($hosts)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'Invalid host specified: '. htmlspecialchars($host)
-		);
-		return false;
-	}
-	foreach ($hosts as $host) {
-    try {
-      $redis->hSet('WHITELISTED_FWD_HOST', $host, $source);
-      if ($filter_spam == 0) {
-        $redis->hSet('KEEP_SPAM', $host, 1);
-      }
-      elseif ($redis->hGet('KEEP_SPAM', $host)) {
-        $redis->hDel('KEEP_SPAM', $host);
-      }
-    }
-    catch (RedisException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'Redis: '.$e
-      );
-      return false;
-    }
-	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['forwarding_host_added'], htmlspecialchars(implode(', ', $hosts)))
-	);
-}
-function delete_forwarding_host($postarray) {
-	global $redis;
-	global $lang;
-	if ($_SESSION['mailcow_cc_role'] != "admin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-  if (!is_array($postarray['forwardinghost'])) {
-    $hosts = array();
-    $hosts[] = $postarray['forwardinghost'];
-  }
-  else {
-    $hosts = $postarray['forwardinghost'];
-  }
-  foreach ($hosts as $host) {
-    try {
-      $redis->hDel('WHITELISTED_FWD_HOST', $host);
-      $redis->hDel('KEEP_SPAM', $host);
-    }
-    catch (RedisException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'Redis: '.$e
-      );
-      return false;
-    }
-  }
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['forwarding_host_removed'], htmlspecialchars(implode(', ', $hosts)))
-	);
-}
 function get_logs($container, $lines = 100) {
 	global $lang;
 	global $redis;

+ 3 - 9
data/web/inc/functions.mailbox.inc.php

@@ -648,7 +648,7 @@ function mailbox($_action, $_type, $_data = null) {
             if ($num_results == 0) {
               $_SESSION['return'] = array(
                 'type' => 'danger',
-                'msg' => sprintf($lang['danger']['domain_not_found'], $domain)
+                'msg' => sprintf($lang['danger']['domain_not_found'], htmlspecialchars($domain))
               );
               return false;
             }
@@ -832,7 +832,7 @@ function mailbox($_action, $_type, $_data = null) {
             if ($num_results == 0) {
               $_SESSION['return'] = array(
                 'type' => 'danger',
-                'msg' => sprintf($lang['danger']['domain_not_found'], $domain)
+                'msg' => sprintf($lang['danger']['domain_not_found'], htmlspecialchars($domain))
               );
               return false;
             }
@@ -875,13 +875,7 @@ function mailbox($_action, $_type, $_data = null) {
     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'];
-          }
+          $alias_domains = (array)$_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);

+ 1 - 7
data/web/inc/functions.policy.inc.php

@@ -168,13 +168,7 @@ function policy($_action, $_scope, $_data = null) {
     case 'delete':
       switch ($_scope) {
         case 'domain':
-          if (!is_array($_data['prefid'])) {
-            $prefids = array();
-            $prefids[] = $_data['prefid'];
-          }
-          else {
-            $prefids = $_data['prefid'];
-          }
+          (array)$prefids = $_data['prefid'];
           foreach ($prefids as $prefid) {
             if (!is_numeric($prefid)) {
               $_SESSION['return'] = array(

+ 3 - 1
data/web/inc/prerequisites.inc.php

@@ -14,7 +14,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/lib/vendor/autoload.php';
 
 // U2F API + T/HOTP API
 $u2f = new u2flib_server\U2F('https://' . $_SERVER['HTTP_HOST']);
-$tfa = new RobThree\Auth\TwoFactorAuth('mailcow UI');
+$tfa = new RobThree\Auth\TwoFactorAuth($OTP_LABEL);
 
 // Redis
 $redis = new Redis();
@@ -60,6 +60,8 @@ include $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.
 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/functions.dkim.inc.php';
+require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fwdhost.inc.php';
 require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/init_db.inc.php';
 require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/triggers.inc.php';
 init_db_schema();

+ 0 - 40
data/web/inc/triggers.inc.php

@@ -53,33 +53,11 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
       }
     }
   }
-
-	if (isset($_POST["edit_admin_account"])) {
-		edit_admin_account($_POST);
-	}
-	if (isset($_POST["dkim_delete_key"])) {
-		dkim_delete_key($_POST);
-	}
-	if (isset($_POST["dkim_add_key"])) {
-		dkim_add_key($_POST);
-	}
-	if (isset($_POST["add_forwarding_host"])) {
-		add_forwarding_host($_POST);
-	}
-	if (isset($_POST["delete_forwarding_host"])) {
-		delete_forwarding_host($_POST);
-	}
 }
 if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "user") {
 	if (isset($_POST["edit_user_account"])) {
 		edit_user_account($_POST);
 	}
-	if (isset($_POST["add_policy_list_item"])) {
-		policy('add', 'mailbox', $_POST);
-	}
-	if (isset($_POST["add_syncjob"])) {
-		mailbox('add', 'syncjob', $_POST);
-	}
 	if (isset($_POST["edit_syncjob"])) {
 		mailbox('edit', 'syncjob', $_POST);
 	}
@@ -94,24 +72,6 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 	if (isset($_POST["unset_tfa_key"])) {
 		unset_tfa_key($_POST);
 	}
-	if (isset($_POST["add_policy_list_item"])) {
-		policy('add', 'domain', $_POST);
-	}
-	if (isset($_POST["mailbox_add_domain"])) {
-		mailbox('add', 'domain', $_POST);
-	}
-	if (isset($_POST["mailbox_add_alias"])) {
-		mailbox('add', 'alias', $_POST);
-	}
-	if (isset($_POST["mailbox_add_alias_domain"])) {
-		mailbox('add', 'alias_domain', $_POST);
-	}
-	if (isset($_POST["mailbox_add_mailbox"])) {
-		mailbox('add', 'mailbox', $_POST);
-	}
-	if (isset($_POST["mailbox_add_resource"])) {
-		mailbox('add', 'resource', $_POST);
-	}
 	if (isset($_POST["mailbox_edit_alias"])) {
 		mailbox('edit', 'alias', $_POST);
 	}

+ 3 - 5
data/web/inc/vars.inc.php

@@ -1,7 +1,6 @@
 <?php
 error_reporting(E_ERROR);
 //error_reporting(E_ALL);
-header('X-Powered-By: mailcow');
 
 /*
 PLEASE USE THE FILE "vars.local.inc.php" TO OVERWRITE SETTINGS AND MAKE THEM PERSISTENT!
@@ -23,10 +22,6 @@ $mailcow_hostname = getenv('MAILCOW_HOSTNAME');
 // "form" will stay in the current form, "previous" will redirect to previous page
 $FORM_ACTION = 'previous';
 
-// File locations should not be changed
-$MC_DKIM_TXTS = '/data/dkim/txt';
-$MC_DKIM_KEYS = '/data/dkim/keys';
-
 // Change default language, "de", "en", "es", "nl", "pt", "ru"
 $DEFAULT_LANG = 'en';
 
@@ -59,3 +54,6 @@ $PAGINATION_SIZE = 10;
 
 // Session lifetime in seconds
 $SESSION_LIFETIME = 3600;
+
+// Label for OTP devices
+$OTP_LABEL = "mailcow UI";

+ 1 - 3
data/web/js/add.js

@@ -1,5 +1,5 @@
 $(document).ready(function() {
-
+  // Auto-fill domain quota when adding new domain
   auto_fill_quota = function(domain) {
 		$.get("/api/v1/get/domain/" + domain, function(data){
       var result = $.parseJSON(JSON.stringify(data));
@@ -16,10 +16,8 @@ $(document).ready(function() {
 			}
 		});
   }
-
 	$('#addSelectDomain').on('change', function() {
     auto_fill_quota($('#addSelectDomain').val());
 	});
-
   auto_fill_quota($('#addSelectDomain').val());
 });

+ 0 - 4
data/web/js/admin.js

@@ -221,7 +221,6 @@ jQuery(function($){
         {"name":"host","type":"text","title":lang.host,"style":{"width":"250px"}},
         {"name":"source","title":lang.source,"breakpoints":"xs sm"},
         {"name":"keep_spam","title":lang.spamfilter, "type": "text","style":{"maxWidth":"80px","width":"80px"}},
-        {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
       ],
       "rows": $.ajax({
         dataType: 'json',
@@ -232,9 +231,6 @@ jQuery(function($){
         },
         success: function (data) {
           $.each(data, function (i, item) {
-            item.action = '<div class="btn-group">' +
-              '<a href="/delete.php?forwardinghost=' + encodeURI(item.host) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
-              '</div>';
             if (item.keep_spam == "yes") {
               item.keep_spam = lang.no;
             }

+ 54 - 30
data/web/js/api.js

@@ -44,7 +44,6 @@ $(document).ready(function() {
   $(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();
   });
@@ -71,15 +70,18 @@ $(document).ready(function() {
     }
     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 },
+        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) {
+        complete: function(data) {
           // var reponse = (JSON.parse(data.responseText));
           // console.log(reponse.type);
           // console.log(reponse.msg);
@@ -98,16 +100,34 @@ $(document).ready(function() {
     // 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 = $(this).closest("form").serializeObject();
-      var api_attr = $.extend(api_attr, attr_to_merge)
+      var req_empty = false;
+      $(this).closest("form").find('select, textarea, input').each(function() {
+        if ($(this).prop('required')) {
+          if (!$(this).val()) {
+            req_empty = true;
+            $(this).addClass('inputMissingAttr');
+          } else {
+            $(this).removeClass('inputMissingAttr');
+          }
+        }
+      });
+      if (!req_empty) {
+        var attr_to_merge = $(this).closest("form").serializeObject();
+        var api_attr = $.extend(api_attr, attr_to_merge)
+      } else {
+        return false;
+      }
     }
     $.ajax({
       type: "POST",
       dataType: "json",
-      data: { "attr": JSON.stringify(api_attr), "csrf_token": csrf_token },
+      data: {
+        "attr": JSON.stringify(api_attr),
+        "csrf_token": csrf_token
+      },
       url: '/api/v1/' + api_url,
       jsonp: false,
-      complete: function (data) {
+      complete: function(data) {
         // var reponse = (JSON.parse(data.responseText));
         // console.log(reponse.type);
         // console.log(reponse.msg);
@@ -115,6 +135,7 @@ $(document).ready(function() {
       }
     });
   });
+
   // General API delete actions
   $(document).on('click', '#delete_selected', function(e) {
     e.preventDefault();
@@ -125,7 +146,7 @@ $(document).ready(function() {
       if (typeof multi_data[id] == "undefined") {
         multi_data[id] = [];
       }
-      multi_data[id].splice($.inArray($(this).data('item'), multi_data[id]),1);
+      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') {
@@ -135,33 +156,36 @@ $(document).ready(function() {
     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 () {
+    $(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",
-        cache: false,
-        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];
-        }
+        backdrop: 'static',
+        keyboard: false
+      })
+      .one('click', '#IsConfirmed', function(e) {
+        $.ajax({
+          type: "POST",
+          dataType: "json",
+          cache: false,
+          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) {
+        // Remove event handler to allow to close modal and restart dialog without multiple submits
+        $('#ConfirmDeleteModal').off();
+        $('#ConfirmDeleteModal').modal('hide');
       });
-    })
-    .one('click', '#isCanceled', function(e) {
-      // Remove event handler to allow to close modal and restart dialog without multiple submits
-      $('#ConfirmDeleteModal').off();
-      $('#ConfirmDeleteModal').modal('hide');
-    });
   });
 });

+ 24 - 0
data/web/js/mailbox.js

@@ -1,3 +1,27 @@
+$(document).ready(function() {
+  // Auto-fill domain quota when adding new domain
+  auto_fill_quota = function(domain) {
+		$.get("/api/v1/get/domain/" + domain, function(data){
+      var result = $.parseJSON(JSON.stringify(data));
+      max_new_mailbox_quota = ( result.max_new_mailbox_quota / 1048576);
+			if (max_new_mailbox_quota != '0') {
+				$("#quotaBadge").html('max. ' +  max_new_mailbox_quota + ' MiB');
+				$('#addInputQuota').attr({"disabled": false, "value": "", "type": "number", "max": max_new_mailbox_quota});
+				$('#addInputQuota').val(max_new_mailbox_quota);
+			}
+			else {
+				$("#quotaBadge").html('max. ' + max_new_mailbox_quota + ' MiB');
+				$('#addInputQuota').attr({"disabled": true, "value": "", "type": "text", "value": "n/a"});
+				$('#addInputQuota').val(max_new_mailbox_quota);
+			}
+		});
+  }
+	$('#addSelectDomain').on('change', function() {
+    auto_fill_quota($('#addSelectDomain').val());
+	});
+  auto_fill_quota($('#addSelectDomain').val());
+});
+
 jQuery(function($){
   // Calculation human readable file sizes
   function humanFileSize(bytes) {

+ 433 - 36
data/web/json_api.php

@@ -60,6 +60,336 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               ));
             }
           break;
+          case "mailbox":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (mailbox('add', 'mailbox', $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;
+          case "domain":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (mailbox('add', 'domain', $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;
+          case "resource":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (mailbox('add', 'resource', $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;
+          case "alias":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (mailbox('add', '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;
+          case "syncjob":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (mailbox('add', 'syncjob', $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;
+          case "domain-policy":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (policy('add', 'domain', $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;
+          case "mailbox-policy":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (policy('add', 'mailbox', $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;
+          case "alias-domain":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (mailbox('add', 'alias_domain', $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;
+          case "fwdhost":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (fwdhost('add', $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;
+          case "dkim":
+            if (isset($_POST['attr'])) {
+              $attr = (array)json_decode($_POST['attr'], true);
+              if (dkim('add', $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;
           case "domain_admin":
             if (isset($_POST['attr'])) {
               $attr = (array)json_decode($_POST['attr'], true);
@@ -353,17 +683,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
           case "fwdhost":
             switch ($object) {
               case "all":
-                $fwdhosts = get_forwarding_hosts();
-                if (!empty($fwdhosts)) {
-                  foreach ($fwdhosts as $fwdhost) {
-                    if ($details = get_forwarding_host_details($fwdhost)) {
-                      $data[] = $details;
-                    }
-                    else {
-                      continue;
-                    }
-                  }
-                }
+                $data = fwdhost('get');
                 if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
@@ -372,7 +692,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
                 }
               break;
               default:
-                $data = get_forwarding_host_details($object);
+                $data = fwdhost('details', $object);
                 if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
@@ -385,34 +705,26 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
           case "alias-domain":
             switch ($object) {
               case "all":
-                $domains = mailbox('get', 'domains');
-                if (!empty($domains)) {
-                  foreach ($domains as $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)) {
-                          $data[] = $details;
-                        }
-                        else {
-                          continue;
-                        }
-                      }
+                $alias_domains = mailbox('get', 'alias_domains');
+                if (!empty($alias_domains)) {
+                  foreach ($alias_domains as $alias_domain) {
+                    if ($details = mailbox('get', 'alias_domain_details', $alias_domain)) {
+                      $data[] = $details;
+                    }
+                    else {
+                      continue;
                     }
-                  }
-                  if (!isset($data) || empty($data)) {
-                    echo '{}';
-                  }
-                  else {
-                    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
                   }
                 }
-                else {
+                if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
+                else {
+                  echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
+                }
               break;
               default:
-                $data = mailbox('get', 'alias_domains', $object);
+                $data = mailbox('get', 'alias_domain_details', $object);
                 if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
@@ -615,7 +927,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 (delete_forwarding_host(array('forwardinghost' => $items)) === false) {
+                if (fwdhost('delete', array('forwardinghost' => $items)) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -656,7 +968,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 (dkim_delete_key(array('domains' => $items)) === false) {
+                if (dkim('delete', array('domains' => $items)) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -857,7 +1169,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               ));
             }
           break;
-          case "policy_mailbox":
+          case "mailbox-policy":
             if (isset($_POST['items'])) {
               $items = (array)json_decode($_POST['items'], true);
               if (is_array($items)) {
@@ -898,6 +1210,47 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               ));
             }
           break;
+          case "domain-policy":
+            if (isset($_POST['items'])) {
+              $items = (array)json_decode($_POST['items'], true);
+              if (is_array($items)) {
+                if (policy('delete', 'domain', 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);
@@ -1509,6 +1862,50 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               ));
             }
           break;
+          case "fwdhost":
+            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('fwdhost' => $items), $attr);
+              if (is_array($postarray['fwdhost'])) {
+                if (fwdhost('edit', $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 "admin":
             // No items as there is only one admin
             if (isset($_POST['attr'])) {

+ 3 - 3
data/web/lang/lang.en.php

@@ -20,7 +20,7 @@ $lang['danger']['dkim_remove_failed'] = "Cannot remove selected DKIM key";
 $lang['danger']['dkim_add_failed'] = "Cannot add given DKIM key";
 $lang['danger']['dkim_domain_or_sel_invalid'] = "DKIM domain or selector invalid";
 $lang['danger']['dkim_key_length_invalid'] = "DKIM key length invalid";
-$lang['success']['dkim_removed'] = "DKIM key has been removed";
+$lang['success']['dkim_removed'] = "DKIM key %s has been removed";
 $lang['success']['dkim_added'] = "DKIM key has been saved";
 $lang['danger']['access_denied'] = "Access denied or invalid form data";
 $lang['danger']['whitelist_from_invalid'] = "Whitelist entry invalid";
@@ -77,7 +77,7 @@ $lang['danger']['is_alias'] = "%s is already known as an alias address";
 $lang['danger']['is_alias_or_mailbox'] = "%s is already known as an alias or a mailbox";
 $lang['danger']['is_spam_alias'] = "%s is already known as a spam alias address";
 $lang['danger']['quota_not_0_not_numeric'] = "Quota must be numeric and >= 0";
-$lang['danger']['domain_not_found'] = 'Domain "%s" not found';
+$lang['danger']['domain_not_found'] = 'Domain %s not found';
 $lang['danger']['max_mailbox_exceeded'] = "Max. mailboxes exceeded (%d of %d)";
 $lang['danger']['max_alias_exceeded'] = 'Max. aliases exceeded';
 $lang['danger']['mailbox_quota_exceeded'] = "Quota exceeds the domain limit (max. %d MiB)";
@@ -260,7 +260,7 @@ $lang['mailbox']['add_alias'] = 'Add alias';
 $lang['mailbox']['add_domain_record_first'] = 'Please add a domain first';
 $lang['mailbox']['empty'] = 'No results';
 $lang['mailbox']['toggle_all'] = 'Toggle all';
-$lang['mailbox']['quick_actions'] = 'Quick actions';
+$lang['mailbox']['quick_actions'] = 'Actions';
 $lang['mailbox']['activate'] = 'Activate';
 $lang['mailbox']['deactivate'] = 'Deactivate';
 

+ 16 - 11
data/web/mailbox.php

@@ -31,7 +31,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
             <?php
             if ($_SESSION['mailcow_cc_role'] == "admin"):
             ?>
-            <a href="/add.php?domain"><span class="glyphicon glyphicon-plus"></span></a></li>
+            <a href="#" data-toggle="modal" data-target="#addDomainModal" ><span class="glyphicon glyphicon-plus"></span></a>
             <?php
             endif;
             ?>
@@ -53,7 +53,8 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
                 </ul>
               </div>
             </div>
-            <span class="footer-add-item"><a href="/add.php?domain"><?=$lang['mailbox']['add_domain'];?></a></span>
+            <span class="footer-add-item"><a href="#" data-toggle="modal" data-target="#addDomainModal" ><?=$lang['mailbox']['add_domain'];?></a></span>
+            
           </div>
         </div>
 
@@ -61,7 +62,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
           <div class="panel panel-default">
             <div class="panel-heading">
               <div class="pull-right">
-                <a href="/add.php?mailbox"><span class="glyphicon glyphicon-plus"></span></a>
+                <a href="#" data-toggle="modal" data-target="#addMailboxModal" ><span class="glyphicon glyphicon-plus"></span></a>
               </div>
               <h3 class="panel-title"><?=$lang['mailbox']['mailboxes'];?></h3>
             </div>
@@ -80,7 +81,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
                 </ul>
               </div>
             </div>
-            <span class="footer-add-item"><a href="/add.php?mailbox"><?=$lang['mailbox']['add_mailbox'];?></a></span>
+            <span class="footer-add-item"><a href="#" data-toggle="modal" data-target="#addMailboxModal" ><?=$lang['mailbox']['add_mailbox'];?></a></span>
           </div>
         </div>
 
@@ -88,7 +89,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
           <div class="panel panel-default">
             <div class="panel-heading">
               <div class="pull-right">
-                <a href="/add.php?resource"><span class="glyphicon glyphicon-plus"></span></a>
+                <a href="#" data-toggle="modal" data-target="#addResourceModal" ><span class="glyphicon glyphicon-plus"></span></a>
               </div>
               <h3 class="panel-title"><?=$lang['mailbox']['resources'];?></h3>
             </div>
@@ -107,7 +108,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
                 </ul>
               </div>
             </div>
-            <span class="footer-add-item"><a href="/add.php?resource"><?=$lang['mailbox']['add_resource'];?></a></span>
+            <span class="footer-add-item"><a href="#" data-toggle="modal" data-target="#addResourceModal" ><?=$lang['mailbox']['add_resource'];?></a></span>
           </div>
         </div>
 
@@ -115,7 +116,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
           <div class="panel panel-default">
             <div class="panel-heading">
               <div class="pull-right">
-                <a href="/add.php?aliasdomain"><span class="glyphicon glyphicon-plus"></span></a>
+                <a href="#" data-toggle="modal" data-target="#addAliasDomainModal" ><span class="glyphicon glyphicon-plus"></span></a>
               </div>
               <h3 class="panel-title"><?=$lang['mailbox']['domain_aliases'];?></h3>
             </div>
@@ -134,14 +135,16 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
                 </ul>
               </div>
             </div>
-            <span class="footer-add-item"><a href="/add.php?aliasdomain"><?=$lang['mailbox']['add_domain_alias'];?></a></span>
+            <span class="footer-add-item"><a href="#" data-toggle="modal" data-target="#addAliasDomainModal" ><?=$lang['mailbox']['add_domain_alias'];?></a></span>
           </div>
         </div>
 
         <div role="tabpanel" class="tab-pane" id="tab-mbox-aliases">
           <div class="panel panel-default">
             <div class="panel-heading">
-              <a class="pull-right" href="/add.php?alias"><span class="glyphicon glyphicon-plus"></span></a>
+              <div class="pull-right">
+                <a href="#" data-toggle="modal" data-target="#addAliasModal" ><span class="glyphicon glyphicon-plus"></span></a>
+              </div>
               <h3 class="panel-title"><?=$lang['mailbox']['aliases'];?></h3>
             </div>
             <div class="table-responsive">
@@ -159,7 +162,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
                 </ul>
               </div>
             </div>
-            <span class="footer-add-item"><a href="/add.php?alias"><?=$lang['mailbox']['add_alias'];?></a></span>
+            <span class="footer-add-item"><a href="#" data-toggle="modal" data-target="#addAliasModal" ><?=$lang['mailbox']['add_alias'];?></a></span>
           </div>
         </div>
 
@@ -167,7 +170,9 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
     </div> <!-- /col-md-12 -->
   </div> <!-- /row -->
 </div> <!-- /container -->
-
+<?php
+require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/mailbox.php';
+?>
 <script type='text/javascript'>
 <?php
 $lang_mailbox = json_encode($lang['mailbox']);

+ 0 - 0
data/web/inc/tfa_modals.php → data/web/modals/footer.php


+ 303 - 0
data/web/modals/mailbox.php

@@ -0,0 +1,303 @@
+<?php
+if (!isset($_SESSION['mailcow_cc_role'])) {
+	header('Location: /');
+	exit();
+}
+?>
+<!-- add mailbox modal -->
+<div class="modal fade" id="addMailboxModal" tabindex="-1" role="dialog" aria-hidden="true">
+  <div class="modal-dialog modal-lg">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
+        <h3 class="modal-title"><?=$lang['mailbox']['add_mailbox'];?></h3>
+      </div>
+      <div class="modal-body">
+        <form class="form-horizontal" data-id="add_mailbox" role="form">
+          <div class="form-group">
+            <label class="control-label col-sm-2" for="local_part"><?=$lang['add']['mailbox_username'];?></label>
+            <div class="col-sm-10">
+              <input type="text" pattern="[A-Za-z0-9\.!#$%&'*+/=?^_`{|}~-]+" autocorrect="off" autocapitalize="none" class="form-control" name="local_part" id="local_part" required>
+            </div>
+          </div>
+          <div class="form-group">
+            <label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
+            <div class="col-sm-10">
+              <select id="addSelectDomain" name="domain" id="domain" required>
+              <?php
+              foreach (mailbox('get', 'domains') as $domain) {
+                echo "<option>".htmlspecialchars($domain)."</option>";
+              }
+              ?>
+              </select>
+            </div>
+          </div> 
+          <div class="form-group">
+            <label class="control-label col-sm-2" for="name"><?=$lang['add']['full_name'];?></label>
+            <div class="col-sm-10">
+            <input type="text" class="form-control" name="name" id="name">
+            </div>
+          </div>
+          <div class="form-group">
+            <label class="control-label col-sm-2" for="addInputQuota"><?=$lang['add']['quota_mb'];?>
+              <br /><span id="quotaBadge" class="badge">max. - MiB</span>
+            </label>
+            <div class="col-sm-10">
+            <input type="text" class="form-control" name="quota" min="1" max="" id="addInputQuota" disabled value="<?=$lang['add']['select_domain'];?>" required>
+            </div>
+          </div>
+          <div class="form-group">
+            <label class="control-label col-sm-2" for="password"><?=$lang['add']['password'];?></label>
+            <div class="col-sm-10">
+            <input type="password" class="form-control" name="password" id="password" placeholder="" required>
+            </div>
+          </div>
+          <div class="form-group">
+            <label class="control-label col-sm-2" for="password2"><?=$lang['add']['password_repeat'];?></label>
+            <div class="col-sm-10">
+            <input type="password" class="form-control" name="password2" id="password2" placeholder="" required>
+            </div>
+          </div>
+          <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+              <div class="checkbox">
+              <label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
+              </div>
+            </div>
+          </div>
+          <div class="form-group">
+            <div class="col-sm-offset-2 col-sm-10">
+              <button class="btn btn-default" id="add_item" data-id="add_mailbox" data-api-url='add/mailbox' data-api-attr='{}' href="#"><?=$lang['admin']['add'];?></button>
+            </div>
+          </div>
+        </form>
+      </div>
+    </div>
+  </div>
+</div><!-- add mailbox modal -->
+<!-- add domain modal -->
+<div class="modal fade" id="addDomainModal" tabindex="-1" role="dialog" aria-hidden="true">
+  <div class="modal-dialog modal-lg">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
+        <h3 class="modal-title"><?=$lang['mailbox']['add_domain'];?></h3>
+      </div>
+      <div class="modal-body">
+				<form class="form-horizontal" data-id="add_domain" role="form">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
+						<div class="col-sm-10">
+						<input type="text" autocorrect="off" autocapitalize="none" class="form-control" name="domain" id="domain" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="description" id="description">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="aliases"><?=$lang['add']['max_aliases'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="aliases" id="aliases" value="400" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="mailboxes"><?=$lang['add']['max_mailboxes'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="mailboxes" id="mailboxes" value="10" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="maxquota"><?=$lang['add']['mailbox_quota_m'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="maxquota" id="maxquota" value="3072" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="quota"><?=$lang['add']['domain_quota_m'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="quota" id="quota" value="10240" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2"><?=$lang['add']['backup_mx_options'];?></label>
+						<div class="col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" value="1" name="backupmx"> <?=$lang['add']['relay_domain'];?></label>
+							<br />
+							<label><input type="checkbox" value="1" name="relay_all_recipients"> <?=$lang['add']['relay_all'];?></label>
+							<p><?=$lang['add']['relay_all_info'];?></p>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+              <button class="btn btn-default" id="add_item" data-id="add_domain" data-api-url='add/domain' data-api-attr='{}' href="#"><?=$lang['admin']['add'];?></button>
+						</div>
+					</div>
+					<p><span class="glyphicon glyphicon-exclamation-sign text-danger"></span> <?=$lang['add']['restart_sogo_hint'];?></p>
+				</form>
+      </div>
+    </div>
+  </div>
+</div><!-- add domain modal -->
+<!-- add resource modal -->
+<div class="modal fade" id="addResourceModal" tabindex="-1" role="dialog" aria-hidden="true">
+  <div class="modal-dialog modal-lg">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
+        <h3 class="modal-title"><?=$lang['mailbox']['add_resource'];?></h3>
+      </div>
+      <div class="modal-body">
+				<form class="form-horizontal" role="form" data-id="add_resource">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
+						<div class="col-sm-10">
+							<input type="text" class="form-control" name="description" id="description" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
+						<div class="col-sm-10">
+							<select name="domain" id="domain" title="<?=$lang['add']['select'];?>" required>
+							<?php
+              foreach (mailbox('get', 'domains') as $domain) {
+								echo "<option>".htmlspecialchars($domain)."</option>";
+							}
+							?>
+							</select>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="domain"><?=$lang['add']['kind'];?>:</label>
+						<div class="col-sm-10">
+							<select name="kind" id="kind" title="<?=$lang['add']['select'];?>" required>
+								<option value="location">Location</option>
+								<option value="group">Group</option>
+								<option value="thing">Thing</option>
+							</select>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" value="1" name="multiple_bookings" checked> <?=$lang['add']['multiple_bookings'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+              <button class="btn btn-default" id="add_item" data-id="add_resource" data-api-url='add/resource' data-api-attr='{}' href="#"><?=$lang['admin']['add'];?></button>
+						</div>
+					</div>
+				</form>
+      </div>
+    </div>
+  </div>
+</div><!-- add resource modal -->
+<!-- add alias modal -->
+<div class="modal fade" id="addAliasModal" tabindex="-1" role="dialog" aria-hidden="true">
+  <div class="modal-dialog modal-lg">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
+        <h3 class="modal-title"><?=$lang['mailbox']['add_alias'];?></h3>
+      </div>
+      <div class="modal-body">
+				<form class="form-horizontal" role="form" data-id="add_alias">
+					<input type="hidden" value="0" name="active">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="address"><?=$lang['add']['alias_address'];?></label>
+						<div class="col-sm-10">
+							<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="5" name="address" id="address" required></textarea>
+							<p><?=$lang['add']['alias_address_info'];?></p>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="goto"><?=$lang['add']['target_address'];?></label>
+						<div class="col-sm-10">
+							<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="5" id="goto" name="goto" required></textarea>
+							<p><?=$lang['add']['target_address_info'];?></p>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+              <button class="btn btn-default" id="add_item" data-id="add_alias" data-api-url='add/alias' data-api-attr='{}' href="#"><?=$lang['admin']['add'];?></button>
+						</div>
+					</div>
+				</form>
+      </div>
+    </div>
+  </div>
+</div><!-- add alias modal -->
+<!-- add domain alias modal -->
+<div class="modal fade" id="addAliasDomainModal" tabindex="-1" role="dialog" aria-hidden="true">
+  <div class="modal-dialog modal-lg">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
+        <h3 class="modal-title"><?=$lang['mailbox']['add_domain_alias'];?></h3>
+      </div>
+      <div class="modal-body">
+				<form class="form-horizontal" role="form" data-id="add_alias_domain">
+					<input type="hidden" value="0" name="active">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="alias_domain"><?=$lang['add']['alias_domain'];?></label>
+						<div class="col-sm-10">
+							<textarea autocorrect="off" autocapitalize="none" class="form-control" rows="5" name="alias_domain" id="alias_domain" required></textarea>
+							<p><?=$lang['add']['alias_domain_info'];?></p>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="target_domain"><?=$lang['add']['target_domain'];?></label>
+						<div class="col-sm-10">
+							<select name="target_domain" id="target_domain" title="<?=$lang['add']['select'];?>" required>
+							<?php
+              foreach (mailbox('get', 'domains') as $domain) {
+								echo "<option>".htmlspecialchars($domain)."</option>";
+							}
+							?>
+							</select>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+              <button class="btn btn-default" id="add_item" data-id="add_alias_domain" data-api-url='add/alias-domain' data-api-attr='{}' href="#"><?=$lang['admin']['add'];?></button>
+						</div>
+					</div>
+				</form>
+      </div>
+    </div>
+  </div>
+</div><!-- add domain alias modal -->

+ 106 - 0
data/web/modals/user.php

@@ -0,0 +1,106 @@
+<?php
+if (!isset($_SESSION['mailcow_cc_role'])) {
+	header('Location: /');
+	exit();
+}
+?>
+<!-- add sync job modal -->
+<div class="modal fade" id="addSyncJobModal" tabindex="-1" role="dialog" aria-hidden="true">
+  <div class="modal-dialog modal-lg">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
+        <h3 class="modal-title"><?=$lang['add']['syncjob'];?></h3>
+      </div>
+      <div class="modal-body">
+        <p><?=$lang['add']['syncjob_hint'];?></p>
+				<form class="form-horizontal" role="form" data-id="add_syncjob">
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="host1"><?=$lang['add']['hostname'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="host1" id="host1" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="port1"><?=$lang['add']['port'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="port1" id="port1" min="1" max="65535" value="143" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="user1"><?=$lang['add']['username'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="user1" id="user1" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="password1"><?=$lang['add']['password'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="password1" id="password1" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="enc1"><?=$lang['add']['enc_method'];?></label>
+						<div class="col-sm-10">
+							<select name="enc1" id="enc1" title="<?=$lang['add']['select'];?>" required>
+                <option selected>TLS</option>
+                <option>SSL</option>
+                <option>PLAIN</option>
+							</select>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="mins_interval"><?=$lang['add']['mins_interval'];?></label>
+						<div class="col-sm-10">
+              <input type="number" class="form-control" name="mins_interval" min="10" max="3600" value="20" required>
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="subfolder2"><?=$lang['edit']['subfolder2'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="subfolder2" id="subfolder2" value="External">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="maxage"><?=$lang['edit']['maxage'];?></label>
+						<div class="col-sm-10">
+						<input type="number" class="form-control" name="maxage" id="maxage" min="0" max="32000" value="0">
+						</div>
+					</div>
+					<div class="form-group">
+						<label class="control-label col-sm-2" for="exclude"><?=$lang['add']['exclude'];?></label>
+						<div class="col-sm-10">
+						<input type="text" class="form-control" name="exclude" id="exclude" value="(?i)spam|(?i)junk">
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" value="1" name="delete2duplicates" checked> <?=$lang['add']['delete2duplicates'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" value="1" name="delete1"> <?=$lang['add']['delete1'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+							<div class="checkbox">
+							<label><input type="checkbox" value="1" name="active" checked> <?=$lang['add']['active'];?></label>
+							</div>
+						</div>
+					</div>
+					<div class="form-group">
+						<div class="col-sm-offset-2 col-sm-10">
+              <button class="btn btn-default" id="add_item" data-id="add_syncjob" data-api-url='add/syncjob' data-api-attr='{}' href="#"><?=$lang['admin']['add'];?></button>
+						</div>
+					</div>
+				</form>
+      </div>
+    </div>
+  </div>
+</div><!-- add sync job modal -->

+ 10 - 9
data/web/user.php

@@ -272,17 +272,15 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
         <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>
+            <a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_wl_mailbox" data-api-url='delete/mailbox-policy' href="#"><?=$lang['mailbox']['remove'];?></a></li>
             </ul>
           </div>
         </div>
-        <form class="form-inline" method="post">
+        <form class="form-inline" data-id="add_wl_policy_mailbox">
           <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>
+              <button class="btn btn-default" id="add_item" data-id="add_wl_policy_mailbox" data-api-url='add/mailbox-policy' data-api-attr='{"username":"<?= $username; ?>","object_list":"wl"}' href="#"><?=$lang['user']['spamfilter_table_add'];?></button>
             </span>
           </div>
         </form>
@@ -296,17 +294,17 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
         <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>
+            <a class="btn btn-sm btn-danger" id="delete_selected" data-id="policy_bl_mailbox" data-api-url='delete/mailbox-policy' href="#"><?=$lang['mailbox']['remove'];?></a></li>
             </ul>
           </div>
         </div>
-        <form class="form-inline" method="post">
+        <form class="form-inline" data-id="add_bl_policy_mailbox">
           <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>
+              <button class="btn btn-default" id="add_item" data-id="add_bl_policy_mailbox" data-api-url='add/mailbox-policy' data-api-attr='{"username":"<?= $username; ?>","object_list":"bl"}' href="#"><?=$lang['user']['spamfilter_table_add'];?></button>
             </span>
           </div>
         </form>
@@ -328,7 +326,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
           <li role="separator" class="divider"></li>
           <li><a id="delete_selected" data-id="syncjob" data-api-url='delete/syncjob' href="#"><?=$lang['mailbox']['remove'];?></a></li>
         </ul>
-        <a class="btn btn-sm btn-success" href="/add.php?syncjob"><?=$lang['user']['create_syncjob'];?></a>
+        <a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addSyncJobModal"><?=$lang['user']['create_syncjob'];?></a>
       </div>
     </div>
 		</div>
@@ -391,6 +389,9 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "use
   </div>
 </div>
 </div> <!-- /container -->
+<?php
+require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/user.php';
+?>
 <script type='text/javascript'>
 <?php
 $lang_user = json_encode($lang['user']);