[Web] Queue manager for Postfix

[Web] Add sogo_access mail attribute
[Web] Allow to wipe SOGo profiles
master
André 2018-10-23 21:14:57 +02:00
parent db64fa490b
commit e30dfd6751
16 changed files with 386 additions and 80 deletions

View File

@ -11,6 +11,7 @@ $tfa_data = get_tfa();
<li role="presentation" class="active"><a href="#tab-access" aria-controls="tab-access" role="tab" data-toggle="tab"><?=$lang['admin']['access'];?></a></li>
<li role="presentation"><a href="#tab-config" aria-controls="tab-config" role="tab" data-toggle="tab"><?=$lang['admin']['configuration'];?></a></li>
<li role="presentation"><a href="#tab-sys-mails" aria-controls="tab-sys-mails" role="tab" data-toggle="tab"><?=$lang['admin']['sys_mails'];?></a></li>
<li role="presentation"><a href="#tab-mailq" aria-controls="tab-mailq" role="tab" data-toggle="tab">Queue manager</a></li>
</ul>
<div class="tab-content" style="padding-top:20px">
@ -855,8 +856,52 @@ $tfa_data = get_tfa();
</form>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-mailq">
<div class="panel panel-default">
<div class="panel-heading">
Queue manager <span class="badge badge-info table-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default refresh_table" data-draw="draw_queue" data-table="queuetable"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="queuetable"></table>
</div>
<div class="mass-actions-admin">
<div class="btn-group">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="mailqitems" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['mailbox']['toggle_all'];?></a>
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['mailbox']['quick_actions'];?> <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a data-toggle="tooltip" title="postqueue -i" data-action="edit_selected" data-id="mailqitems" data-api-url='edit/mailq' data-api-attr='{"action":"deliver"}' href="#">Deliver</a></li>
<li><a data-toggle="tooltip" title="postsuper -H" data-action="edit_selected" data-id="mailqitems" data-api-url='edit/mailq' data-api-attr='{"action":"unhold"}' href="#">Unhold</a></li>
<li><a data-toggle="tooltip" title="postsuper -h" data-action="edit_selected" data-id="mailqitems" data-api-url='edit/mailq' data-api-attr='{"action":"hold"}' href="#">Hold</a></li>
<li role="separator" class="divider"></li>
<li><a data-toggle="tooltip" title="postsuper -d" data-action="delete_selected" data-id="mailqitems" data-api-url='delete/mailq' href="#"><?=$lang['mailbox']['remove'];?></a></li>
</ul>
<a class="btn btn-sm btn-primary"
data-action="edit_selected"
data-item="mailqitems-all"
data-api-url='edit/mailq'
data-api-attr='{"action":"flush"}'
data-toggle="tooltip" title="postqueue -f"
href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['admin']['flush_queue'];?></a>
<a class="btn btn-sm btn-danger"
id="super_delete"
data-action="edit_selected"
data-item="mailqitems-all"
data-api-url='edit/mailq'
data-api-attr='{"action":"super_delete"}'
data-toggle="tooltip" title="postsuper -d ALL"
href="#"><span class="glyphicon glyphicon-trash" aria-hidden="true"></span> <?=$lang['admin']['delete_queue'];?></a>
</div>
</div>
</div>
</div>
</div>
</div> <!-- /container -->
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/admin.php';

View File

@ -35,7 +35,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<div class="tab-content" style="padding-top:20px">
<div class="debug-log-info"><?=sprintf($lang['debug']['log_info'], getenv('LOG_LINES') + 1);?></div>
<?php
$exec_fields = array('cmd' => 'df', 'dir' => '/var/vmail');
$exec_fields = array('cmd' => 'system', 'task' => 'df', 'dir' => '/var/vmail');
$vmail_df = explode(',', json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true));
?>
<div role="tabpanel" class="tab-pane active" id="tab-containers">

View File

@ -478,6 +478,7 @@ if (isset($_SESSION['mailcow_cc_role'])) {
<input type="hidden" value="default" name="sender_acl">
<input type="hidden" value="0" name="active">
<input type="hidden" value="0" name="force_pw_update">
<input type="hidden" value="0" name="sogo_access">
<div class="form-group">
<label class="control-label col-sm-2" for="name"><?=$lang['edit']['full_name'];?>:</label>
<div class="col-sm-10">
@ -573,6 +574,14 @@ if (isset($_SESSION['mailcow_cc_role'])) {
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<div class="checkbox">
<label><input type="checkbox" value="1" name="sogo_access" <?=($result['attributes']['sogo_access']=="1") ? "checked" : null;?>> <?=$lang['edit']['sogo_access'];?></label>
<small class="help-block"><?=$lang['edit']['sogo_access_info'];?></small>
</div>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button class="btn btn-success" data-action="edit_selected" data-id="editmailbox" data-item="<?=htmlspecialchars($result['username']);?>" data-api-url='edit/mailbox' data-api-attr='{}' href="#"><?=$lang['edit']['save'];?></button>

View File

@ -0,0 +1,16 @@
<?php
session_start();
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
header('Content-Type: text/plain');
if (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != 'admin') {
exit();
}
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq'));
if (isset($docker_return['type']['danger'])) {
echo "Cannot load mail queue: " . $docker_return['msg'];
}
else {
echo $docker_return;
}
?>

View File

@ -1225,7 +1225,7 @@ function rspamd_ui($action, $data = null) {
);
return false;
}
$docker_return = docker('post', 'rspamd-mailcow', 'exec', array('cmd' => 'worker_password', 'raw' => $rspamd_ui_pass), array('Content-Type: application/json'));
$docker_return = docker('post', 'rspamd-mailcow', 'exec', array('cmd' => 'rspamd', 'task' => 'worker_password', 'raw' => $rspamd_ui_pass), array('Content-Type: application/json'));
if ($docker_return_array = json_decode($docker_return, true)) {
if ($docker_return_array['type'] == 'success') {
$_SESSION['return'][] = array(

View File

@ -739,7 +739,9 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
array(
'force_pw_update' => strval(intval($MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update'])),
'tls_enforce_in' => strval(intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_in'])),
'tls_enforce_out' => strval(intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out'])))
'tls_enforce_out' => strval(intval($MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out'])),
'sogo_access' => strval(intval($MAILBOX_DEFAULT_ATTRIBUTES['sogo_access']))
)
);
if (!is_valid_domain_name($domain)) {
$_SESSION['return'][] = array(
@ -1881,6 +1883,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active_int'];
(int)$force_pw_update = (isset($_data['force_pw_update'])) ? intval($_data['force_pw_update']) : intval($is_now['attributes']['force_pw_update']);
(int)$sogo_access = (isset($_data['sogo_access'])) ? intval($_data['sogo_access']) : intval($is_now['attributes']['sogo_access']);
$name = (!empty($_data['name'])) ? $_data['name'] : $is_now['name'];
$domain = $is_now['domain'];
$quota_m = (!empty($_data['quota'])) ? $_data['quota'] : ($is_now['quota'] / 1048576);
@ -2082,13 +2085,15 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
`active` = :active,
`name`= :name,
`quota` = :quota_b,
`attributes` = JSON_SET(`attributes`, '$.force_pw_update', :force_pw_update)
`attributes` = JSON_SET(`attributes`, '$.force_pw_update', :force_pw_update),
`attributes` = JSON_SET(`attributes`, '$.sogo_access', :sogo_access)
WHERE `username` = :username");
$stmt->execute(array(
':active' => $active,
':name' => $name,
':quota_b' => $quota_b,
':force_pw_update' => $force_pw_update,
':sogo_access' => $sogo_access,
':username' => $username
));
$_SESSION['return'][] = array(
@ -2384,20 +2389,23 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
$_data = $_SESSION['mailcow_cc_username'];
}
$exec_fields = array(
'cmd' => 'sieve_list',
'cmd' => 'sieve',
'task' => 'list',
'username' => $_data
);
$filters = json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true);
$filters = array_filter(explode(PHP_EOL, $filters));
$filters = docker('post', 'dovecot-mailcow', 'exec', $exec_fields);
$filters = array_filter(preg_split("/(\r\n|\n|\r)/",$filters));
foreach ($filters as $filter) {
if (preg_match('/.+ ACTIVE/i', $filter)) {
$exec_fields = array(
'cmd' => 'sieve_print',
'cmd' => 'sieve',
'task' => 'print',
'script_name' => substr($filter, 0, -7),
'username' => $_data
);
$filters = json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true);
return preg_replace('/^.+\n/', '', $filters);
$script = docker('post', 'dovecot-mailcow', 'exec', $exec_fields);
// Remove first line
return preg_replace('/^.+\n/', '', $script);
}
}
return false;
@ -3081,6 +3089,66 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
}
break;
case 'sogo_profile':
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
if (!isset($_SESSION['acl']['sogo_profile_reset']) || $_SESSION['acl']['sogo_profile_reset'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
foreach ($usernames as $username) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `sogo_user_profile` WHERE `c_uid` = :username");
$stmt->execute(array(
':username' => $username
));
$stmt = $pdo->prepare("DELETE FROM `sogo_cache_folder` WHERE `c_uid` = :username");
$stmt->execute(array(
':username' => $username
));
$stmt = $pdo->prepare("DELETE FROM `sogo_acl` WHERE `c_object` LIKE '%/" . $username . "/%' OR `c_uid` = :username");
$stmt->execute(array(
':username' => $username
));
$stmt = $pdo->prepare("DELETE FROM `sogo_store` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
$stmt->execute(array(
':username' => $username
));
$stmt = $pdo->prepare("DELETE FROM `sogo_quick_contact` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
$stmt->execute(array(
':username' => $username
));
$stmt = $pdo->prepare("DELETE FROM `sogo_quick_appointment` WHERE `c_folder_id` IN (SELECT `c_folder_id` FROM `sogo_folder_info` WHERE `c_path2` = :username)");
$stmt->execute(array(
':username' => $username
));
$stmt = $pdo->prepare("DELETE FROM `sogo_folder_info` WHERE `c_path2` = :username");
$stmt->execute(array(
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => array('sogo_profile_reset', htmlspecialchars($username))
);
}
break;
case 'domain':
if (!is_array($_data['domain'])) {
$domains = array();
@ -3119,7 +3187,7 @@ function mailbox($_action, $_type, $_data = null, $_extra = null) {
);
continue;
}
$exec_fields = array('cmd' => 'maildir_cleanup', 'maildir' => $domain);
$exec_fields = array('cmd' => 'maildir', 'task' => 'cleanup', 'maildir' => $domain);
$maildir_gc = json_decode(docker('post', 'dovecot-mailcow', 'exec', $exec_fields), true);
if ($maildir_gc['type'] != 'success') {
$_SESSION['return'][] = array(

View File

@ -0,0 +1,72 @@
<?php
function mailq($_action, $_data = null) {
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
function process_mailq_output($returned_output, $_action, $_data) {
if ($returned_output !== NULL) {
if (isset($returned_output['type']) && $returned_output['type'] == 'danger') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array('mailq', $_action, $_data),
'msg' => 'Error: ' . $returned_output['msg']
);
}
if (isset($returned_output['type']) && $returned_output['type'] == 'success') {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array('mailq', $_action, $_data),
'msg' => 'queue_command_success'
);
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array('mailq', $_action, $_data),
'msg' => 'unknown'
);
}
}
global $lang;
switch ($_action) {
case 'delete':
if (!is_array($_data['qid'])) {
$qids = array();
$qids[] = $_data['qid'];
}
else {
$qids = $_data['qid'];
}
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => 'delete', 'items' => $qids));
process_mailq_output(json_decode($docker_return, true), $_action, $_data);
break;
case 'edit':
if (in_array($_data['action'], array('hold', 'unhold', 'deliver'))) {
if (!is_array($_data['qid'])) {
$qids = array();
$qids[] = $_data['qid'];
}
else {
$qids = $_data['qid'];
}
if (!empty($qids)) {
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => $_data['action'], 'items' => $qids));
process_mailq_output(json_decode($docker_return, true), $_action, $_data);
}
}
if (in_array($_data['action'], array('flush', 'super_delete'))) {
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => $_data['action']));
process_mailq_output(json_decode($docker_return, true), $_action, $_data);
}
break;
case 'get':
// todo: move get from json_api here
break;
}
}

View File

@ -3,7 +3,7 @@ function init_db_schema() {
try {
global $pdo;
$db_version = "07102018_1502";
$db_version = "22102018_1502";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@ -291,6 +291,7 @@ function init_db_schema() {
"delimiter_action" => "TINYINT(1) NOT NULL DEFAULT '1'",
"syncjobs" => "TINYINT(1) NOT NULL DEFAULT '1'",
"eas_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
"sogo_profile_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantine" => "TINYINT(1) NOT NULL DEFAULT '1'",
),
"keys" => array(
@ -963,7 +964,8 @@ DELIMITER ;';
// Migrate tls_enforce_* options and add force_pw_update attribute
$stmt = $pdo->query("UPDATE `mailbox` SET `attributes` = '{}' WHERE `attributes` IS NULL;");
$stmt = $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.force_pw_update', 0) WHERE JSON_EXTRACT(`attributes`, '$.force_pw_update') IS NULL;");
$stmt = $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.force_pw_update', \"0\") WHERE JSON_EXTRACT(`attributes`, '$.force_pw_update') IS NULL;");
$stmt = $pdo->query("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.sogo_access', \"1\") WHERE JSON_EXTRACT(`attributes`, '$.sogo_access') IS NULL;");
foreach($tls_options as $tls_user => $tls_options) {
$stmt = $pdo->prepare("UPDATE `mailbox` SET `attributes` = JSON_SET(`attributes`, '$.tls_enforce_in', :tls_enforce_in),
`attributes` = JSON_SET(`attributes`, '$.tls_enforce_out', :tls_enforce_out)

View File

@ -142,6 +142,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.quarantine.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.policy.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.dkim.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fwdhost.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailq.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.ratelimit.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.relayhost.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.rsettings.inc.php';

View File

@ -139,3 +139,6 @@ $MAILBOX_DEFAULT_ATTRIBUTES['tls_enforce_out'] = false;
// Force password change on next login (only allows login to mailcow UI)
$MAILBOX_DEFAULT_ATTRIBUTES['force_pw_update'] = false;
// Force password change on next login (only allows login to mailcow UI)
$MAILBOX_DEFAULT_ATTRIBUTES['sogo_access'] = true;

View File

@ -5,18 +5,6 @@ jQuery(function($){
var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
$("#import_dkim_legend").on('click', function(e) {
e.preventDefault();
$('#import_dkim_arrow').toggleClass("animation");
});
$("#duplicate_dkim_legend").on('click', function(e) {
e.preventDefault();
$('#duplicate_dkim_arrow').toggleClass("animation");
});
$("#api_legend").on('click', function(e) {
e.preventDefault();
$('#api_arrow').toggleClass("animation");
});
$("#rspamd_preset_1").on('click', function(e) {
e.preventDefault();
$("form[data-id=rsetting]").find("#adminRspamdSettingsDesc").val(lang.rsettings_preset_1);
@ -35,15 +23,40 @@ jQuery(function($){
});
$('#dkim_add_domains').val(domains);
});
$("#mass_exclude").change(function(){
$("#mass_include").selectpicker('deselectAll');
});
$("#mass_include").change(function(){
$("#mass_exclude").selectpicker('deselectAll');
});
$("#mass_disarm").click(function() {
$("#mass_send").attr("disabled", !this.checked);
$("#import_dkim_legend").on('click', function(e) { e.preventDefault(); $('#import_dkim_arrow').toggleClass("animation"); });
$("#duplicate_dkim_legend").on('click', function(e) { e.preventDefault(); $('#duplicate_dkim_arrow').toggleClass("animation"); });
$("#api_legend").on('click', function(e) { e.preventDefault(); $('#api_arrow').toggleClass("animation"); });
$("#mass_exclude").change(function(){ $("#mass_include").selectpicker('deselectAll'); });
$("#mass_include").change(function(){ $("#mass_exclude").selectpicker('deselectAll'); });
$("#mass_disarm").click(function() { $("#mass_send").attr("disabled", !this.checked); });
$("#super_delete").click(function() { return confirm(lang.queue_ays); });
$(".refresh_table").on('click', function(e) {
e.preventDefault();
var table_name = $(this).data('table');
$('#' + table_name).find("tr.footable-empty").remove();
draw_table = $(this).data('draw');
eval(draw_table + '()');
});
if (localStorage.getItem("current_page") === null) {
var current_page = {};
} else {
var current_page = JSON.parse(localStorage.getItem('current_page'));
}
function table_admin_ready(ft, name) {
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
var ft_paging = ft.use(FooTable.Paging)
$(heading).children('.table-lines').text(function(){
return ft_paging.totalRows;
})
if (current_page[name]) {
ft_paging.goto(parseInt(current_page[name]))
}
}
function paging_admin_after(ft, name) {
var ft_paging = ft.use(FooTable.Paging)
current_page[name] = ft_paging.current;
localStorage.setItem('current_page', JSON.stringify(current_page));
}
function draw_domain_admins() {
ft_domainadmins = FooTable.init('#domainadminstable', {
"columns": [
@ -150,6 +163,43 @@ jQuery(function($){
"sorting": {"enabled": true}
});
}
function draw_queue() {
ft_queuetable = FooTable.init('#queuetable', {
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"queue_id","type":"text","title":"QID","style":{"width":"50px"}},
{"name":"queue_name","type":"text","title":"Queue","style":{"width":"120px"}},
{"name":"arrival_time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.arrival_time,"style":{"width":"170px"}},
{"name":"message_size","style":{"whiteSpace":"nowrap"},"title":lang.message_size,"formatter": function(value){
return humanFileSize(value);
}},
{"name":"sender","title":lang.sender, "type": "text","breakpoints":"xs sm"},
{"name":"recipients","title":lang.recipients, "type": "text","breakpoints":"xs sm"},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/mailq',
jsonp: false,
error: function () {
console.log('Cannot draw forwarding hosts table');
},
success: function (data) {
return process_table_data(data, 'queuetable');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
table_admin_ready(ft, 'queuetable');
},
"after.ft.paging": function(e, ft){
paging_admin_after(ft, 'queuetable');
}
}
});
}
function process_table_data(data, table) {
if (table == 'relayhoststable') {
@ -161,6 +211,11 @@ jQuery(function($){
'</div>';
item.chkbox = '<input type="checkbox" data-id="rlyhosts" name="multi_select" value="' + item.id + '" />';
});
} else if (table == 'queuetable') {
$.each(data, function (i, item) {
item.chkbox = '<input type="checkbox" data-id="mailqitems" name="multi_select" value="' + item.queue_id + '" />';
item.recipients = JSON.stringify(item.recipients);
});
} else if (table == 'forwardinghoststable') {
$.each(data, function (i, item) {
item.action = '<div class="btn-group">' +
@ -206,6 +261,7 @@ jQuery(function($){
draw_admins();
draw_fwd_hosts();
draw_relayhosts();
draw_queue();
// Relayhost
$('#testRelayhostModal').on('show.bs.modal', function (e) {
$('#test_relayhost_result').text("-");

View File

@ -58,7 +58,6 @@ $(document).ready(function() {
$(this.$domain).closest("select").selectpicker();
}
});
// Auto-fill domain quota when adding new domain
auto_fill_quota = function(domain) {
$.get("/api/v1/get/domain/" + domain, function(data){
@ -80,7 +79,6 @@ $(document).ready(function() {
auto_fill_quota($('#addSelectDomain').val());
});
auto_fill_quota($('#addSelectDomain').val());
$(".generate_password").click(function( event ) {
event.preventDefault();
$('[data-hibp]').trigger('input');
@ -90,7 +88,6 @@ $(document).ready(function() {
$(this).closest("form").find("input[name='password']").val(random_passwd);
$(this).closest("form").find("input[name='password2']").val(random_passwd);
});
$(".goto_checkbox").click(function( event ) {
$("form[data-id='add_alias'] .goto_checkbox").not(this).prop('checked', false);
if ($("form[data-id='add_alias'] .goto_checkbox:checked").length > 0) {
@ -108,7 +105,6 @@ $(document).ready(function() {
$("#textarea_alias_goto").removeAttr('disabled');
}
});
// Log modal
$('#syncjobLogModal').on('show.bs.modal', function(e) {
var syncjob_id = $(e.relatedTarget).data('syncjob-id');
@ -124,7 +120,6 @@ $(document).ready(function() {
}
});
});
// Log modal
$('#dnsInfoModal').on('show.bs.modal', function(e) {
var domain = $(e.relatedTarget).data('domain');
@ -141,13 +136,11 @@ $(document).ready(function() {
}
});
});
// Sieve data modal
$('#sieveDataModal').on('show.bs.modal', function(e) {
var sieveScript = $(e.relatedTarget).data('sieve-script');
$(e.currentTarget).find('#sieveDataText').html('<pre style="font-size:14px;line-height:1.1">' + sieveScript + '</pre>');
});
// Set line numbers for textarea
$("#script_data").numberedtextarea({allowTabChar: true});
// Disable submit button on script change
@ -155,7 +148,6 @@ $(document).ready(function() {
$('#add_filter_btns > #add_item').attr({"disabled": true});
$('#validation_msg').html('-');
});
// Validate script data
$("#validate_sieve").click(function( event ) {
event.preventDefault();
@ -185,21 +177,8 @@ $(document).ready(function() {
});
jQuery(function($){
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
var entityMap = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
'/': '&#x2F;',
'`': '&#x60;',
'=': '&#x3D;'
};
function escapeHtml(string) {
return String(string).replace(/[&<>"'`=\/]/g, function (s) {
return entityMap[s];
});
}
var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})}
if (localStorage.getItem("current_page") === null) {
var current_page = {};
} else {
@ -210,23 +189,7 @@ jQuery(function($){
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
// Calculation human readable file sizes
function humanFileSize(bytes) {
if(Math.abs(bytes) < 1024) {
return bytes + ' B';
}
var units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'];
var u = -1;
do {
bytes /= 1024;
++u;
} while(Math.abs(bytes) >= 1024 && u < units.length - 1);
return bytes.toFixed(1)+' '+units[u];
}
function unix_time_format(tm) {
var date = new Date(tm ? tm * 1000 : 0);
return date.toLocaleString();
}
function humanFileSize(i){if(Math.abs(i)<1024)return i+" B";var B=["KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"],e=-1;do{i/=1024,++e}while(Math.abs(i)>=1024&&e<B.length-1);return i.toFixed(1)+" "+B[e]}
$(".refresh_table").on('click', function(e) {
e.preventDefault();
var table_name = $(this).data('table');

View File

@ -218,6 +218,24 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
}
break;
case "mailq":
$mailq_lines = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => 'list'));
$lines = 0;
// Hard limit to 1000 items
foreach (preg_split("/((\r?\n)|(\r\n?))/", $mailq_lines) as $mailq_item) if ($lines++ < 1000) {
if (empty($mailq_item) || $mailq_item == '1') {
continue;
}
$line[] = json_decode($mailq_item, true);
}
if (!isset($line) || empty($line)) {
echo '{}';
}
else {
echo json_encode($line);
}
break;
case "rl-domain":
switch ($object) {
case "all":
@ -974,6 +992,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "filter":
process_delete_return(mailbox('delete', 'filter', array('id' => $items)));
break;
case "mailq":
process_delete_return(mailq('delete', array('qid' => $items)));
break;
case "qitem":
process_delete_return(quarantine('delete', array('id' => $items)));
break;
@ -1017,6 +1038,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "eas_cache":
process_delete_return(mailbox('delete', 'eas_cache', array('username' => $items)));
break;
case "sogo_profile":
process_delete_return(mailbox('delete', 'sogo_profile', array('username' => $items)));
break;
case "domain-admin":
process_delete_return(domain_admin('delete', array('username' => $items)));
break;
@ -1088,6 +1112,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "quarantine":
process_edit_return(quarantine('edit', $attr));
break;
case "mailq":
process_edit_return(mailq('edit', array_merge(array('qid' => $items), $attr)));
break;
case "time_limited_alias":
process_edit_return(mailbox('edit', 'time_limited_alias', array_merge(array('address' => $items), $attr)));
break;

View File

@ -31,6 +31,7 @@ $lang['danger']['yotp_verification_failed'] = "Yubico OTP Verifizierung fehlgesc
$lang['danger']['ip_list_empty'] = "Liste erlaubter IPs darf nicht leer sein";
$lang['danger']['rspamd_ui_pw_length'] = "Rspamd UI Passwort muss mindestens 6 Zeichen lang sein";
$lang['success']['rspamd_ui_pw_set'] = "Rspamd UI Passwort wurde gesetzt";
$lang['success']['queue_command_success'] = "Queue-Aufgabe erfolgreich ausgeführt";
$lang['danger']['unknown'] = "Ein unbekannter Fehler trat auf";
$lang['danger']['malformed_username'] = "Benutzername hat falsches Format";
$lang['info']['awaiting_tfa_confirmation'] = "Warte auf TFA Verifizierung";
@ -134,6 +135,7 @@ $lang['success']['domain_admin_removed'] = 'Domain-Administrator %s wurde entfer
$lang['success']['admin_removed'] = 'Administrator %s wurde entfernt';
$lang['success']['mailbox_removed'] = 'Mailbox %s wurde entfernt';
$lang['success']['eas_reset'] = "ActiveSync Gerät des Benutzers %s wurden zurückgesetzt";
$lang['success']['sogo_profile_reset'] = "ActiveSync Gerät des Benutzers %s wurden zurückgesetzt";
$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';
@ -221,10 +223,15 @@ $lang['user']['tag_in_none'] = 'Nichts tun';
$lang['user']['tag_help_explain'] = 'Als Unterordner: Es wird ein Ordner mit dem Namen des Tags unterhalb der Inbox erstellt ("INBOX/Facebook").<br>
In Betreff: Der Name des Tags wird dem Betreff angefügt, etwa "[Facebook] Meine Neuigkeiten".';
$lang['user']['tag_help_example'] = 'Beispiel für eine getaggte E-Mail-Adresse: ich<b>+Facebook</b>@example.org';
$lang['user']['eas_reset'] = 'ActiveSync Geräte-Cache zurücksetzen';
$lang['user']['eas_reset_now'] = 'Jetzt zurücksetzen';
$lang['user']['eas_reset_help'] = 'In vielen Fällen kann ein ActiveSync Profil durch das Zurücksetzen des Caches repariert werden.<br><b>Vorsicht:</b> Alle Elemente werden erneut heruntergeladen!';
$lang['user']['sogo_profile_reset'] = 'SOGo Profil zurücksetzen';
$lang['user']['sogo_profile_reset_now'] = 'Profil jetzt zurücksetzen';
$lang['user']['sogo_profile_reset_help'] = 'Das Profil wird zuzüglich aller Daten <b>unwiederbringlich gelöscht</b>.';
$lang['user']['encryption'] = 'Verschlüsselung';
$lang['user']['username'] = 'Benutzername';
$lang['user']['last_run'] = 'Letzte Ausführung';
@ -331,6 +338,8 @@ $lang['edit']['target_address'] = 'Ziel-Adresse(n) <small>(getrennt durch Komma)
$lang['edit']['active'] = 'Aktiv';
$lang['edit']['force_pw_update'] = 'Erzwinge Passwortänderung bei nächstem Login';
$lang['edit']['force_pw_update_info'] = 'Dem Benutzer wird lediglich der Zugang zur mailcow UI ermöglicht.';
$lang['edit']['sogo_access'] = 'SOGo Zugriffsrecht';
$lang['edit']['sogo_access_info'] = 'Zugriff auf SOGo erlauben oder verbieten. Diese Einstellung hat weder Einfluss auf den Zugang sonstiger Dienste noch entfernt sie ein vorhandenes SOGo Benutzerprofil.';
$lang['edit']['target_domain'] = 'Ziel-Domain:';
$lang['edit']['password'] = 'Passwort:';
$lang['edit']['password_repeat'] = 'Passwort (Wiederholung):';
@ -367,6 +376,7 @@ $lang['acl']['spam_policy'] = 'Blacklist/Whitelist';
$lang['acl']['delimiter_action'] = 'Delimiter Aktionen (tags)';
$lang['acl']['syncjobs'] = 'Sync Jobs';
$lang['acl']['eas_reset'] = 'EAS-Cache zurücksetzen';
$lang['acl']['sogo_profile_reset'] = 'SOGo Profil zurücksetzen';
$lang['acl']['quarantine'] = 'Quarantäne';
$lang['acl']['login_as'] = 'Einloggen als Mailbox-Benutzer';
$lang['acl']['bcc_maps'] = 'BCC Maps';
@ -520,6 +530,13 @@ $lang['admin']['rsettings_preset_2'] = 'Spam an Postmaster-Addressen nicht block
$lang['admin']['rsettings_insert_preset'] = 'Beispiel "%s" laden';
$lang['admin']['rsetting_add_rule'] = 'Regel hinzufügen';
$lang['admin']['admin_domains'] = 'Domain-Zuweisungen';
$lang['admin']['queue_ays'] = 'Soll die derzeitige Queue wirklich komplett bereinigt werden?';
$lang['admin']['arrival_time'] = 'Ankunftszeit (Serverzeit)';
$lang['admin']['message_size'] = 'Nachrichtengröße';
$lang['admin']['sender'] = 'Sender';
$lang['admin']['recipients'] = 'Empfänger';
$lang['admin']['flush_queue'] = 'Flush Queue';
$lang['admin']['delete_queue'] = 'Alle löschen';
$lang['admin']['domain_admins'] = 'Domain-Administratoren';
$lang['admin']['username'] = 'Benutzername';
$lang['admin']['edit'] = 'Bearbeiten';

View File

@ -31,6 +31,7 @@ $lang['danger']['yotp_verification_failed'] = "Yubico OTP verification failed: %
$lang['danger']['ip_list_empty'] = "List of allowed IPs cannot be empty";
$lang['danger']['rspamd_ui_pw_length'] = "Rspamd UI password should be at least 6 chars long";
$lang['success']['rspamd_ui_pw_set'] = "Rspamd UI password successfully set";
$lang['success']['queue_command_success'] = "Queue command completed successfully";
$lang['danger']['unknown'] = "An unknown error occured";
$lang['danger']['malformed_username'] = "Malformed username";
$lang['info']['awaiting_tfa_confirmation'] = "Awaiting TFA confirmation";
@ -137,6 +138,7 @@ $lang['success']['domain_admin_removed'] = "Domain administrator %s has been rem
$lang['success']['admin_removed'] = "Administrator %s has been removed";
$lang['success']['mailbox_removed'] = "Mailbox %s has been removed";
$lang['success']['eas_reset'] = "ActiveSync devices for user %s were reset";
$lang['success']['sogo_profile_reset'] = "SOGo profile for user %s was reset";
$lang['success']['resource_removed'] = "Resource %s has been removed";
$lang['warning']['cannot_delete_self'] = "Cannot delete logged in user";
$lang['warning']['no_active_admin'] = "Cannot deactivate last active admin";
@ -223,10 +225,15 @@ $lang['user']['tag_in_none'] = 'Do nothing';
$lang['user']['tag_help_explain'] = 'In subfolder: a new subfolder named after the tag will be created below INBOX ("INBOX/Facebook").<br>
In subject: the tags name will be prepended to the mails subject, example: "[Facebook] My News".';
$lang['user']['tag_help_example'] = 'Example for a tagged email address: me<b>+Facebook</b>@example.org';
$lang['user']['eas_reset'] = 'Reset ActiveSync device cache';
$lang['user']['eas_reset_now'] = 'Reset now';
$lang['user']['eas_reset_help'] = 'In many cases a device cache reset will help to recover a broken ActiveSync profile.<br><b>Attention:</b> All elements will be redownloaded!';
$lang['user']['sogo_profile_reset'] = 'Reset SOGo profile';
$lang['user']['sogo_profile_reset_now'] = 'Reset profile now';
$lang['user']['sogo_profile_reset_help'] = 'This will destroy a users SOGo profile and <b>delete all data irretrievable</b>.';
$lang['user']['encryption'] = 'Encryption';
$lang['user']['username'] = 'Username';
$lang['user']['last_run'] = 'Last run';
@ -342,6 +349,8 @@ $lang['edit']['target_address'] = 'Goto address/es <small>(comma-separated)</sma
$lang['edit']['active'] = 'Active';
$lang['edit']['force_pw_update'] = 'Force password update at next login';
$lang['edit']['force_pw_update_info'] = 'This user will only be able to login to mailcow UI.';
$lang['edit']['sogo_access'] = 'Grant access to SOGo';
$lang['edit']['sogo_access_info'] = 'Grant or permit access to SOGo. This setting does neither affect access to all other services nor does it delete or change a users existing SOGo profile.';
$lang['edit']['target_domain'] = 'Target domain';
$lang['edit']['password'] = 'Password';
$lang['edit']['password_repeat'] = 'Confirmation password (repeat)';
@ -378,6 +387,7 @@ $lang['acl']['spam_policy'] = 'Blacklist/Whitelist';
$lang['acl']['delimiter_action'] = 'Delimiter action';
$lang['acl']['syncjobs'] = 'Sync jobs';
$lang['acl']['eas_reset'] = 'Reset EAS devices';
$lang['acl']['sogo_profile_reset'] = 'Reset SOGo profile';
$lang['acl']['quarantine'] = 'Quarantine';
$lang['acl']['login_as'] = 'Login as mailbox user';
$lang['acl']['bcc_maps'] = 'BCC maps';
@ -533,8 +543,15 @@ $lang['admin']['rsettings_preset_1'] = 'Disable all but DKIM and rate limit for
$lang['admin']['rsettings_preset_2'] = 'Postmasters want spam';
$lang['admin']['rsettings_insert_preset'] = 'Insert example preset "%s"';
$lang['admin']['rsetting_add_rule'] = 'Add rule';
$lang['admin']['queue_ays'] = 'Please confirm you want to delete all items from the current queue.';
$lang['admin']['arrival_time'] = 'Arrival time (server time)';
$lang['admin']['message_size'] = 'Message size';
$lang['admin']['sender'] = 'Sender';
$lang['admin']['recipients'] = 'Recipients';
$lang['admin']['admin_domains'] = 'Domain assignments';
$lang['admin']['domain_admins'] = 'Domain administrators';
$lang['admin']['flush_queue'] = 'Flush queue';
$lang['admin']['delete_queue'] = 'Delete all';
$lang['admin']['username'] = 'Username';
$lang['admin']['edit'] = 'Edit';
$lang['admin']['remove'] = 'Remove';

View File

@ -224,6 +224,8 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
</div>
</div>
<hr>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['eas_reset'];?>:</div>
<div class="col-md-9 col-xs-7">
@ -232,6 +234,14 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
</div>
</div>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['sogo_profile_reset'];?>:</div>
<div class="col-md-9 col-xs-7">
<button class="btn btn-xs btn-default" data-acl="<?=$_SESSION['acl']['sogo_profile_reset'];?>" data-action="delete_selected" data-text="<?=$lang['user']['sogo_profile_reset'];?>?" data-item="<?= htmlentities($username); ?>" data-id="sogo_profile" data-api-url='delete/sogo_profile' href="#"><?=$lang['user']['sogo_profile_reset_now'];?></button>
<p class="help-block"><?=$lang['user']['sogo_profile_reset_help'];?></p>
</div>
</div>
</div>
</div>
@ -392,7 +402,7 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
<li><a data-action="edit_selected" data-id="syncjob" data-api-url='edit/syncjob' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
<li><a data-action="edit_selected" data-id="syncjob" data-api-url='edit/syncjob' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li>
<li role="separator" class="divider"></li>
<li><a data-action="delete_selected" data-text="<?=$lang['user']['eas_reset'];?>?" data-id="syncjob" data-api-url='delete/syncjob' href="#"><?=$lang['mailbox']['remove'];?></a></li>
<li><a data-action="delete_selected" data-id="syncjob" data-api-url='delete/syncjob' href="#"><?=$lang['mailbox']['remove'];?></a></li>
</ul>
<a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addSyncJobModal"><span class="glyphicon glyphicon-plus"></span> <?=$lang['user']['create_syncjob'];?></a>
</div>