浏览代码

[Web] Initial ratelimit support, more API actions

andryyy 8 年之前
父节点
当前提交
84ad579437

+ 2 - 2
data/web/admin.php

@@ -56,7 +56,7 @@ $tfa_data = get_tfa();
           </div>
           <div class="form-group">
             <div class="col-sm-offset-3 col-sm-9">
-              <button class="btn btn-default" id="edit_selected" data-id="admin" data-item="null" data-api-url='edit/admin' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
+              <button class="btn btn-default" id="edit_selected" data-id="admin" data-item="null" data-api-url='edit/self' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
             </div>
           </div>
         </form>
@@ -253,7 +253,7 @@ XYZ
         </div>
       </div>
     </div>
-    
+
     <div class="panel panel-default">
       <div class="panel-heading"><?=$lang['admin']['forwarding_hosts'];?></div>
       <div class="panel-body">

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

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

+ 57 - 28
data/web/edit.php

@@ -25,9 +25,8 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 				?>
 					<h4><?=$lang['edit']['alias'];?></h4>
 					<br />
-					<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+					<form class="form-horizontal" data-id="editalias" role="form" method="post">
 						<input type="hidden" value="0" name="active">
-						<input type="hidden" name="address" value="<?=htmlspecialchars($alias);?>">
 						<div class="form-group">
 							<label class="control-label col-sm-2" for="goto"><?=$lang['edit']['target_address'];?></label>
 							<div class="col-sm-10">
@@ -43,7 +42,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 						</div>
 						<div class="form-group">
 							<div class="col-sm-offset-2 col-sm-10">
-								<button type="submit" name="mailbox_edit_alias" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+                <button class="btn btn-success" id="edit_selected" data-id="editalias" data-item="<?=$alias;?>" data-api-url='edit/alias' data-api-attr='{}' href="#"><?=$lang['edit']['save'];?></button>
 							</div>
 						</div>
 					</form>
@@ -55,20 +54,19 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 				<?php
 				}
 		}
-		elseif (isset($_GET['domainadmin']) && 
-			ctype_alnum(str_replace(array('_', '.', '-'), '', $_GET["domainadmin"])) &&
-			!empty($_GET["domainadmin"]) &&
-			$_GET["domainadmin"] != 'admin' &&
-			$_SESSION['mailcow_cc_role'] == "admin") {
+		elseif (isset($_GET['domainadmin']) &&
+				ctype_alnum(str_replace(array('_', '.', '-'), '', $_GET["domainadmin"])) &&
+				!empty($_GET["domainadmin"]) &&
+				$_GET["domainadmin"] != 'admin' &&
+				$_SESSION['mailcow_cc_role'] == "admin") {
 				$domain_admin = $_GET["domainadmin"];
-        $result = get_domain_admin_details($domain_admin);
+				$result = domain_admin('details', $domain_admin);
 				if (!empty($result)) {
 				?>
 				<h4><?=$lang['edit']['domain_admin'];?></h4>
 				<br />
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+				<form class="form-horizontal" data-id="editdomainadmin" role="form" method="post">
 					<input type="hidden" value="0" name="active">
-					<input type="hidden" name="username" value="<?=htmlspecialchars($domain_admin);?>">
 					<div class="form-group">
 						<label class="control-label col-sm-2" for="username_new"><?=$lang['edit']['username'];?></label>
 						<div class="col-sm-10">
@@ -122,7 +120,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 					</div>
 					<div class="form-group">
 						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="edit_domain_admin" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+              <button class="btn btn-success" id="edit_selected" data-id="editdomainadmin" data-item="<?=$domain_admin;?>" data-api-url='edit/domain-admin' data-api-attr='{}' href="#"><?=$lang['edit']['save'];?></button>
 						</div>
 					</div>
 				</form>
@@ -139,14 +137,14 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 		!empty($_GET["domain"])) {
 			$domain = $_GET["domain"];
       $result = mailbox('get', 'domain_details', $domain);
+      $rl = mailbox('get', 'domain_ratelimit', $domain);
 			if (!empty($result)) {
 			?>
 				<h4><?=$lang['edit']['domain'];?></h4>
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+				<form data-id="editdomain" class="form-horizontal" role="form" method="post">
 					<input type="hidden" value="0" name="active">
 					<input type="hidden" value="0" name="backupmx">
 					<input type="hidden" value="0" name="relay_all_recipients">
-					<input type="hidden" name="domain" value="<?=htmlspecialchars($domain);?>">
 					<div class="form-group">
 						<label class="control-label col-sm-2" for="description"><?=$lang['edit']['description'];?></label>
 						<div class="col-sm-10">
@@ -203,7 +201,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 					</div>
 					<div class="form-group">
 						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="mailbox_edit_domain" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+              <button class="btn btn-success" id="edit_selected" data-id="editdomain" data-item="<?=$domain;?>" data-api-url='edit/domain' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
 						</div>
 					</div>
 				</form>
@@ -223,6 +221,23 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 				}
         ?>
 		<hr>
+    <form data-id="domratelimit" class="form-inline well" method="post">
+      <div class="form-group">
+        <label class="control-label">Ratelimit</label>
+        <input name="rl_value" id="rl_value" type="number" value="<?=(!empty($rl['value'])) ? $rl['value'] : null;?>" class="form-control" placeholder="disabled">
+      </div>
+      <div class="form-group">
+        <select name="rl_frame" id="rl_frame" class="form-control">
+          <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
+          <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
+          <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
+        </select>
+      </div>
+      <div class="form-group">
+        <button class="btn btn-default" id="edit_selected" data-id="domratelimit" data-item="<?=$domain;?>" data-api-url='edit/domain-ratelimit' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
+      </div>
+    </form>
+		<hr>
 		<div class="row">
 			<div class="col-sm-6">
 				<h4><?=$lang['user']['spamfilter_wl'];?></h4>
@@ -282,12 +297,12 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 		!empty($_GET["aliasdomain"])) {
 			$alias_domain = $_GET["aliasdomain"];
       $result = mailbox('get', 'alias_domain_details', $alias_domain);
+      $rl = mailbox('get', 'domain_ratelimit', $alias_domain);
       if (!empty($result)) {
 			?>
 				<h4><?=$lang['edit']['edit_alias_domain'];?></h4>
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+				<form class="form-horizontal" data-id="editaliasdomain" role="form" method="post">
 					<input type="hidden" value="0" name="active">
-					<input type="hidden" value="<?=$result['alias_domain'];?>" name="alias_domain">
 					<div class="form-group">
 						<label class="control-label col-sm-2" for="target_domain"><?=$lang['edit']['target_domain'];?></label>
 						<div class="col-sm-10">
@@ -303,10 +318,27 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 					</div>
 					<div class="form-group">
 						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="mailbox_edit_alias_domain" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+              <button class="btn btn-success" id="edit_selected" data-id="editaliasdomain" data-item="<?=$alias_domain;?>" data-api-url='edit/alias-domain' data-api-attr='{}' href="#"><?=$lang['edit']['save'];?></button>
 						</div>
 					</div>
 				</form>
+        <hr>
+        <form data-id="domratelimit" class="form-inline well" method="post">
+          <div class="form-group">
+            <label class="control-label">Ratelimit</label>
+            <input name="rl_value" id="rl_value" type="number" value="<?=(!empty($rl['value'])) ? $rl['value'] : null;?>" class="form-control" placeholder="disabled">
+          </div>
+          <div class="form-group">
+            <select name="rl_frame" id="rl_frame" class="form-control">
+              <option value="s" <?=(isset($rl['frame']) && $rl['frame'] == 's') ? 'selected' : null;?>>msgs / second</option>
+              <option value="m" <?=(isset($rl['frame']) && $rl['frame'] == 'm') ? 'selected' : null;?>>msgs / minute</option>
+              <option value="h" <?=(isset($rl['frame']) && $rl['frame'] == 'h') ? 'selected' : null;?>>msgs / hour</option>
+            </select>
+          </div>
+          <div class="form-group">
+            <button class="btn btn-default" id="edit_selected" data-id="domratelimit" data-item="<?=$alias_domain;?>" data-api-url='edit/domain-ratelimit' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button>
+          </div>
+        </form>
 				<?php
         if (!empty($dkim = dkim('details', $alias_domain))) {
 				?>
@@ -334,10 +366,9 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
     if (!empty($result)) {
       ?>
       <h4><?=$lang['edit']['mailbox'];?></h4>
-      <form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+      <form class="form-horizontal" data-id="editmailbox" role="form" method="post">
 				<input type="hidden" value="0" name="sender_acl">
 				<input type="hidden" value="0" name="active">
-				<input type="hidden" name="username" value="<?=htmlspecialchars($result['username']);?>">
         <div class="form-group">
           <label class="control-label col-sm-2" for="name"><?=$lang['edit']['full_name'];?>:</label>
           <div class="col-sm-10">
@@ -355,7 +386,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
         <div class="form-group">
           <label class="control-label col-sm-2" for="sender_acl"><?=$lang['edit']['sender_acl'];?>:</label>
           <div class="col-sm-10">
-            <select data-width="100%" style="width:100%" id="sender_acl" name="sender_acl[]" size="10" multiple>
+            <select data-width="100%" style="width:100%" id="sender_acl" name="sender_acl" size="10" multiple>
             <?php
             $sender_acl_handles = mailbox('get', 'sender_acl_handles', $mailbox);
 
@@ -426,7 +457,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
         </div>
         <div class="form-group">
           <div class="col-sm-offset-2 col-sm-10">
-            <button type="submit" name="mailbox_edit_mailbox" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+            <button class="btn btn-success" id="edit_selected" data-id="editmailbox" data-item="<?=htmlspecialchars($result['username']);?>" data-api-url='edit/mailbox' data-api-attr='{}' href="#"><?=$lang['edit']['save'];?></button>
           </div>
         </div>
       </form>
@@ -439,10 +470,9 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
       if (!empty($result)) {
         ?>
 				<h4><?=$lang['edit']['resource'];?></h4>
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+				<form class="form-horizontal" role="form" method="post" data-id="editresource">
           <input type="hidden" value="0" name="active">
           <input type="hidden" value="0" name="multiple_bookings">
-          <input type="hidden" name="name" value="<?=htmlspecialchars($result['name']);?>">
 					<div class="form-group">
 						<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
 						<div class="col-sm-10">
@@ -475,7 +505,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
 					</div>
 					<div class="form-group">
 						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="mailbox_edit_resource" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+              <button class="btn btn-success" id="edit_selected" data-id="editresource" data-item="<?=htmlspecialchars($result['name']);?>" data-api-url='edit/resource' data-api-attr='{}' href="#"><?=$lang['edit']['save'];?></button>
 						</div>
 					</div>
 				</form>
@@ -501,11 +531,10 @@ elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] ==
       if (!empty($result)) {
 			?>
 				<h4><?=$lang['edit']['syncjob'];?></h4>
-				<form class="form-horizontal" role="form" method="post" action="<?=($FORM_ACTION == "previous") ? $_SESSION['return_to'] : null;?>">
+				<form class="form-horizontal" data-id="editsyncjob" role="form" method="post">
           <input type="hidden" value="0" name="delete2duplicates">
           <input type="hidden" value="0" name="delete1">
           <input type="hidden" value="0" name="active">
-          <input type="hidden" name="id" value="<?=htmlspecialchars($result['id']);?>">
 					<div class="form-group">
 						<label class="control-label col-sm-2" for="host1"><?=$lang['edit']['hostname'];?></label>
 						<div class="col-sm-10">
@@ -587,7 +616,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] ==
 					</div>
 					<div class="form-group">
 						<div class="col-sm-offset-2 col-sm-10">
-							<button type="submit" name="edit_syncjob" value="1" class="btn btn-success btn-sm"><?=$lang['edit']['save'];?></button>
+              <button class="btn btn-success" id="edit_selected" data-id="editsyncjob" data-item="<?=htmlspecialchars($result['id']);?>" data-api-url='edit/syncjob' data-api-attr='{}' href="#"><?=$lang['edit']['save'];?></button>
 						</div>
 					</div>
 				</form>

+ 507 - 0
data/web/inc/functions.domain_admin.inc.php

@@ -0,0 +1,507 @@
+<?php
+
+function domain_admin($_action, $_data = null) {
+  global $pdo;
+  global $lang;
+  switch ($_action) {
+    case 'add':
+      $username		= strtolower(trim($_data['username']));
+      $password		= $_data['password'];
+      $password2  = $_data['password2'];
+      $domains    = (array)$_data['domains'];
+      $active     = intval($_data['active']);
+
+      if ($_SESSION['mailcow_cc_role'] != "admin") {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+      if (empty($domains)) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['domain_invalid'])
+        );
+        return false;
+      }
+      if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['username_invalid'])
+        );
+        return false;
+      }
+      try {
+        $stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
+          WHERE `username` = :username");
+        $stmt->execute(array(':username' => $username));
+        $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+        
+        $stmt = $pdo->prepare("SELECT `username` FROM `admin`
+          WHERE `username` = :username");
+        $stmt->execute(array(':username' => $username));
+        $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+        
+        $stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
+          WHERE `username` = :username");
+        $stmt->execute(array(':username' => $username));
+        $num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
+      }
+      catch(PDOException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'MySQL: '.$e
+        );
+        return false;
+      }
+      foreach ($num_results as $num_results_each) {
+        if ($num_results_each != 0) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($username))
+          );
+          return false;
+        }
+      }
+      if (!empty($password) && !empty($password2)) {
+        if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => sprintf($lang['danger']['password_complexity'])
+          );
+          return false;
+        }
+        if ($password != $password2) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => sprintf($lang['danger']['password_mismatch'])
+          );
+          return false;
+        }
+        $password_hashed = hash_password($password);
+        foreach ($domains as $domain) {
+          if (!is_valid_domain_name($domain)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['domain_invalid'])
+            );
+            return false;
+          }
+          try {
+            $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
+                VALUES (:username, :domain, :created, :active)");
+            $stmt->execute(array(
+              ':username' => $username,
+              ':domain' => $domain,
+              ':created' => date('Y-m-d H:i:s'),
+              ':active' => $active
+            ));
+          }
+          catch (PDOException $e) {
+            domain_admin('delete', $username);
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+        }
+        try {
+          $stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
+            VALUES (:username, :password_hashed, '0', :active)");
+          $stmt->execute(array(
+            ':username' => $username,
+            ':password_hashed' => $password_hashed,
+            ':active' => $active
+          ));
+        }
+        catch (PDOException $e) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => 'MySQL: '.$e
+          );
+          return false;
+        }
+      }
+      else {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['password_empty'])
+        );
+        return false;
+      }
+      $_SESSION['return'] = array(
+        'type' => 'success',
+        'msg' => sprintf($lang['success']['domain_admin_added'], htmlspecialchars($username))
+      );
+    break;
+    case 'edit':
+      if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+      // Administrator
+      if ($_SESSION['mailcow_cc_role'] == "admin") {
+        if (!is_array($_data['username'])) {
+          $usernames = array();
+          $usernames[] = $_data['username'];
+        }
+        else {
+          $usernames = $_data['username'];
+        }
+        foreach ($usernames as $username) {
+          $is_now = domain_admin('details', $username);
+          $domains = (isset($_data['domains'])) ? (array)$_data['domains'] : null;
+          if (!empty($is_now)) {
+            $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
+            $domains = (!empty($domains)) ? $domains : $is_now['selected_domains'];
+            $username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
+          }
+          else {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['access_denied'])
+            );
+            return false;
+          }
+          $password     = $_data['password'];
+          $password2    = $_data['password2'];
+
+          if (!empty($domains)) {
+            foreach ($domains as $domain) {
+              if (!is_valid_domain_name($domain)) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => sprintf($lang['danger']['domain_invalid'])
+                );
+                return false;
+              }
+            }
+          }
+          if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['username_invalid'])
+            );
+            return false;
+          }
+          if ($username_new != $username) {
+            if (!empty(domain_admin('details', $username_new)['username'])) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['username_invalid'])
+              );
+              return false;
+            }
+          }
+          try {
+            $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
+            $stmt->execute(array(
+              ':username' => $username,
+            ));
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+
+          if (!empty($domains)) {
+            foreach ($domains as $domain) {
+              try {
+                $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
+                  VALUES (:username_new, :domain, :created, :active)");
+                $stmt->execute(array(
+                  ':username_new' => $username_new,
+                  ':domain' => $domain,
+                  ':created' => date('Y-m-d H:i:s'),
+                  ':active' => $active
+                ));
+              }
+              catch (PDOException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'MySQL: '.$e
+                );
+                return false;
+              }
+            }
+          }
+
+          if (!empty($password) && !empty($password2)) {
+            if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['password_complexity'])
+              );
+              return false;
+            }
+            if ($password != $password2) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['password_mismatch'])
+              );
+              return false;
+            }
+            $password_hashed = hash_password($password);
+            try {
+              $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
+              $stmt->execute(array(
+                ':password_hashed' => $password_hashed,
+                ':username_new' => $username_new,
+                ':username' => $username,
+                ':active' => $active
+              ));
+              if (isset($_data['disable_tfa'])) {
+                $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
+                $stmt->execute(array(':username' => $username));
+              }
+              else {
+                $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
+                $stmt->execute(array(':username_new' => $username_new, ':username' => $username));
+              }
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+          else {
+            try {
+              $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
+              $stmt->execute(array(
+                ':username_new' => $username_new,
+                ':username' => $username,
+                ':active' => $active
+              ));
+              if (isset($_data['disable_tfa'])) {
+                $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
+                $stmt->execute(array(':username' => $username));
+              }
+              else {
+                $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
+                $stmt->execute(array(':username_new' => $username_new, ':username' => $username));
+              }
+            }
+            catch (PDOException $e) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'MySQL: '.$e
+              );
+              return false;
+            }
+          }
+        }
+        $_SESSION['return'] = array(
+          'type' => 'success',
+          'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars(implode(', ', $usernames)))
+        );
+      }
+      // Domain administrator
+      // Can only edit itself
+      elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
+        $username = $_SESSION['mailcow_cc_username'];
+        $password_old		= $_data['user_old_pass'];
+        $password_new	= $_data['user_new_pass'];
+        $password_new2	= $_data['user_new_pass2'];
+
+        $stmt = $pdo->prepare("SELECT `password` FROM `admin`
+            WHERE `username` = :user");
+        $stmt->execute(array(':user' => $username));
+        $row = $stmt->fetch(PDO::FETCH_ASSOC);
+        if (!verify_ssha256($row['password'], $password_old)) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => sprintf($lang['danger']['access_denied'])
+          );
+          return false;
+        }
+
+        if (!empty($password_new2) && !empty($password_new)) {
+          if ($password_new2 != $password_new) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['password_mismatch'])
+            );
+            return false;
+          }
+          if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password_new)) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => sprintf($lang['danger']['password_complexity'])
+            );
+            return false;
+          }
+          $password_hashed = hash_password($password_new);
+          try {
+            $stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
+            $stmt->execute(array(
+              ':password_hashed' => $password_hashed,
+              ':username' => $username
+            ));
+          }
+          catch (PDOException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'MySQL: '.$e
+            );
+            return false;
+          }
+        }
+        
+        $_SESSION['return'] = array(
+          'type' => 'success',
+          'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars($username))
+        );
+      }
+    break;
+    case 'delete':
+      if ($_SESSION['mailcow_cc_role'] != "admin") {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+      $usernames = (array)$_data['username'];
+      foreach ($usernames as $username) {
+        if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => sprintf($lang['danger']['username_invalid'])
+          );
+          return false;
+        }
+        try {
+          $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
+          $stmt->execute(array(
+            ':username' => $username,
+          ));
+          $stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
+          $stmt->execute(array(
+            ':username' => $username,
+          ));
+        }
+        catch (PDOException $e) {
+          $_SESSION['return'] = array(
+            'type' => 'danger',
+            'msg' => 'MySQL: '.$e
+          );
+          return false;
+        }
+      }
+      $_SESSION['return'] = array(
+        'type' => 'success',
+        'msg' => sprintf($lang['success']['domain_admin_removed'], htmlspecialchars(implode(', ', $usernames)))
+      );
+    break;
+    case 'get':
+      $domainadmins = array();
+      if ($_SESSION['mailcow_cc_role'] != "admin") {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => sprintf($lang['danger']['access_denied'])
+        );
+        return false;
+      }
+      try {
+        $stmt = $pdo->query("SELECT DISTINCT
+          `username`
+            FROM `domain_admins` 
+              WHERE `username` IN (
+                SELECT `username` FROM `admin`
+                  WHERE `superadmin`!='1'
+              )");
+        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+        while ($row = array_shift($rows)) {
+          $domainadmins[] = $row['username'];
+        }
+      }
+      catch(PDOException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'MySQL: '.$e
+        );
+      }
+      return $domainadmins;
+    break;
+    case 'details':
+      $domainadmindata = array();
+
+      if ($_SESSION['mailcow_cc_role'] == "domainadmin" && $_data != $_SESSION['mailcow_cc_username']) {
+        return false;
+      }
+      elseif ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
+        return false;
+      }
+
+      if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $_data))) {
+        return false;
+      }
+      try {
+        $stmt = $pdo->prepare("SELECT
+          `tfa`.`active` AS `tfa_active_int`,
+          CASE `tfa`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `tfa_active`,
+          `domain_admins`.`username`,
+          `domain_admins`.`created`,
+          `domain_admins`.`active` AS `active_int`,
+          CASE `domain_admins`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
+            FROM `domain_admins`
+            LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
+              WHERE `domain_admins`.`username`= :domain_admin");
+        $stmt->execute(array(
+          ':domain_admin' => $_data
+        ));
+        $row = $stmt->fetch(PDO::FETCH_ASSOC);
+        if (empty($row)) { 
+          return false;
+        }
+        $domainadmindata['username'] = $row['username'];
+        $domainadmindata['tfa_active'] = $row['tfa_active'];
+        $domainadmindata['active'] = $row['active'];
+        $domainadmindata['tfa_active_int'] = $row['tfa_active_int'];
+        $domainadmindata['active_int'] = $row['active_int'];
+        $domainadmindata['modified'] = $row['created'];
+        // GET SELECTED
+        $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+          WHERE `domain` IN (
+            SELECT `domain` FROM `domain_admins`
+              WHERE `username`= :domain_admin)");
+        $stmt->execute(array(':domain_admin' => $_data));
+        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+        while($row = array_shift($rows)) {
+          $domainadmindata['selected_domains'][] = $row['domain'];
+        }
+        // GET UNSELECTED
+        $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
+          WHERE `domain` NOT IN (
+            SELECT `domain` FROM `domain_admins`
+              WHERE `username`= :domain_admin)");
+        $stmt->execute(array(':domain_admin' => $_data));
+        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
+        while($row = array_shift($rows)) {
+          $domainadmindata['unselected_domains'][] = $row['domain'];
+        }
+        if (!isset($domainadmindata['unselected_domains'])) {
+          $domainadmindata['unselected_domains'] = "";
+        }
+      }
+      catch(PDOException $e) {
+        $_SESSION['return'] = array(
+          'type' => 'danger',
+          'msg' => 'MySQL: '.$e
+        );
+      }
+      return $domainadmindata;
+    break;
+  }
+}

+ 10 - 563
data/web/inc/functions.inc.php

@@ -73,7 +73,6 @@ function generate_tlsa_digest($hostname, $port, $starttls = null) {
     unset($lines[0]);
     return base64_decode(implode('', $lines));
   }
-
   if (empty($starttls)) {
     $context = stream_context_create(array("ssl" => array("capture_peer_cert" => true, 'verify_peer' => false, 'allow_self_signed' => true)));
     $stream = stream_socket_client('tls://' . $hostname . ':' . $port, $error_nr, $error_msg, 5, STREAM_CLIENT_CONNECT, $context);
@@ -113,7 +112,6 @@ function generate_tlsa_digest($hostname, $port, $starttls = null) {
     stream_socket_enable_crypto($stream, true, STREAM_CRYPTO_METHOD_ANY_CLIENT);
     stream_set_blocking($stream, false);
   }
-
   $params = stream_context_get_params($stream);
   if (!empty($params['options']['ssl']['peer_certificate'])) {
     $key_resource = openssl_pkey_get_public($params['options']['ssl']['peer_certificate']);
@@ -142,30 +140,6 @@ function verify_ssha256($hash, $password) {
 		return false;
 	}
 }
-function doveadm_authenticate($hash, $algorithm, $password) {
-	$descr = array(0 => array('pipe', 'r'), 1 => array('pipe', 'w'), 2 => array('pipe', 'w'));
-	$pipes = array();
-	$process = proc_open("/usr/bin/doveadm pw -s ".$algorithm." -t '".$hash."'", $descr, $pipes);
-	if (is_resource($process)) {
-		fputs($pipes[0], $password);
-		fclose($pipes[0]);
-		while ($f = fgets($pipes[1])) {
-			if (preg_match('/(verified)/', $f)) {
-				proc_close($process);
-				return true;
-			}
-			return false;
-		}
-		fclose($pipes[1]);
-		while ($f = fgets($pipes[2])) {
-			proc_close($process);
-			return false;
-		}
-		fclose($pipes[2]);
-		proc_close($process);
-	}
-	return false;
-}
 function check_login($user, $pass) {
 	global $pdo;
 	global $redis;
@@ -272,7 +246,6 @@ function edit_admin_account($postarray) {
 		);
 		return false;
 	}
-
 	if (!empty($password) && !empty($password2)) {
     if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
       $_SESSION['return'] = array(
@@ -348,28 +321,20 @@ function edit_admin_account($postarray) {
 function edit_user_account($postarray) {
 	global $lang;
 	global $pdo;
-  if (isset($postarray['username']) && filter_var($postarray['username'], FILTER_VALIDATE_EMAIL)) {
-    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $postarray['username'])) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-    else {
-      $username = $postarray['username'];
-    }
-  }
-  else {
-    $username = $_SESSION['mailcow_cc_username'];
+  $username = $_SESSION['mailcow_cc_username'];
+  $role = $_SESSION['mailcow_cc_role'];
+	$password_old = $postarray['user_old_pass'];
+  if (filter_var($username, FILTER_VALIDATE_EMAIL === false) || $role != 'user') {
+    $_SESSION['return'] = array(
+      'type' => 'danger',
+      'msg' => sprintf($lang['danger']['access_denied'])
+    );
+    return false;
   }
-	$password_old		= $postarray['user_old_pass'];
-
 	if (isset($postarray['user_new_pass']) && isset($postarray['user_new_pass2'])) {
 		$password_new	= $postarray['user_new_pass'];
 		$password_new2	= $postarray['user_new_pass2'];
 	}
-
 	$stmt = $pdo->prepare("SELECT `password` FROM `mailbox`
 			WHERE `kind` NOT REGEXP 'location|thing|group'
         AND `username` = :user");
@@ -382,7 +347,6 @@ function edit_user_account($postarray) {
     );
     return false;
   }
-
 	if (isset($password_new) && isset($password_new2)) {
 		if (!empty($password_new2) && !empty($password_new)) {
 			if ($password_new2 != $password_new) {
@@ -486,293 +450,12 @@ function is_valid_domain_name($domain_name) {
 		   && preg_match("/^.{1,253}$/", $domain_name)
 		   && preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name));
 }
-function add_domain_admin($postarray) {
-	global $lang;
-	global $pdo;
-	$username		= strtolower(trim($postarray['username']));
-	$password		= $postarray['password'];
-	$password2  = $postarray['password2'];
-	$domains    = (array)$postarray['domains'];
-  $active     = intval($postarray['active']);
-
-	if ($_SESSION['mailcow_cc_role'] != "admin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-	if (empty($domains)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['domain_invalid'])
-		);
-		return false;
-	}
-	if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username)) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['username_invalid'])
-		);
-		return false;
-	}
-	try {
-		$stmt = $pdo->prepare("SELECT `username` FROM `mailbox`
-			WHERE `username` = :username");
-		$stmt->execute(array(':username' => $username));
-		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-		
-		$stmt = $pdo->prepare("SELECT `username` FROM `admin`
-			WHERE `username` = :username");
-		$stmt->execute(array(':username' => $username));
-		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-		
-		$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
-			WHERE `username` = :username");
-		$stmt->execute(array(':username' => $username));
-		$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
-	}
-	catch(PDOException $e) {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => 'MySQL: '.$e
-		);
-		return false;
-	}
-	foreach ($num_results as $num_results_each) {
-		if ($num_results_each != 0) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['object_exists'], htmlspecialchars($username))
-			);
-			return false;
-		}
-	}
-	if (!empty($password) && !empty($password2)) {
-    if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['password_complexity'])
-      );
-      return false;
-    }
-		if ($password != $password2) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => sprintf($lang['danger']['password_mismatch'])
-			);
-			return false;
-		}
-		$password_hashed = hash_password($password);
-		foreach ($domains as $domain) {
-			if (!is_valid_domain_name($domain)) {
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => sprintf($lang['danger']['domain_invalid'])
-				);
-				return false;
-			}
-			try {
-				$stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
-						VALUES (:username, :domain, :created, :active)");
-				$stmt->execute(array(
-					':username' => $username,
-					':domain' => $domain,
-					':created' => date('Y-m-d H:i:s'),
-					':active' => $active
-				));
-			}
-			catch (PDOException $e) {
-        delete_domain_admin(array('username' => $username));
-				$_SESSION['return'] = array(
-					'type' => 'danger',
-					'msg' => 'MySQL: '.$e
-				);
-				return false;
-			}
-		}
-		try {
-			$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
-				VALUES (:username, :password_hashed, '0', :active)");
-			$stmt->execute(array(
-				':username' => $username,
-				':password_hashed' => $password_hashed,
-				':active' => $active
-			));
-		}
-		catch (PDOException $e) {
-			$_SESSION['return'] = array(
-				'type' => 'danger',
-				'msg' => 'MySQL: '.$e
-			);
-			return false;
-		}
-	}
-	else {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['password_empty'])
-		);
-		return false;
-	}
-	$_SESSION['return'] = array(
-		'type' => 'success',
-		'msg' => sprintf($lang['success']['domain_admin_added'], htmlspecialchars($username))
-	);
-}
-function delete_domain_admin($postarray) {
-	global $pdo;
-	global $lang;
-	if ($_SESSION['mailcow_cc_role'] != "admin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-	$usernames = (array)$postarray['username'];
-  foreach ($usernames as $username) {
-    if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username))) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['username_invalid'])
-      );
-      return false;
-    }
-    try {
-      $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
-      $stmt->execute(array(
-        ':username' => $username,
-      ));
-      $stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
-      $stmt->execute(array(
-        ':username' => $username,
-      ));
-    }
-    catch (PDOException $e) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => 'MySQL: '.$e
-      );
-      return false;
-    }
-  }
-  $_SESSION['return'] = array(
-    'type' => 'success',
-    'msg' => sprintf($lang['success']['domain_admin_removed'], htmlspecialchars(implode(', ', $usernames)))
-  );
-}
-function get_domain_admins() {
-	global $pdo;
-	global $lang;
-  $domainadmins = array();
-	if ($_SESSION['mailcow_cc_role'] != "admin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-  try {
-    $stmt = $pdo->query("SELECT DISTINCT
-      `username`
-        FROM `domain_admins` 
-          WHERE `username` IN (
-            SELECT `username` FROM `admin`
-              WHERE `superadmin`!='1'
-          )");
-    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while ($row = array_shift($rows)) {
-      $domainadmins[] = $row['username'];
-    }
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-  }
-  return $domainadmins;
-}
-function get_domain_admin_details($domain_admin) {
-	global $pdo;
-
-	global $lang;
-  $domainadmindata = array();
-	if (isset($domain_admin) && $_SESSION['mailcow_cc_role'] != "admin") {
-		return false;
-	}
-  if (!isset($domain_admin) && $_SESSION['mailcow_cc_role'] != "domainadmin") {
-		return false;
-	}
-  (!isset($domain_admin)) ? $domain_admin = $_SESSION['mailcow_cc_username'] : null;
-  
-  if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $domain_admin))) {
-		return false;
-	}
-  try {
-    $stmt = $pdo->prepare("SELECT
-      `tfa`.`active` AS `tfa_active_int`,
-      CASE `tfa`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `tfa_active`,
-      `domain_admins`.`username`,
-      `domain_admins`.`created`,
-      `domain_admins`.`active` AS `active_int`,
-      CASE `domain_admins`.`active` WHEN 1 THEN '".$lang['mailbox']['yes']."' ELSE '".$lang['mailbox']['no']."' END AS `active`
-        FROM `domain_admins`
-        LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`domain_admins`.`username`
-          WHERE `domain_admins`.`username`= :domain_admin");
-    $stmt->execute(array(
-      ':domain_admin' => $domain_admin
-    ));
-    $row = $stmt->fetch(PDO::FETCH_ASSOC);
-    if (empty($row)) { 
-      return false;
-    }
-    $domainadmindata['username'] = $row['username'];
-    $domainadmindata['tfa_active'] = $row['tfa_active'];
-    $domainadmindata['active'] = $row['active'];
-    $domainadmindata['tfa_active_int'] = $row['tfa_active_int'];
-    $domainadmindata['active_int'] = $row['active_int'];
-    $domainadmindata['modified'] = $row['created'];
-    // GET SELECTED
-    $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
-      WHERE `domain` IN (
-        SELECT `domain` FROM `domain_admins`
-          WHERE `username`= :domain_admin)");
-    $stmt->execute(array(':domain_admin' => $domain_admin));
-    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while($row = array_shift($rows)) {
-      $domainadmindata['selected_domains'][] = $row['domain'];
-    }
-    // GET UNSELECTED
-    $stmt = $pdo->prepare("SELECT `domain` FROM `domain`
-      WHERE `domain` NOT IN (
-        SELECT `domain` FROM `domain_admins`
-          WHERE `username`= :domain_admin)");
-    $stmt->execute(array(':domain_admin' => $domain_admin));
-    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
-    while($row = array_shift($rows)) {
-      $domainadmindata['unselected_domains'][] = $row['domain'];
-    }
-    if (!isset($domainadmindata['unselected_domains'])) {
-      $domainadmindata['unselected_domains'] = "";
-    }
-  }
-  catch(PDOException $e) {
-    $_SESSION['return'] = array(
-      'type' => 'danger',
-      'msg' => 'MySQL: '.$e
-    );
-  }
-  return $domainadmindata;
-}
 function set_tfa($postarray) {
 	global $lang;
 	global $pdo;
 	global $yubi;
 	global $u2f;
 	global $tfa;
-
   if ($_SESSION['mailcow_cc_role'] != "domainadmin" &&
     $_SESSION['mailcow_cc_role'] != "admin") {
       $_SESSION['return'] = array(
@@ -847,7 +530,6 @@ function set_tfa($postarray) {
 				'msg' => sprintf($lang['success']['object_modified'], htmlspecialchars($username))
 			);
 		break;
-
 		case "u2f":
       $key_id = (!isset($postarray["key_id"])) ? 'unidentified' : $postarray["key_id"];
       try {
@@ -871,7 +553,6 @@ function set_tfa($postarray) {
         return false;
       }
 		break;
-
 		case "totp":
       $key_id = (!isset($postarray["key_id"])) ? 'unidentified' : $postarray["key_id"];
       if ($tfa->verifyCode($_POST['totp_secret'], $_POST['totp_confirm_token']) === true) {
@@ -900,7 +581,6 @@ function set_tfa($postarray) {
         );
       }
 		break;
-
 		case "none":
 			try {
 				$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
@@ -977,7 +657,6 @@ function get_tfa($username = null) {
   elseif (empty($username)) {
     return false;
   }
-
   $stmt = $pdo->prepare("SELECT * FROM `tfa`
       WHERE `username` = :username AND `active` = '1'");
   $stmt->execute(array(':username' => $username));
@@ -1041,7 +720,6 @@ function verify_tfa_login($username, $token) {
 	global $yubi;
 	global $u2f;
 	global $tfa;
-
   $stmt = $pdo->prepare("SELECT `authmech` FROM `tfa`
       WHERE `username` = :username AND `active` = '1'");
   $stmt->execute(array(':username' => $username));
@@ -1126,237 +804,6 @@ function verify_tfa_login($username, $token) {
 	}
   return false;
 }
-function edit_domain_admin($postarray) {
-	global $lang;
-	global $pdo;
-
-	if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
-		$_SESSION['return'] = array(
-			'type' => 'danger',
-			'msg' => sprintf($lang['danger']['access_denied'])
-		);
-		return false;
-	}
-	// Administrator
-  if ($_SESSION['mailcow_cc_role'] == "admin") {
-    if (!is_array($postarray['username'])) {
-      $usernames = array();
-      $usernames[] = $postarray['username'];
-    }
-    else {
-      $usernames = $postarray['username'];
-    }
-    foreach ($usernames as $username) {
-      $is_now = get_domain_admin_details($username);
-      $domains = (isset($postarray['domains'])) ? (array)$postarray['domains'] : null;
-      if (!empty($is_now)) {
-        $active = (isset($postarray['active'])) ? $postarray['active'] : $is_now['active_int'];
-        $domains = (!empty($domains)) ? $domains : $is_now['selected_domains'];
-        $username_new = (!empty($postarray['username_new'])) ? $postarray['username_new'] : $is_now['username'];
-      }
-      else {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['access_denied'])
-        );
-        return false;
-      }
-      $password     = $postarray['password'];
-      $password2    = $postarray['password2'];
-
-      if (!empty($domains)) {
-        foreach ($domains as $domain) {
-          if (!is_valid_domain_name($domain)) {
-            $_SESSION['return'] = array(
-              'type' => 'danger',
-              'msg' => sprintf($lang['danger']['domain_invalid'])
-            );
-            return false;
-          }
-        }
-      }
-      if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['username_invalid'])
-        );
-        return false;
-      }
-      if ($username_new != $username) {
-        if (!empty(get_domain_admin_details($username_new)['username'])) {
-          $_SESSION['return'] = array(
-            'type' => 'danger',
-            'msg' => sprintf($lang['danger']['username_invalid'])
-          );
-          return false;
-        }
-      }
-      try {
-        $stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
-        $stmt->execute(array(
-          ':username' => $username,
-        ));
-      }
-      catch (PDOException $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'MySQL: '.$e
-        );
-        return false;
-      }
-
-      if (!empty($domains)) {
-        foreach ($domains as $domain) {
-          try {
-            $stmt = $pdo->prepare("INSERT INTO `domain_admins` (`username`, `domain`, `created`, `active`)
-              VALUES (:username_new, :domain, :created, :active)");
-            $stmt->execute(array(
-              ':username_new' => $username_new,
-              ':domain' => $domain,
-              ':created' => date('Y-m-d H:i:s'),
-              ':active' => $active
-            ));
-          }
-          catch (PDOException $e) {
-            $_SESSION['return'] = array(
-              'type' => 'danger',
-              'msg' => 'MySQL: '.$e
-            );
-            return false;
-          }
-        }
-      }
-
-      if (!empty($password) && !empty($password2)) {
-        if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
-          $_SESSION['return'] = array(
-            'type' => 'danger',
-            'msg' => sprintf($lang['danger']['password_complexity'])
-          );
-          return false;
-        }
-        if ($password != $password2) {
-          $_SESSION['return'] = array(
-            'type' => 'danger',
-            'msg' => sprintf($lang['danger']['password_mismatch'])
-          );
-          return false;
-        }
-        $password_hashed = hash_password($password);
-        try {
-          $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
-          $stmt->execute(array(
-            ':password_hashed' => $password_hashed,
-            ':username_new' => $username_new,
-            ':username' => $username,
-            ':active' => $active
-          ));
-          if (isset($postarray['disable_tfa'])) {
-            $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
-            $stmt->execute(array(':username' => $username));
-          }
-          else {
-            $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
-            $stmt->execute(array(':username_new' => $username_new, ':username' => $username));
-          }
-        }
-        catch (PDOException $e) {
-          $_SESSION['return'] = array(
-            'type' => 'danger',
-            'msg' => 'MySQL: '.$e
-          );
-          return false;
-        }
-      }
-      else {
-        try {
-          $stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
-          $stmt->execute(array(
-            ':username_new' => $username_new,
-            ':username' => $username,
-            ':active' => $active
-          ));
-          if (isset($postarray['disable_tfa'])) {
-            $stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
-            $stmt->execute(array(':username' => $username));
-          }
-          else {
-            $stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
-            $stmt->execute(array(':username_new' => $username_new, ':username' => $username));
-          }
-        }
-        catch (PDOException $e) {
-          $_SESSION['return'] = array(
-            'type' => 'danger',
-            'msg' => 'MySQL: '.$e
-          );
-          return false;
-        }
-      }
-    }
-    $_SESSION['return'] = array(
-      'type' => 'success',
-      'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars(implode(', ', $usernames)))
-    );
-  }
-  // Domain administrator
-  // Can only edit itself
-  elseif ($_SESSION['mailcow_cc_role'] == "domainadmin") {
-    $username = $_SESSION['mailcow_cc_username'];
-    $password_old		= $postarray['user_old_pass'];
-    $password_new	= $postarray['user_new_pass'];
-    $password_new2	= $postarray['user_new_pass2'];
-
-    $stmt = $pdo->prepare("SELECT `password` FROM `admin`
-        WHERE `username` = :user");
-    $stmt->execute(array(':user' => $username));
-    $row = $stmt->fetch(PDO::FETCH_ASSOC);
-    if (!verify_ssha256($row['password'], $password_old)) {
-      $_SESSION['return'] = array(
-        'type' => 'danger',
-        'msg' => sprintf($lang['danger']['access_denied'])
-      );
-      return false;
-    }
-
-    if (!empty($password_new2) && !empty($password_new)) {
-      if ($password_new2 != $password_new) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['password_mismatch'])
-        );
-        return false;
-      }
-      if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password_new)) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => sprintf($lang['danger']['password_complexity'])
-        );
-        return false;
-      }
-      $password_hashed = hash_password($password_new);
-      try {
-        $stmt = $pdo->prepare("UPDATE `admin` SET `password` = :password_hashed WHERE `username` = :username");
-        $stmt->execute(array(
-          ':password_hashed' => $password_hashed,
-          ':username' => $username
-        ));
-      }
-      catch (PDOException $e) {
-        $_SESSION['return'] = array(
-          'type' => 'danger',
-          'msg' => 'MySQL: '.$e
-        );
-        return false;
-      }
-    }
-    
-    $_SESSION['return'] = array(
-      'type' => 'success',
-      'msg' => sprintf($lang['success']['domain_admin_modified'], htmlspecialchars($username))
-    );
-  }
-}
 function get_admin_details() {
   // No parameter to be given, only one admin should exist
 	global $pdo;
@@ -1438,4 +885,4 @@ function get_logs($container, $lines = 100) {
   }
   return false;
 }
-?>
+?>

+ 105 - 29
data/web/inc/functions.mailbox.inc.php

@@ -879,7 +879,7 @@ function mailbox($_action, $_type, $_data = null) {
             $alias_domain = idn_to_ascii(strtolower(trim($alias_domain)));
             $is_now = mailbox('get', 'alias_domain_details', $alias_domain);
             if (!empty($is_now)) {
-              $active         = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
+              $active         = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
               $target_domain  = (!empty($_data['target_domain'])) ? idn_to_ascii(strtolower(trim($_data['target_domain']))) : $is_now['target_domain'];
             }
             else {
@@ -903,7 +903,7 @@ function mailbox($_action, $_type, $_data = null) {
               );
               return false;
             }
-            if (empty(mailbox('get', 'domain_details', $target_domain))) {
+            if (empty(mailbox('get', 'domain_details', $target_domain)) || !empty(mailbox('get', 'alias_domain_details', $target_domain))) {
               $_SESSION['return'] = array(
                 'type' => 'danger',
                 'msg' => sprintf($lang['danger']['target_domain_invalid'])
@@ -950,12 +950,10 @@ function mailbox($_action, $_type, $_data = null) {
               );
               return false;
             }
-            $tls_enforce_out = intval($_data['tls_enforce_out']);
-            $tls_enforce_in = intval($_data['tls_enforce_in']);
             $is_now = mailbox('get', 'tls_policy', $username);
             if (!empty($is_now)) {
-              $tls_enforce_in = (isset($_data['tls_enforce_in'])) ? $_data['tls_enforce_in'] : $is_now['tls_enforce_in'];
-              $tls_enforce_out = (isset($_data['tls_enforce_out'])) ? $_data['tls_enforce_out'] : $is_now['tls_enforce_out'];
+              $tls_enforce_in = (isset($_data['tls_enforce_in'])) ? intval($_data['tls_enforce_in']) : $is_now['tls_enforce_in'];
+              $tls_enforce_out = (isset($_data['tls_enforce_out'])) ? intval($_data['tls_enforce_out']) : $is_now['tls_enforce_out'];
             }
             else {
               $_SESSION['return'] = array(
@@ -1136,6 +1134,63 @@ function mailbox($_action, $_type, $_data = null) {
             'msg' => sprintf($lang['success']['mailbox_modified'], implode(', ', $usernames))
           );
         break;
+        case 'domain_ratelimit':
+          $rl_value = intval($_data['rl_value']);
+          $rl_frame = $_data['rl_frame'];
+          if (!in_array($rl_frame, array('s', 'm', 'h'))) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => 'Ratelimit time frame is incorrect'
+              );
+              return false;
+          }
+          if (!is_array($_data['domain'])) {
+            $domains = array();
+            $domains[] = $_data['domain'];
+          }
+          else {
+            $domains = $_data['domain'];
+          }
+          foreach ($domains as $domain) {
+            if (!is_valid_domain_name($domain) || !hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
+              $_SESSION['return'] = array(
+                'type' => 'danger',
+                'msg' => sprintf($lang['danger']['access_denied'])
+              );
+              return false;
+            }
+            if (empty($rl_value)) {
+              try {
+                $redis->hDel('RL_OBJECT', $domain);
+                $redis->hDel('RL_VALUE', $domain);
+              }
+              catch (RedisException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'Redis: '.$e
+                );
+                return false;
+              }
+            }
+            else {
+              try {
+                $redis->hSet('RL_OBJECT', $domain, '1');
+                $redis->hSet('RL_VALUE', $domain, $rl_value . ' / 1' . $rl_frame);
+              }
+              catch (RedisException $e) {
+                $_SESSION['return'] = array(
+                  'type' => 'danger',
+                  'msg' => 'Redis: '.$e
+                );
+                return false;
+              }
+            }
+          }
+          $_SESSION['return'] = array(
+            'type' => 'success',
+            'msg' => sprintf($lang['success']['domain_modified'], implode(', ', $domains))
+          );
+        break;
         case 'syncjob':
           if (!is_array($_data['id'])) {
             $ids = array();
@@ -1149,9 +1204,9 @@ function mailbox($_action, $_type, $_data = null) {
             if (!empty($is_now)) {
               $username = $is_now['user2'];
               $user1 = (!empty($_data['user1'])) ? $_data['user1'] : $is_now['user1'];
-              $active = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
-              $delete2duplicates = (isset($_data['delete2duplicates'])) ? $_data['delete2duplicates'] : $is_now['delete2duplicates'];
-              $delete1 = (isset($_data['delete1'])) ? $_data['delete1'] : $is_now['delete1'];
+              $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
+              $delete2duplicates = (isset($_data['delete2duplicates'])) ? intval($_data['delete2duplicates']) : $is_now['delete2duplicates'];
+              $delete1 = (isset($_data['delete1'])) ? intval($_data['delete1']) : $is_now['delete1'];
               $port1 = (!empty($_data['port1'])) ? $_data['port1'] : $is_now['port1'];
               $password1 = (!empty($_data['password1'])) ? $_data['password1'] : $is_now['password1'];
               $host1 = (!empty($_data['host1'])) ? $_data['host1'] : $is_now['host1'];
@@ -1253,7 +1308,7 @@ function mailbox($_action, $_type, $_data = null) {
           foreach ($addresses as $address) {
             $is_now = mailbox('get', 'alias_details', $address);
             if (!empty($is_now)) {
-              $active = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
+              $active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
               $goto   = (!empty($_data['goto'])) ? $_data['goto'] : $is_now['goto'];
             }
             else {
@@ -1383,9 +1438,9 @@ function mailbox($_action, $_type, $_data = null) {
             elseif ($_SESSION['mailcow_cc_role'] == "admin") {
               $is_now = mailbox('get', 'domain_details', $domain);
               if (!empty($is_now)) {
-                $active               = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
-                $backupmx             = (isset($_data['backupmx'])) ? $_data['backupmx'] : $is_now['backupmx_int'];
-                $relay_all_recipients = (isset($_data['relay_all_recipients'])) ? $_data['relay_all_recipients'] : $is_now['relay_all_recipients_int'];
+                $active               = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
+                $backupmx             = (isset($_data['backupmx'])) ? intval($_data['backupmx']) : $is_now['backupmx_int'];
+                $relay_all_recipients = (isset($_data['relay_all_recipients'])) ? intval($_data['relay_all_recipients']) : $is_now['relay_all_recipients_int'];
                 $aliases              = (!empty($_data['aliases'])) ? $_data['aliases'] : $is_now['max_num_aliases_for_domain'];
                 $mailboxes            = (!empty($_data['mailboxes'])) ? $_data['mailboxes'] : $is_now['max_num_mboxes_for_domain'];
                 $maxquota             = (!empty($_data['maxquota'])) ? $_data['maxquota'] : ($is_now['max_quota_for_mbox'] / 1048576);
@@ -1524,7 +1579,7 @@ function mailbox($_action, $_type, $_data = null) {
             }
             $is_now = mailbox('get', 'mailbox_details', $username);
             if (!empty($is_now)) {
-              $active     = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
+              $active     = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
               $name       = (!empty($_data['name'])) ? $_data['name'] : $is_now['name'];
               $domain     = $is_now['domain'];
               $quota_m    = (!empty($_data['quota'])) ? $_data['quota'] : ($is_now['quota'] / 1048576);
@@ -1588,19 +1643,15 @@ function mailbox($_action, $_type, $_data = null) {
                 mailbox('get', 'sender_acl_handles', $username)['sender_acl_addresses']['ro']
               );
               // Get sender_acl items from POST array
-              $sender_acl_domain_admin = ($_data['sender_acl'] == "0") ? array() : $_data['sender_acl'];
+              $sender_acl_domain_admin = ($_data['sender_acl'] == "0") ? array() : (array)$_data['sender_acl'];
               if (!empty($sender_acl_domain_admin) || !empty($sender_acl_admin)) {
-                // Check items in POST array
-                foreach ($sender_acl_domain_admin as $sender_acl) {
-                  if (!filter_var($sender_acl, FILTER_VALIDATE_EMAIL) && !is_valid_domain_name(ltrim($sender_acl, '@'))) {
-                      $_SESSION['return'] = array(
-                        'type' => 'danger',
-                        'msg' => sprintf($lang['danger']['sender_acl_invalid'])
-                      );
-                      return false;
+                // Check items in POST array and skip invalid
+                foreach ($sender_acl_domain_admin as $key => $val) {
+                  if (!filter_var($val, FILTER_VALIDATE_EMAIL) && !is_valid_domain_name(ltrim($val, '@'))) {
+                    unset($sender_acl_domain_admin[$key]);
                   }
-                  if (is_valid_domain_name(ltrim($sender_acl, '@'))) {
-                    if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], ltrim($sender_acl, '@'))) {
+                  if (is_valid_domain_name(ltrim($val, '@'))) {
+                    if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], ltrim($val, '@'))) {
                       $_SESSION['return'] = array(
                         'type' => 'danger',
                         'msg' => sprintf($lang['danger']['sender_acl_invalid'])
@@ -1608,8 +1659,8 @@ function mailbox($_action, $_type, $_data = null) {
                       return false;
                     }
                   }
-                  if (filter_var($sender_acl, FILTER_VALIDATE_EMAIL)) {
-                    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $sender_acl)) {
+                  if (filter_var($val, FILTER_VALIDATE_EMAIL)) {
+                    if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $val)) {
                       $_SESSION['return'] = array(
                         'type' => 'danger',
                         'msg' => sprintf($lang['danger']['sender_acl_invalid'])
@@ -1761,8 +1812,8 @@ function mailbox($_action, $_type, $_data = null) {
           foreach ($names as $name) {
             $is_now = mailbox('get', 'resource_details', $name);
             if (!empty($is_now)) {
-              $active             = (isset($_data['active'])) ? $_data['active'] : $is_now['active_int'];
-              $multiple_bookings  = (isset($_data['multiple_bookings'])) ? $_data['multiple_bookings'] : $is_now['multiple_bookings_int'];
+              $active             = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
+              $multiple_bookings  = (isset($_data['multiple_bookings'])) ? intval($_data['multiple_bookings']) : $is_now['multiple_bookings_int'];
               $description        = (!empty($_data['description'])) ? $_data['description'] : $is_now['description'];
               $kind               = (!empty($_data['kind'])) ? $_data['kind'] : $is_now['kind'];
             }
@@ -2267,6 +2318,31 @@ function mailbox($_action, $_type, $_data = null) {
           }
           return $aliases;
         break;
+        case 'domain_ratelimit':
+          $aliases = array();
+          if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
+            return false;
+          }
+          try {
+            if (($rl_value = $redis->hGet('RL_VALUE', $_data)) && $redis->hGet('RL_OBJECT', $_data)) {
+              $rl = explode(' / 1', $rl_value);
+              $data['value'] = $rl[0];
+              $data['frame'] = $rl[1];
+              return $data;
+            }
+            else {
+              return false;
+            }
+          }
+          catch (RedisException $e) {
+            $_SESSION['return'] = array(
+              'type' => 'danger',
+              'msg' => 'Redis: '.$e
+            );
+            return false;
+          }
+          return false;
+        break;
         case 'alias_details':
           $aliasdata = array();
           try {

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

@@ -61,6 +61,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.en.php';
 include $_SERVER['DOCUMENT_ROOT'] . '/lang/lang.'.$_SESSION['mailcow_locale'].'.php';
 require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.inc.php';
 require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php';
+require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.domain_admin.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';

+ 2 - 0
data/web/inc/sessions.inc.php

@@ -1,6 +1,8 @@
 <?php
 // Start session
 ini_set("session.cookie_httponly", 1);
+ini_set('session.gc_maxlifetime', $SESSION_LIFETIME);
+
 if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 
   strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == "https") {
   ini_set("session.cookie_secure", 1);

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

@@ -54,53 +54,12 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
     }
   }
 }
-if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "user") {
-	if (isset($_POST["edit_user_account"])) {
-		edit_user_account($_POST);
-	}
-	if (isset($_POST["edit_syncjob"])) {
-		mailbox('edit', 'syncjob', $_POST);
-	}
-}
 if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "admin" || $_SESSION['mailcow_cc_role'] == "domainadmin")) {
-	if (isset($_POST["edit_domain_admin"])) {
-		edit_domain_admin($_POST);
-	}
 	if (isset($_POST["set_tfa"])) {
 		set_tfa($_POST);
 	}
 	if (isset($_POST["unset_tfa_key"])) {
 		unset_tfa_key($_POST);
 	}
-	if (isset($_POST["mailbox_edit_alias"])) {
-		mailbox('edit', 'alias', $_POST);
-	}
-	if (isset($_POST["mailbox_edit_domain"])) {
-		mailbox('edit', 'domain', $_POST);
-	}
-	if (isset($_POST["mailbox_edit_mailbox"])) {
-		mailbox('edit', 'mailbox', $_POST);
-	}
-	if (isset($_POST["mailbox_edit_alias_domain"])) {
-		mailbox('edit', 'alias_domain', $_POST);
-	}
-	if (isset($_POST["mailbox_edit_resource"])) {
-		mailbox('edit', 'resource', $_POST);
-	}
-	if (isset($_POST["mailbox_delete_domain"])) {
-		mailbox('delete', 'domain', $_POST);
-	}
-	if (isset($_POST["mailbox_delete_alias"])) {
-		mailbox('delete', 'delete_alias', $_POST);
-	}
-	if (isset($_POST["mailbox_delete_alias_domain"])) {
-		mailbox('delete', 'alias_domain', $_POST);
-	}
-	if (isset($_POST["mailbox_delete_mailbox"])) {
-		mailbox('delete', 'mailbox', $_POST);
-	}
-	if (isset($_POST["mailbox_delete_resource"])) {
-		mailbox('delete', 'resource', $_POST);
-	}
 }
 ?>

+ 18 - 2
data/web/js/api.js

@@ -64,8 +64,23 @@ $(document).ready(function() {
     // If clicked element #edit_selected is in a form with the same data-id as the button,
     // we merge all input fields by {"name":"value"} into api-attr
     if ($(this).closest("form").data('id') == id) {
-      var attr_to_merge = $(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;
+      }
     }
     // If clicked element #edit_selected has data-item attribute, it is added to "items"
     if (typeof $(this).data('item') !== 'undefined') {
@@ -77,6 +92,7 @@ $(document).ready(function() {
     }
     if (typeof multi_data[id] == "undefined") return;
     api_items = multi_data[id];
+    // alert(JSON.stringify(api_attr));
     if (Object.keys(api_items).length !== 0) {
       $.ajax({
         type: "POST",

+ 145 - 30
data/web/json_api.php

@@ -426,7 +426,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
           case "domain-admin":
             if (isset($_POST['attr'])) {
               $attr = (array)json_decode($_POST['attr'], true);
-              if (add_domain_admin($attr) === false) {
+              if (domain_admin('add', $attr) === false) {
                 if (isset($_SESSION['return'])) {
                   echo json_encode($_SESSION['return']);
                 }
@@ -826,10 +826,10 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
           case "domain-admin":
             switch ($object) {
               case "all":
-                $domain_admins = get_domain_admins();
+                $domain_admins = domain_admin('get');
                 if (!empty($domain_admins)) {
                   foreach ($domain_admins as $domain_admin) {
-                    if ($details = get_domain_admin_details($domain_admin)) {
+                    if ($details = domain_admin('details', $domain_admin)) {
                       $data[] = $details;
                     }
                     else {
@@ -849,7 +849,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               break;
 
               default:
-                $data = get_domain_admin_details($object);
+                $data = domain_admin('details', $object);
                 if (!isset($data) || empty($data)) {
                   echo '{}';
                 }
@@ -1385,7 +1385,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_domain_admin(array('username' => $items)) === false) {
+                if (domain_admin('delete', array('username' => $items)) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -1603,6 +1603,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
             }
           break;
           case "mailbox":
+            // sender_acl:0 removes all entries
             if (isset($_POST['items']) && isset($_POST['attr'])) {
               $items = (array)json_decode($_POST['items'], true);
               $attr = (array)json_decode($_POST['attr'], true);
@@ -1778,6 +1779,50 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               ));
             }
           break;
+          case "domain-ratelimit":
+            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('domain' => $items), $attr);
+              if (is_array($postarray['domain'])) {
+                if (mailbox('edit', 'domain_ratelimit', $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 "alias-domain":
             if (isset($_POST['items']) && isset($_POST['attr'])) {
               $items = (array)json_decode($_POST['items'], true);
@@ -1822,7 +1867,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               ));
             }
           break;
-          case "spam_score":
+          case "spam-score":
             if (isset($_POST['items']) && isset($_POST['attr'])) {
               $items = (array)json_decode($_POST['items'], true);
               $attr = (array)json_decode($_POST['attr'], true);
@@ -1872,7 +1917,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               $attr = (array)json_decode($_POST['attr'], true);
               $postarray = array_merge(array('username' => $items), $attr);
               if (is_array($postarray['username'])) {
-                if (edit_domain_admin($postarray) === false) {
+                if (domain_admin('edit', $postarray) === false) {
                   if (isset($_SESSION['return'])) {
                     echo json_encode($_SESSION['return']);
                   }
@@ -1989,39 +2034,109 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
               ));
             }
           break;
-          case "admin":
-            // No items as there is only one admin
-            if (isset($_POST['attr'])) {
-              $attr = (array)json_decode($_POST['attr'], true);
-              if (edit_admin_account($attr) === false) {
-                if (isset($_SESSION['return'])) {
-                  echo json_encode($_SESSION['return']);
+          case "self":
+            // No items, logged-in user, users and domain admins
+            if ($_SESSION['mailcow_cc_role'] == "domainadmin") {
+              if (isset($_POST['attr'])) {
+                $attr = (array)json_decode($_POST['attr'], true);
+                if (domain_admin('edit', $attr) === false) {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'error',
+                      'msg' => 'Edit failed'
+                    ));
+                  }
+                  exit();
                 }
                 else {
-                  echo json_encode(array(
-                    'type' => 'error',
-                    'msg' => 'Edit failed'
-                  ));
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'success',
+                      'msg' => 'Task completed'
+                    ));
+                  }
                 }
-                exit();
               }
               else {
-                if (isset($_SESSION['return'])) {
-                  echo json_encode($_SESSION['return']);
+                echo json_encode(array(
+                  'type' => 'error',
+                  'msg' => 'Incomplete post data'
+                ));
+              }
+            }
+            elseif ($_SESSION['mailcow_cc_role'] == "user") {
+              if (isset($_POST['attr'])) {
+                $attr = (array)json_decode($_POST['attr'], true);
+                if (edit_user_account($attr) === false) {
+                  if (isset($_SESSION['return'])) {
+                    echo json_encode($_SESSION['return']);
+                  }
+                  else {
+                    echo json_encode(array(
+                      'type' => 'error',
+                      'msg' => 'Edit failed'
+                    ));
+                  }
+                  exit();
                 }
                 else {
-                  echo json_encode(array(
-                    'type' => 'success',
-                    'msg' => 'Task completed'
-                  ));
+                  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'
-              ));
+            elseif ($_SESSION['mailcow_cc_role'] == "admin") {
+              if (isset($_POST['attr'])) {
+                $attr = (array)json_decode($_POST['attr'], true);
+                if (edit_admin_account($attr) === 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'
+                ));
+              }
             }
           break;
         }

+ 2 - 2
data/web/user.php

@@ -357,7 +357,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "use
   <div class="modal-dialog" role="document">
     <div class="modal-content">
       <div class="modal-body">
-        <form class="form-horizontal" role="form" method="post" autocomplete="off">
+        <form class="form-horizontal" data-id="pwchange" role="form" method="post" autocomplete="off">
           <div class="form-group">
             <label class="control-label col-sm-3" for="user_new_pass"><?=$lang['user']['new_password'];?></label>
             <div class="col-sm-5">
@@ -380,7 +380,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "use
           </div>
           <div class="form-group">
             <div class="col-sm-offset-3 col-sm-9">
-              <button type="submit" name="edit_<?=($_SESSION['mailcow_cc_role'] == "domainadmin") ? "domain_admin" : "user_account";?>" class="btn btn-sm btn-success"><?=$lang['user']['change_password'];?></button>
+              <button class="btn btn-default" id="edit_selected" data-id="pwchange" data-item="null" data-api-url='edit/self' data-api-attr='{}' href="#"><?=$lang['user']['change_password'];?></button>
             </div>
           </div>
         </form>