From 1e6a032d1e38149ace8ff922b70ca55496eb9801 Mon Sep 17 00:00:00 2001 From: heavygale Date: Sun, 29 Sep 2019 17:45:36 +0200 Subject: [PATCH 1/5] displaying assigned domain quota adding the amount of assigned domain quota to the disk usage display as potentially used disk space --- data/web/css/site/debug.css | 12 +++++++++++- data/web/debug.php | 10 ++++++++-- data/web/inc/functions.domain_admin.inc.php | 21 +++++++++++++++++---- data/web/lang/lang.de.php | 15 +++++++++++---- data/web/lang/lang.en.php | 5 ++++- 5 files changed, 51 insertions(+), 12 deletions(-) diff --git a/data/web/css/site/debug.css b/data/web/css/site/debug.css index 89eaf41a..9b45f028 100644 --- a/data/web/css/site/debug.css +++ b/data/web/css/site/debug.css @@ -44,4 +44,14 @@ tbody { .container-indicator { width: 15px; height: 15px; -} \ No newline at end of file +} +.disk_space { + margin-top: 20px; +} +.progress-bar-committed { + background-color: #B6E1F2; +} +.usage-info { + display: inline-block; + margin-right: 5px; +} diff --git a/data/web/debug.php b/data/web/debug.php index 67417bc2..09bd44b5 100644 --- a/data/web/debug.php +++ b/data/web/debug.php @@ -43,6 +43,10 @@ else { 'system', 'task' => 'df', 'dir' => '/var/vmail'); $vmail_df = explode(',', json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true)); + $domainQuota = round(domain_admin('total_quota')/1024); + $quotaPercent1 = round(($domainQuota/substr($vmail_df[3], 0, -1))*100); + $quotaPercent2 = round((($domainQuota-substr($vmail_df[2], 0, -1))/substr($vmail_df[3], 0, -1))*100); + $quotaPercent2 = ($quotaPercent2+substr($vmail_df[4], 0, -1)>100) ? 100-substr($vmail_df[4], 0, -1) : $quotaPercent2; //handling overcommitment ?>
@@ -53,12 +57,15 @@ else {

/var/vmail on

-

/ ()

+

B

+
+

  B ()

+

  GB (%)

@@ -344,7 +351,6 @@ $lang_admin = json_encode($lang['admin']); echo "var lang = ". $lang_admin . ";\n"; echo "var csrf_token = '". $_SESSION['CSRF']['TOKEN'] . "';\n"; echo "var log_pagination_size = '". $LOG_PAGINATION_SIZE . "';\n"; - ?> 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)); @@ -373,7 +373,7 @@ function domain_admin($_action, $_data = null) { } $stmt = $pdo->query("SELECT DISTINCT `username` - FROM `domain_admins` + FROM `domain_admins` WHERE `username` IN ( SELECT `username` FROM `admin` WHERE `superadmin`!='1' @@ -409,7 +409,7 @@ function domain_admin($_action, $_data = null) { ':domain_admin' => $_data )); $row = $stmt->fetch(PDO::FETCH_ASSOC); - if (empty($row)) { + if (empty($row)) { return false; } $domainadmindata['username'] = $row['username']; @@ -444,5 +444,18 @@ function domain_admin($_action, $_data = null) { return $domainadmindata; break; + case 'total_quota': + if ($_SESSION['mailcow_cc_role'] != "admin") { + $_SESSION['return'][] = array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'access_denied' + ); + return false; + } + $stmt = $pdo->query("SELECT SUM(`quota`) AS `quota` FROM `domain`"); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + return $row['quota']; + break; } } diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index 1f671b3d..e0f859a1 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -150,7 +150,7 @@ $lang['success']['resource_removed'] = 'Ressource %s wurde entfernt'; $lang['warning']['cannot_delete_self'] = 'Kann derzeit eingeloggten Benutzer nicht entfernen'; $lang['warning']['no_active_admin'] = 'Kann letzten aktiven Administrator nicht deaktivieren'; $lang['danger']['max_quota_in_use'] = 'Mailbox Speicherplatzlimit muss größer oder gleich %d MiB sein'; -$lang['danger']['domain_quota_m_in_use'] = 'Domain Speicherplatzlimit muss größer oder gleich %d MiB sein'; +$lang['danger']['domain_quota_m_in_use'] = 'Domain-Speicherplatzlimit muss größer oder gleich %d MiB sein'; $lang['danger']['mailboxes_in_use'] = 'Maximale Anzahl an Mailboxen muss größer oder gleich %d sein'; $lang['danger']['aliases_in_use'] = 'Maximale Anzahl an Aliassen muss größer oder gleich %d sein'; $lang['danger']['sender_acl_invalid'] = 'Sender ACL %s ist ungültig'; @@ -375,7 +375,7 @@ $lang['edit']['mailbox'] = 'Mailbox bearbeiten'; $lang['edit']['description'] = 'Beschreibung'; $lang['edit']['max_aliases'] = 'Max. Aliasse'; $lang['edit']['max_quota'] = 'Max. Größe per Mailbox (MiB)'; -$lang['edit']['domain_quota'] = 'Domain Speicherplatz gesamt (MiB)'; +$lang['edit']['domain_quota'] = 'Domain-Speicherplatz gesamt (MiB)'; $lang['edit']['backup_mx_options'] = 'Backup MX Optionen'; $lang['edit']['relay_domain'] = 'Diese Domain relayen'; $lang['edit']['relay_all'] = 'Alle Empfänger-Adressen relayen'; @@ -474,7 +474,7 @@ $lang['add']['description'] = 'Beschreibung'; $lang['add']['max_aliases'] = 'Max. mögliche Aliasse'; $lang['add']['max_mailboxes'] = 'Max. mögliche Mailboxen'; $lang['add']['mailbox_quota_m'] = 'Max. Speicherplatz pro Mailbox (MiB)'; -$lang['add']['domain_quota_m'] = 'Domain Speicherplatz gesamt (MiB)'; +$lang['add']['domain_quota_m'] = 'Domain-Speicherplatz gesamt (MiB)'; $lang['add']['backup_mx_options'] = 'Backup MX Optionen'; $lang['add']['relay_all'] = 'Alle Empfänger-Adressen relayen'; $lang['add']['relay_domain'] = 'Relay Domain'; @@ -545,7 +545,7 @@ $lang['tfa']['confirm_totp_token'] = "Bitte bestätigen Sie die Änderung durch $lang['admin']['rspamd-com_settings'] = 'Rspamd docs - Ein Name wird automatisch generiert. Beispielinhalte zur Einsicht stehen nachstehend bereit.'; - + $lang['admin']['no_new_rows'] = 'Keine weiteren Zeilen vorhanden'; $lang['admin']['additional_rows'] = ' zusätzliche Zeilen geladen'; // parses to 'n additional rows were added' $lang['admin']['private_key'] = 'Private Key'; @@ -791,6 +791,13 @@ $lang['debug']['solr_last_modified'] = 'Zuletzt geändert'; $lang['debug']['solr_size'] = 'Größe'; $lang['debug']['solr_docs'] = 'Dokumente'; +$lang['debug']['disk_usage'] = 'Festplattenauslastung'; +$lang['debug']['containers_info'] = "Container-Informationen"; + +$lang['debug']['disk_space'] = "Verfügbarer Speicherplatz:"; +$lang['debug']['disk_used'] = "Von Mailboxen belegter Speicherplatz:"; +$lang['debug']['total_quota'] = "Zugewiesener Domain-Speicherplatz:"; + $lang['quarantine']['release_body'] = "Die ursprüngliche Nachricht wurde als EML-Datei im Anhang hinterlegt."; $lang['danger']['release_send_failed'] = "Die Nachricht konnte nicht versendet werden: %s"; $lang['quarantine']['release_subject'] = "Potentiell schädliche Nachricht aus Quarantäne: %s"; diff --git a/data/web/lang/lang.en.php b/data/web/lang/lang.en.php index ec0d2019..be7033db 100644 --- a/data/web/lang/lang.en.php +++ b/data/web/lang/lang.en.php @@ -820,6 +820,10 @@ $lang['debug']['disk_usage'] = 'Disk usage'; $lang['debug']['containers_info'] = "Container information"; $lang['debug']['restart_container'] = 'Restart'; +$lang['debug']['disk_space'] = "Available disk space:"; +$lang['debug']['disk_used'] = "Disk space occupied by mailboxes:"; +$lang['debug']['total_quota'] = "Total amount of assigned domain quota:"; + $lang['quarantine']['release_body'] = "We have attached your message as eml file to this message."; $lang['danger']['release_send_failed'] = "Message could not be released: %s"; $lang['quarantine']['release_subject'] = "Potentially damaging quarantine item %s"; @@ -915,4 +919,3 @@ $lang['mailbox']['alias_domain_backupmx'] = 'Alias domain inactive for relay dom $lang['danger']['extra_acl_invalid'] = 'External sender address "%s" is invalid'; $lang['danger']['extra_acl_invalid_domain'] = 'External sender "%s" uses an invalid domain'; - From f01e3e59c58f09bfaf31810fd062241b5271a416 Mon Sep 17 00:00:00 2001 From: heavygale Date: Sun, 29 Sep 2019 19:04:00 +0200 Subject: [PATCH 2/5] don't count assigned domain quota for inactive domains now only summing up the assigned domain quota for active domains, and not subtracting disc base occupied by mailboxes of inactive domains (inactive_bytes) --- data/web/debug.php | 3 +- data/web/inc/functions.domain_admin.inc.php | 2 +- data/web/inc/functions.mailbox.inc.php | 49 +++++++++++++-------- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/data/web/debug.php b/data/web/debug.php index 09bd44b5..eaa4b9a9 100644 --- a/data/web/debug.php +++ b/data/web/debug.php @@ -44,8 +44,9 @@ else { $exec_fields = array('cmd' => 'system', 'task' => 'df', 'dir' => '/var/vmail'); $vmail_df = explode(',', json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true)); $domainQuota = round(domain_admin('total_quota')/1024); + $inactive_bytes = round(mailbox('get','inactive_bytes')/1024/1024/1024); $quotaPercent1 = round(($domainQuota/substr($vmail_df[3], 0, -1))*100); - $quotaPercent2 = round((($domainQuota-substr($vmail_df[2], 0, -1))/substr($vmail_df[3], 0, -1))*100); + $quotaPercent2 = round((($domainQuota-substr($vmail_df[2], 0, -1)+$inactive_bytes)/substr($vmail_df[3], 0, -1))*100); $quotaPercent2 = ($quotaPercent2+substr($vmail_df[4], 0, -1)>100) ? 100-substr($vmail_df[4], 0, -1) : $quotaPercent2; //handling overcommitment ?>
diff --git a/data/web/inc/functions.domain_admin.inc.php b/data/web/inc/functions.domain_admin.inc.php index 2bd2b807..7fe4b3fa 100644 --- a/data/web/inc/functions.domain_admin.inc.php +++ b/data/web/inc/functions.domain_admin.inc.php @@ -453,7 +453,7 @@ function domain_admin($_action, $_data = null) { ); return false; } - $stmt = $pdo->query("SELECT SUM(`quota`) AS `quota` FROM `domain`"); + $stmt = $pdo->query("SELECT SUM(`quota`) AS `quota` FROM `domain` WHERE `active`=1"); $row = $stmt->fetch(PDO::FETCH_ASSOC); return $row['quota']; break; diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 99fda2d8..0b781a6c 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -46,7 +46,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $stmt = $pdo->prepare("SELECT `domain` FROM `mailbox` WHERE `username` = :username"); $stmt->execute(array(':username' => $_SESSION['mailcow_cc_username'])); $domain = $stmt->fetch(PDO::FETCH_ASSOC)['domain']; - $validity = strtotime("+".$_data["validity"]." hour"); + $validity = strtotime("+".$_data["validity"]." hour"); $letters = 'abcefghijklmnopqrstuvwxyz1234567890'; $random_name = substr(str_shuffle($letters), 0, 24); $stmt = $pdo->prepare("INSERT INTO `spamalias` (`address`, `goto`, `validity`) VALUES @@ -485,7 +485,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { 'msg' => 'comment_too_long' ); return false; - } + } if (empty($addresses[0])) { $_SESSION['return'][] = array( 'type' => 'danger', @@ -841,7 +841,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { WHERE `domain` = :domain"); $stmt->execute(array(':domain' => $domain)); $DomainData = $stmt->fetch(PDO::FETCH_ASSOC); - $stmt = $pdo->prepare("SELECT + $stmt = $pdo->prepare("SELECT COUNT(*) as count, COALESCE(ROUND(SUM(`quota`)/1048576), 0) as `quota` FROM `mailbox` @@ -945,7 +945,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ); return false; } - $stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `quota`, `local_part`, `domain`, `attributes`, `active`) + $stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `quota`, `local_part`, `domain`, `attributes`, `active`) VALUES (:username, :password_hashed, :name, :quota_b, :local_part, :domain, :mailbox_attrs, :active)"); $stmt->execute(array( ':username' => $username, @@ -1073,7 +1073,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ); return false; } - $stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `quota`, `local_part`, `domain`, `active`, `multiple_bookings`, `kind`) + $stmt = $pdo->prepare("INSERT INTO `mailbox` (`username`, `password`, `name`, `quota`, `local_part`, `domain`, `active`, `multiple_bookings`, `kind`) VALUES (:name, 'RESOURCE', :description, 0, :local_part, :domain, :active, :multiple_bookings, :kind)"); $stmt->execute(array( ':name' => $name, @@ -1249,7 +1249,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { 'msg' => 'access_denied' ); continue; - } + } $stmt = $pdo->prepare("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.quarantine_notification', :quarantine_notification) WHERE `username` = :username"); @@ -1360,7 +1360,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { continue; } $validity = round((int)time() + ($_data['validity'] * 3600)); - $stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = :validity WHERE + $stmt = $pdo->prepare("UPDATE `spamalias` SET `validity` = :validity WHERE `address` = :address"); $stmt->execute(array( ':address' => $address, @@ -1888,7 +1888,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ); continue; } - $stmt = $pdo->prepare("UPDATE `domain` SET + $stmt = $pdo->prepare("UPDATE `domain` SET `description` = :description, `gal` = :gal WHERE `domain` = :domain"); @@ -1928,7 +1928,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { continue; } // todo: should be using api here - $stmt = $pdo->prepare("SELECT + $stmt = $pdo->prepare("SELECT COUNT(*) AS count, MAX(COALESCE(ROUND(`quota`/1048576), 0)) AS `biggest_mailbox`, COALESCE(ROUND(SUM(`quota`)/1048576), 0) AS `quota_all` @@ -2009,7 +2009,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ); continue; } - $stmt = $pdo->prepare("UPDATE `domain` SET + $stmt = $pdo->prepare("UPDATE `domain` SET `relay_all_recipients` = :relay_all_recipients, `backupmx` = :backupmx, `gal` = :gal, @@ -2071,7 +2071,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $domain = $is_now['domain']; $quota_b = $quota_m * 1048576; $password = (!empty($_data['password'])) ? $_data['password'] : null; - $password2 = (!empty($_data['password2'])) ? $_data['password2'] : null; + $password2 = (!empty($_data['password2'])) ? $_data['password2'] : null; } else { $_SESSION['return'][] = array( @@ -2517,14 +2517,14 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { } $stmt = $pdo->prepare("SELECT `domain` FROM `domain` WHERE `domain` NOT IN ( - SELECT REPLACE(`send_as`, '@', '') FROM `sender_acl` + SELECT REPLACE(`send_as`, '@', '') FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as1 AND `external` = '0' AND `send_as` LIKE '@%') UNION SELECT '*' FROM `domain` WHERE '*' NOT IN ( - SELECT `send_as` FROM `sender_acl` + SELECT `send_as` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as2 AND `external` = '0' )"); @@ -2546,7 +2546,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { $stmt = $pdo->prepare("SELECT `address` FROM `alias` WHERE `goto` != :goto AND `address` NOT IN ( - SELECT `send_as` FROM `sender_acl` + SELECT `send_as` FROM `sender_acl` WHERE `logged_in_as` = :logged_in_as AND `external` = '0' AND `send_as` NOT LIKE '@%')"); @@ -3074,11 +3074,11 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { if (!empty($row)) { $_data = $row['target_domain']; } - $stmt = $pdo->prepare("SELECT + $stmt = $pdo->prepare("SELECT `domain`, `description`, `aliases`, - `mailboxes`, + `mailboxes`, `defquota`, `maxquota`, `quota`, @@ -3096,7 +3096,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { ':domain' => $_data )); $row = $stmt->fetch(PDO::FETCH_ASSOC); - if (empty($row)) { + if (empty($row)) { return false; } $stmt = $pdo->prepare("SELECT COUNT(*) AS `count`, @@ -3254,6 +3254,19 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { } return $resourcedata; break; + case 'inactive_bytes': + if ($_SESSION['mailcow_cc_role'] != "admin") { + return false; + } + $stmt = $pdo->query("SELECT SUM(`quota2`.`bytes`) AS `bytes` + FROM `mailbox`, `quota2`, `domain` + WHERE `mailbox`.`kind` NOT REGEXP 'location|thing|group' + AND `mailbox`.`username` = `quota2`.`username` + AND `domain`.`domain` = `mailbox`.`domain` + AND `domain`.`active` = 0"); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + return $row['bytes']; + break; } break; case 'delete': @@ -3724,7 +3737,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { curl_setopt($curl, CURLOPT_HTTPHEADER,array('Content-Type: text/xml')); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_POST, 1); - curl_setopt($curl, CURLOPT_POSTFIELDS, 'user:' . $username . ''); + curl_setopt($curl, CURLOPT_POSTFIELDS, 'user:' . $username . ''); curl_setopt($curl, CURLOPT_TIMEOUT, 30); $response = curl_exec($curl); if ($response === false) { From 7c2a5ba37e51863a75039990c22d680871ad2791 Mon Sep 17 00:00:00 2001 From: heavygale Date: Sun, 29 Sep 2019 19:19:06 +0200 Subject: [PATCH 3/5] uniform spelling for quota --- data/web/lang/lang.de.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/web/lang/lang.de.php b/data/web/lang/lang.de.php index 9dd20b56..d0b6d119 100644 --- a/data/web/lang/lang.de.php +++ b/data/web/lang/lang.de.php @@ -149,7 +149,7 @@ $lang['success']['sogo_profile_reset'] = "ActiveSync Gerät des Benutzers %s wur $lang['success']['resource_removed'] = 'Ressource %s wurde entfernt'; $lang['warning']['cannot_delete_self'] = 'Kann derzeit eingeloggten Benutzer nicht entfernen'; $lang['warning']['no_active_admin'] = 'Kann letzten aktiven Administrator nicht deaktivieren'; -$lang['danger']['max_quota_in_use'] = 'Mailbox Speicherplatzlimit muss größer oder gleich %d MiB sein'; +$lang['danger']['max_quota_in_use'] = 'Mailbox-Speicherplatzlimit muss größer oder gleich %d MiB sein'; $lang['danger']['domain_quota_m_in_use'] = 'Domain-Speicherplatzlimit muss größer oder gleich %d MiB sein'; $lang['danger']['mailboxes_in_use'] = 'Maximale Anzahl an Mailboxen muss größer oder gleich %d sein'; $lang['danger']['aliases_in_use'] = 'Maximale Anzahl an Aliassen muss größer oder gleich %d sein'; From ecda554e5353b4b398165a05efdb8eea3ba5eaac Mon Sep 17 00:00:00 2001 From: heavygale Date: Tue, 1 Oct 2019 20:27:06 +0200 Subject: [PATCH 4/5] finalizing disk usage rework - changed dockerapi to not return human readable values for df - all caluclations now in KiB - moved code from domain_admin to mailbox functions - using actual occupied bytes as used quota - all outputs now in *iB using formatBytes() --- data/Dockerfiles/dockerapi/server.py | 22 ++++++++++----------- data/web/debug.php | 18 ++++++++--------- data/web/inc/functions.domain_admin.inc.php | 13 ------------ data/web/inc/functions.mailbox.inc.php | 11 ++++++++--- 4 files changed, 28 insertions(+), 36 deletions(-) diff --git a/data/Dockerfiles/dockerapi/server.py b/data/Dockerfiles/dockerapi/server.py index ede9433c..0c4672df 100644 --- a/data/Dockerfiles/dockerapi/server.py +++ b/data/Dockerfiles/dockerapi/server.py @@ -64,11 +64,11 @@ class container_post(Resource): print("api call: %s, container_id: %s" % (api_call_method_name, container_id)) return api_call_method(container_id) except Exception as e: - print("error - container_post: %s" % str(e)) + print("error - container_post: %s" % str(e)) return jsonify(type='danger', msg=str(e)) else: - return jsonify(type='danger', msg='invalid container id or missing action') + return jsonify(type='danger', msg='invalid container id or missing action') # api call: container_post - post_action: stop @@ -107,7 +107,7 @@ class container_post(Resource): # api call: container_post - post_action: exec - cmd: mailq - task: delete def container_post__exec__mailq__delete(self, container_id): - if 'items' in request.json: + if 'items' in request.json: r = re.compile("^[0-9a-fA-F]+$") filtered_qids = filter(r.match, request.json['items']) if filtered_qids: @@ -121,7 +121,7 @@ class container_post(Resource): # api call: container_post - post_action: exec - cmd: mailq - task: hold def container_post__exec__mailq__hold(self, container_id): - if 'items' in request.json: + if 'items' in request.json: r = re.compile("^[0-9a-fA-F]+$") filtered_qids = filter(r.match, request.json['items']) if filtered_qids: @@ -135,7 +135,7 @@ class container_post(Resource): # api call: container_post - post_action: exec - cmd: mailq - task: unhold def container_post__exec__mailq__unhold(self, container_id): - if 'items' in request.json: + if 'items' in request.json: r = re.compile("^[0-9a-fA-F]+$") filtered_qids = filter(r.match, request.json['items']) if filtered_qids: @@ -149,7 +149,7 @@ class container_post(Resource): # api call: container_post - post_action: exec - cmd: mailq - task: deliver def container_post__exec__mailq__deliver(self, container_id): - if 'items' in request.json: + if 'items' in request.json: r = re.compile("^[0-9a-fA-F]+$") filtered_qids = filter(r.match, request.json['items']) if filtered_qids: @@ -206,7 +206,7 @@ class container_post(Resource): def container_post__exec__system__df(self, container_id): if 'dir' in request.json: for container in docker_client.containers.list(filters={"id": container_id}): - df_return = container.exec_run(["/bin/bash", "-c", "/bin/df -H '" + request.json['dir'].replace("'", "'\\''") + "' | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody') + df_return = container.exec_run(["/bin/bash", "-c", "/bin/df '" + request.json['dir'].replace("'", "'\\''") + "' | /usr/bin/tail -n1 | /usr/bin/tr -s [:blank:] | /usr/bin/tr ' ' ','"], user='nobody') if df_return.exit_code == 0: return df_return.output.decode('utf-8').rstrip() else: @@ -264,7 +264,7 @@ class container_post(Resource): def container_post__exec__sieve__print(self, container_id): if 'username' in request.json and 'script_name' in request.json: for container in docker_client.containers.list(filters={"id": container_id}): - cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"] + cmd = ["/bin/bash", "-c", "/usr/bin/doveadm sieve get -u '" + request.json['username'].replace("'", "'\\''") + "' '" + request.json['script_name'].replace("'", "'\\''") + "'"] sieve_return = container.exec_run(cmd) return exec_run_handler('utf8_text_only', sieve_return) @@ -286,7 +286,7 @@ class container_post(Resource): for container in docker_client.containers.list(filters={"id": container_id}): cmd = "/usr/bin/rspamadm pw -e -p '" + request.json['raw'].replace("'", "'\\''") + "' 2> /dev/null" cmd_response = exec_cmd_container(container, cmd, user="_rspamd") - + matched = False for line in cmd_response.split("\n"): if '$2$' in line: @@ -306,7 +306,7 @@ class container_post(Resource): return jsonify(type='success', msg='command completed successfully') else: return jsonify(type='danger', msg='command did not complete') - + def exec_cmd_container(container, cmd, user, timeout=2, shell_cmd="/bin/bash"): @@ -333,7 +333,7 @@ def exec_cmd_container(container, cmd, user, timeout=2, shell_cmd="/bin/bash"): except: pass return ''.join(total_data) - + try : socket = container.exec_run([shell_cmd], stdin=True, socket=True, user=user).output._sock if not cmd.endswith("\n"): diff --git a/data/web/debug.php b/data/web/debug.php index eaa4b9a9..03752d34 100644 --- a/data/web/debug.php +++ b/data/web/debug.php @@ -43,11 +43,11 @@ else { 'system', 'task' => 'df', 'dir' => '/var/vmail'); $vmail_df = explode(',', json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true)); - $domainQuota = round(domain_admin('total_quota')/1024); - $inactive_bytes = round(mailbox('get','inactive_bytes')/1024/1024/1024); - $quotaPercent1 = round(($domainQuota/substr($vmail_df[3], 0, -1))*100); - $quotaPercent2 = round((($domainQuota-substr($vmail_df[2], 0, -1)+$inactive_bytes)/substr($vmail_df[3], 0, -1))*100); - $quotaPercent2 = ($quotaPercent2+substr($vmail_df[4], 0, -1)>100) ? 100-substr($vmail_df[4], 0, -1) : $quotaPercent2; //handling overcommitment + $used_percent = substr($vmail_df[4], 0, -1); + $quota_stats = mailbox('get','quota_stats'); + $quotaPercent1 = round(($quota_stats['total_quota']/$vmail_df[3])*100); + $quotaPercent2 = round((($quota_stats['total_quota']-$quota_stats['used_bytes'])/$vmail_df[3])*100); + $quotaPercent2 = ($quotaPercent2+$used_percent>100) ? 100-$used_percent : $quotaPercent2; ?>
@@ -58,15 +58,15 @@ else {

/var/vmail on

-

B

+

-
+
-

  B ()

-

  GB (%)

+

  (%)

+

  (%)

diff --git a/data/web/inc/functions.domain_admin.inc.php b/data/web/inc/functions.domain_admin.inc.php index 7fe4b3fa..6c821aa1 100644 --- a/data/web/inc/functions.domain_admin.inc.php +++ b/data/web/inc/functions.domain_admin.inc.php @@ -444,18 +444,5 @@ function domain_admin($_action, $_data = null) { return $domainadmindata; break; - case 'total_quota': - if ($_SESSION['mailcow_cc_role'] != "admin") { - $_SESSION['return'][] = array( - 'type' => 'danger', - 'log' => array(__FUNCTION__, $_action, $_data_log), - 'msg' => 'access_denied' - ); - return false; - } - $stmt = $pdo->query("SELECT SUM(`quota`) AS `quota` FROM `domain` WHERE `active`=1"); - $row = $stmt->fetch(PDO::FETCH_ASSOC); - return $row['quota']; - break; } } diff --git a/data/web/inc/functions.mailbox.inc.php b/data/web/inc/functions.mailbox.inc.php index 0b781a6c..7546c6c6 100644 --- a/data/web/inc/functions.mailbox.inc.php +++ b/data/web/inc/functions.mailbox.inc.php @@ -3254,7 +3254,8 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { } return $resourcedata; break; - case 'inactive_bytes': + case 'quota_stats': + $quotadata = array(); if ($_SESSION['mailcow_cc_role'] != "admin") { return false; } @@ -3263,9 +3264,13 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) { WHERE `mailbox`.`kind` NOT REGEXP 'location|thing|group' AND `mailbox`.`username` = `quota2`.`username` AND `domain`.`domain` = `mailbox`.`domain` - AND `domain`.`active` = 0"); + AND `domain`.`active` = 1"); $row = $stmt->fetch(PDO::FETCH_ASSOC); - return $row['bytes']; + $quotadata['used_bytes'] = $row['bytes']/1024; + $stmt = $pdo->query("SELECT SUM(`quota`) AS `quota` FROM `domain` WHERE `active`=1"); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + $quotadata['total_quota'] = $row['quota']*1024; + return $quotadata; break; } break; From 8b5be0b56de4c00b29064360bde7f1bba17ca93d Mon Sep 17 00:00:00 2001 From: ntimo Date: Wed, 2 Oct 2019 13:05:12 +0200 Subject: [PATCH 5/5] [Web] Return 401 status code when API authentication fails --- data/web/inc/sessions.inc.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/web/inc/sessions.inc.php b/data/web/inc/sessions.inc.php index a94d438c..20232511 100644 --- a/data/web/inc/sessions.inc.php +++ b/data/web/inc/sessions.inc.php @@ -60,6 +60,7 @@ if (!empty($_SERVER['HTTP_X_API_KEY'])) { else { $redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']); error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']); + http_response_code(401); echo json_encode(array( 'type' => 'error', 'msg' => 'api access denied for ip ' . $_SERVER['REMOTE_ADDR'] @@ -71,6 +72,7 @@ if (!empty($_SERVER['HTTP_X_API_KEY'])) { else { $redis->publish("F2B_CHANNEL", "mailcow UI: Invalid password for API_USER by " . $_SERVER['REMOTE_ADDR']); error_log("mailcow UI: Invalid password for " . $user . " by " . $_SERVER['REMOTE_ADDR']); + http_response_code(401); echo json_encode(array( 'type' => 'error', 'msg' => 'authentication failed'