Various...

master
andre.peters 2017-12-09 13:17:15 +01:00
parent 5f5b6652a1
commit adc23d86f9
70 changed files with 6008 additions and 1381 deletions

1
.gitignore vendored
View File

@ -21,4 +21,3 @@ data/conf/nginx/*.conf
data/conf/nginx/*.custom data/conf/nginx/*.custom
data/conf/nginx/*.bak data/conf/nginx/*.bak
data/conf/dovecot/extra.conf data/conf/dovecot/extra.conf
data/conf/rspamd/custom/*

View File

@ -8,26 +8,8 @@ $tfa_data = get_tfa();
?> ?>
<div class="container"> <div class="container">
<ul class="nav nav-tabs" role="tablist"> <ul class="nav nav-tabs" role="tablist">
<li role="presentation" class="active"> <li role="presentation" class="active"><a href="#tab-access" aria-controls="tab-access" role="tab" data-toggle="tab"><?=$lang['admin']['access'];?></a></li>
<a href="#tab-access" aria-controls="tab-access" role="tab" data-toggle="tab"><?=$lang['admin']['access'];?></a> <li role="presentation"><a href="#tab-config" aria-controls="tab-config" role="tab" data-toggle="tab"><?=$lang['admin']['configuration'];?></a></li>
</li>
<li role="presentation">
<a href="#tab-config" aria-controls="tab-config" role="tab" data-toggle="tab"><?=$lang['admin']['configuration'];?></a>
</li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">Logs
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li role="presentation"><a href="#tab-postfix-logs" aria-controls="tab-postfix-logs" role="tab" data-toggle="tab">Postfix</a></li>
<li role="presentation"><a href="#tab-dovecot-logs" aria-controls="tab-dovecot-logs" role="tab" data-toggle="tab">Dovecot</a></li>
<li role="presentation"><a href="#tab-sogo-logs" aria-controls="tab-sogo-logs" role="tab" data-toggle="tab">SOGo</a></li>
<?php if (F2B == 1): ?>
<li role="presentation"><a href="#tab-fail2ban-logs" aria-controls="tab-fail2ban-logs" role="tab" data-toggle="tab">Fail2ban</a></li>
<?php endif; ?>
<li role="presentation"><a href="#tab-rspamd-history" aria-controls="tab-rspamd-history" role="tab" data-toggle="tab">Rspamd</a></li>
<li role="presentation"><a href="#tab-autodiscover-logs" aria-controls="tab-autodiscover-logs" role="tab" data-toggle="tab">Autodiscover</a></li>
</ul>
</li>
</ul> </ul>
<div class="tab-content" style="padding-top:20px"> <div class="tab-content" style="padding-top:20px">
@ -58,7 +40,7 @@ $tfa_data = get_tfa();
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-3 col-sm-9"> <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/self' 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="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
</div> </div>
</div> </div>
</form> </form>
@ -96,6 +78,42 @@ $tfa_data = get_tfa();
</div> </div>
</div> </div>
</div> </div>
<div class="panel panel-primary">
<div class="panel-heading">API</div>
<div class="panel-body">
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
<div class="form-group">
<label class="control-label col-sm-3" for="allow_from"><?=$lang['admin']['api_allow_from'];?>:</label>
<div class="col-sm-9">
<textarea class="form-control" rows="5" name="allow_from" id="allow_from" required><?=htmlspecialchars($admindetails['allow_from']);?></textarea>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3" for="admin_api_key"><?=$lang['admin']['api_key'];?>:</label>
<div class="col-sm-9">
<input type="text" class="form-control" placeholder="-" value="<?=htmlspecialchars($admindetails['api_key']);?>" readonly>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<label>
<input type="checkbox" name="active" <?=($admindetails['api_active'] == 1) ? 'checked' : null;?>> <?=$lang['admin']['activate_api'];?>
</label>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<div class="btn-group">
<button class="btn btn-default" name="admin_api" type="submit" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
<button class="btn btn-info" name="admin_api_regen_key" type="submit" href="#"><?=$lang['admin']['regen_api_key'];?></button>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><?=$lang['admin']['domain_admins'];?></div> <div class="panel-heading"><?=$lang['admin']['domain_admins'];?></div>
<div class="panel-body"> <div class="panel-body">
@ -121,17 +139,15 @@ $tfa_data = get_tfa();
</div> </div>
</div> </div>
<div role="tabpanel" class="tab-pane" id="tab-config"> <div role="tabpanel" class="tab-pane" id="tab-config">
<div class="row"> <div class="row">
<div id="sidebar-admin" class="col-sm-2 hidden-xs"> <div id="sidebar-admin" class="col-sm-2 hidden-xs">
<div id="scrollbox" class="list-group"> <div id="scrollbox" class="list-group">
<a href="#dkim" class="list-group-item"><?=$lang['admin']['dkim_keys'];?></a> <a href="#dkim" class="list-group-item"><?=$lang['admin']['dkim_keys'];?></a>
<a href="#fwdhosts" class="list-group-item"><?=$lang['admin']['forwarding_hosts'];?></a> <a href="#fwdhosts" class="list-group-item"><?=$lang['admin']['forwarding_hosts'];?></a>
<?php if (F2B == 1): ?>
<a href="#f2bparams" class="list-group-item"><?=$lang['admin']['f2b_parameters'];?></a> <a href="#f2bparams" class="list-group-item"><?=$lang['admin']['f2b_parameters'];?></a>
<?php endif; ?>
<a href="#relayhosts" class="list-group-item">Relayhosts</a> <a href="#relayhosts" class="list-group-item">Relayhosts</a>
<a href="#quarantaine" class="list-group-item">Quarantaine</a>
<a href="#customize" class="list-group-item"><?=$lang['admin']['customize'];?></a> <a href="#customize" class="list-group-item"><?=$lang['admin']['customize'];?></a>
<a href="#top" class="list-group-item" style="border-top:1px dashed #dadada"> <?=$lang['admin']['to_top'];?></a> <a href="#top" class="list-group-item" style="border-top:1px dashed #dadada"> <?=$lang['admin']['to_top'];?></a>
</div> </div>
@ -310,7 +326,6 @@ $tfa_data = get_tfa();
</div> </div>
</div> </div>
<?php if (F2B == 1): ?>
<span class="anchor" id="f2bparams"></span> <span class="anchor" id="f2bparams"></span>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><?=$lang['admin']['f2b_parameters'];?></div> <div class="panel-heading"><?=$lang['admin']['f2b_parameters'];?></div>
@ -332,14 +347,13 @@ $tfa_data = get_tfa();
<input type="number" class="form-control" id="retry_window" name="retry_window" value="<?=$f2b_data['retry_window'];?>" required> <input type="number" class="form-control" id="retry_window" name="retry_window" value="<?=$f2b_data['retry_window'];?>" required>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="retry_window"><?=$lang['admin']['f2b_whitelist'];?>:</label> <label for="whitelist"><?=$lang['admin']['f2b_whitelist'];?>:</label>
<textarea class="form-control" id="whitelist" name="whitelist" rows="5"><?=$f2b_data['whitelist'];?></textarea> <textarea class="form-control" id="whitelist" name="whitelist" rows="5"><?=$f2b_data['whitelist'];?></textarea>
</div> </div>
<button class="btn btn-default" id="add_item" data-id="f2b" data-api-url='edit/fail2ban' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button> <button class="btn btn-default" id="add_item" data-id="f2b" data-api-url='edit/fail2ban' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
</form> </form>
</div> </div>
</div> </div>
<?php endif; ?>
<span class="anchor" id="relayhosts"></span> <span class="anchor" id="relayhosts"></span>
<div class="panel panel-default"> <div class="panel panel-default">
@ -381,6 +395,43 @@ $tfa_data = get_tfa();
</div> </div>
</div> </div>
<span class="anchor" id="quarantaine"></span>
<div class="panel panel-default">
<div class="panel-heading">Quarantäne</div>
<div class="panel-body">
<?php $q_data = quarantaine('settings'); ?>
<form class="form" data-id="quarantaine" role="form" method="post">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label for="retention_size">Rückhaltungen pro Mailbox:</label>
<input type="number" class="form-control" id="retention_size" name="retention_size" value="<?=$q_data['retention_size'];?>" required>
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label for="max_size">Maximale Größe in MiB (größere Elemente werden verworfen):</label>
<input type="number" class="form-control" id="max_size" name="max_size" value="<?=$q_data['max_size'];?>" required>
</div>
</div>
</div>
<div class="form-group">
<label for="exclude_domains">Domains und Alias-Domains ausschließen:</label><br />
<select data-width="100%" id="exclude_domains" name="exclude_domains" class="selectpicker" title="<?=$lang['tfa']['select'];?>" multiple>
<?php
foreach (array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')) as $domain):
?>
<option <?=(in_array($domain, $q_data['exclude_domains'])) ? 'selected' : null;?>><?=htmlspecialchars($domain);?></option>
<?php
endforeach;
?>
</select>
</div>
<button class="btn btn-success" id="edit_selected" data-item="self" data-id="quarantaine" data-api-url='edit/quarantaine' data-api-attr='{"action":"settings"}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
</form>
</div>
</div>
<span class="anchor" id="customize"></span> <span class="anchor" id="customize"></span>
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"><?=$lang['admin']['customize'];?></div> <div class="panel-heading"><?=$lang['admin']['customize'];?></div>
@ -449,10 +500,29 @@ $tfa_data = get_tfa();
endforeach; endforeach;
?> ?>
</table> </table>
<div class="btn-group"> <p><div class="btn-group">
<button class="btn btn-success" id="edit_selected" data-item="admin" data-id="app_links" data-reload="no" data-api-url='edit/app_links' data-api-attr='{}' href="#"><?=$lang['admin']['save'];?></button> <button class="btn btn-sm btn-success" id="edit_selected" data-item="admin" data-id="app_links" data-reload="no" data-api-url='edit/app_links' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
<button class="btn btn-default" type="button" id="add_app_link_row"><?=$lang['admin']['add_row'];?></button> <button class="btn btn-sm btn-default" type="button" id="add_app_link_row"><?=$lang['admin']['add_row'];?></button>
</div></p>
</form>
<legend><?=$lang['admin']['ui_texts'];?></legend>
<?php
$ui_texts = customize('get', 'ui_texts');
?>
<form class="form" data-id="uitexts" role="form" method="post">
<div class="form-group">
<label for="main_name"><?=$lang['admin']['main_name'];?>:</label>
<input type="text" class="form-control" id="main_name" name="main_name" placeholder="mailcow UI" value="<?=$ui_texts['main_name'];?>">
</div> </div>
<div class="form-group">
<label for="apps_name"><?=$lang['admin']['apps_name'];?>:</label>
<input type="text" class="form-control" id="apps_name" name="apps_name" placeholder="mailcow Apps" value="<?=$ui_texts['apps_name'];?>">
</div>
<div class="form-group">
<label for="help_text"><?=$lang['admin']['help_text'];?>:</label>
<textarea class="form-control" id="help_text" name="help_text" rows="7"><?=$ui_texts['help_text'];?></textarea>
</div>
<button class="btn btn-success" id="edit_selected" data-item="null" data-id="uitexts" data-api-url='edit/ui_texts' data-api-attr='{}' href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
</form> </form>
</div> </div>
</div> </div>
@ -460,111 +530,6 @@ $tfa_data = get_tfa();
</div> </div>
</div> </div>
<div role="tabpanel" class="tab-pane" id="tab-postfix-logs">
<div class="panel panel-default">
<div class="panel-heading">Postfix <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_postfix_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="postfix_log"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
<div class="panel panel-default">
<div class="panel-heading">Dovecot <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_dovecot_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="dovecot_log"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-sogo-logs">
<div class="panel panel-default">
<div class="panel-heading">SOGo <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_sogo_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="sogo_log"></table>
</div>
</div>
</div>
</div>
<?php if (F2B == 1): ?>
<div role="tabpanel" class="tab-pane" id="tab-fail2ban-logs">
<div class="panel panel-default">
<div class="panel-heading">Fail2ban <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="fail2ban_log" data-log-url="fail2ban" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="fail2ban_log" data-log-url="fail2ban" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_fail2ban_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="fail2ban_log"></table>
</div>
</div>
</div>
</div>
<?php endif; ?>
<div role="tabpanel" class="tab-pane" id="tab-rspamd-history">
<div class="panel panel-default">
<div class="panel-heading">Rspamd history <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd-history" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd-history" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_rspamd_history"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed log-table" id="rspamd_history"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-autodiscover-logs">
<div class="panel panel-default">
<div class="panel-heading">Autodiscover <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_autodiscover_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="autodiscover_log"></table>
</div>
</div>
</div>
</div>
</div> </div>
</div> <!-- /container --> </div> <!-- /container -->
<?php <?php

View File

@ -0,0 +1 @@
@media (max-width:1050px){.navbar-header{float:none}.navbar-left,.navbar-nav,.navbar-right{float:none!important}.navbar-toggle{display:block}.navbar-collapse{border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-collapse.collapse{display:none!important}.navbar-nav{margin-top:7.5px}.navbar-nav>li{float:none}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px}.collapse.in{display:block!important}.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}}

View File

@ -0,0 +1,37 @@
table.footable>tbody>tr.footable-empty>td {
font-size:15px !important;
font-style:italic;
}
.pagination a {
text-decoration: none !important;
}
.panel panel-default {
overflow: visible !important;
}
.table-responsive {
overflow: visible !important;
}
@media screen and (max-width: 767px) {
.table-responsive {
overflow-x: scroll !important;
}
}
.footer-add-item {
display:block;
text-align: center;
font-style: italic;
padding: 10px;
background: #F5F5F5;
}
@media (min-width: 992px) {
.container {
width: 80%;
}
}
.mass-actions-debug {
user-select: none;
padding:10px 0 10px 10px;
}
.inputMissingAttr {
border-color: #FF4136;
}

View File

@ -0,0 +1,37 @@
table.footable>tbody>tr.footable-empty>td {
font-size:15px !important;
font-style:italic;
}
.pagination a {
text-decoration: none !important;
}
.panel panel-default {
overflow: visible !important;
}
.table-responsive {
overflow: visible !important;
}
@media screen and (max-width: 767px) {
.table-responsive {
overflow-x: scroll !important;
}
}
.footer-add-item {
display:block;
text-align: center;
font-style: italic;
padding: 10px;
background: #F5F5F5;
}
@media (min-width: 992px) {
.container {
width: 80%;
}
}
.mass-actions-quarantaine {
user-select: none;
padding:10px 0 10px 10px;
}
.inputMissingAttr {
border-color: #FF4136;
}

328
data/web/debug.php 100644
View File

@ -0,0 +1,328 @@
<?php
require_once "inc/prerequisites.inc.php";
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
require_once "inc/header.inc.php";
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
?>
<div class="container">
<ul class="nav nav-tabs" role="tablist">
<li class="dropdown active"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Rspamd
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li role="presentation" class="active"><a href="#tab-rspamd-ui" aria-controls="tab-rspamd-ui" role="tab" data-toggle="tab">Rspamd UI</a></li>
<li role="presentation"><a href="#tab-rspamd-settings" aria-controls="tab-rspamd-settings" role="tab" data-toggle="tab">Rspamd settings map</a></li>
</ul>
</li>
<li role="presentation"><a href="#tab-containers" aria-controls="tab-containers" role="tab" data-toggle="tab">Containers</a></li>
<li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Logs
<span class="caret"></span></a>
<ul class="dropdown-menu">
<li role="presentation"><a href="#tab-postfix-logs" aria-controls="tab-postfix-logs" role="tab" data-toggle="tab">Postfix</a></li>
<li role="presentation"><a href="#tab-dovecot-logs" aria-controls="tab-dovecot-logs" role="tab" data-toggle="tab">Dovecot</a></li>
<li role="presentation"><a href="#tab-sogo-logs" aria-controls="tab-sogo-logs" role="tab" data-toggle="tab">SOGo</a></li>
<li role="presentation"><a href="#tab-fail2ban-logs" aria-controls="tab-fail2ban-logs" role="tab" data-toggle="tab">Fail2ban</a></li>
<li role="presentation"><a href="#tab-rspamd-history" aria-controls="tab-rspamd-history" role="tab" data-toggle="tab">Rspamd</a></li>
<li role="presentation"><a href="#tab-autodiscover-logs" aria-controls="tab-autodiscover-logs" role="tab" data-toggle="tab">Autodiscover</a></li>
<li role="presentation"><a href="#tab-watchdog-logs" aria-controls="tab-watchdog-logs" role="tab" data-toggle="tab">Watchdog</a></li>
<li role="presentation"><a href="#tab-acme-logs" aria-controls="tab-acme-logs" role="tab" data-toggle="tab">ACME</a></li>
<li role="presentation"><a href="#tab-api-logs" aria-controls="tab-api-logs" role="tab" data-toggle="tab">API</a></li>
</ul>
</li>
</ul>
<div class="row">
<div class="col-md-12">
<div class="tab-content" style="padding-top:20px">
<div role="tabpanel" class="tab-pane active" id="tab-rspamd-ui">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Rspamd UI</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="col-sm-9">
<form class="form-horizontal" autocapitalize="none" data-id="admin" autocorrect="off" role="form" method="post">
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<label>
<a href="/rspamd/" target="_blank"><span class="glyphicon glyphicon-new-window" aria-hidden="true"></span> Rspamd UI</a>
</label>
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3" for="rspamd_ui_pass"><?=$lang['admin']['password'];?>:</label>
<div class="col-sm-9">
<input type="password" class="form-control" name="rspamd_ui_pass" id="rspamd_ui_pass">
</div>
</div>
<div class="form-group">
<label class="control-label col-sm-3" for="rspamd_ui_pass2"><?=$lang['admin']['password_repeat'];?>:</label>
<div class="col-sm-9">
<input type="password" class="form-control" name="rspamd_ui_pass2" id="rspamd_ui_pass2">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button type="submit" class="btn btn-default" id="rspamd_ui" name="rspamd_ui" href="#"><span class="glyphicon glyphicon-check"></span> <?=$lang['admin']['save'];?></button>
</div>
</div>
</form>
</div>
<div class="col-sm-3">
<img class="img-responsive" src="/img/rspamd_logo.png" alt="Rspamd UI" />
</div>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-rspamd-settings">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Rspamd settings map</h3>
</div>
<div class="panel-body">
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control" rows="20" id="settings_map" name="settings_map" readonly><?=file_get_contents('http://nginx:8081/settings.php');?></textarea>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-containers">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Container information</h3>
</div>
<div class="panel-body">
<ul class="list-group">
<?php
$container_array = array(
'nginx-mailcow',
'rspamd-mailcow',
'postfix-mailcow',
'dovecot-mailcow',
'sogo-mailcow',
'acme-mailcow',
'memcached-mailcow',
'watchdog-mailcow',
'unbound-mailcow',
'redis-mailcow',
'php-fpm-mailcow',
'mysql-mailcow',
'fail2ban-mailcow',
'clamd-mailcow'
);
}
foreach ($container_array as $container) {
$container_stats = docker($container, 'info');
?>
<li class="list-group-item">
<?=$container;?>
<?php
date_default_timezone_set('UTC');
$StartedAt = date_parse($container_stats['State']['StartedAt']);
$date = new \DateTime();
$date->setTimestamp(mktime(
$StartedAt['hour'],
$StartedAt['minute'],
$StartedAt['second'],
$StartedAt['month'],
$StartedAt['day'],
$StartedAt['year']));
$user_tz = new DateTimeZone(getenv('TZ'));
$date->setTimezone($user_tz);
$started = $date->format('r');
?>
<small>(Started on <?=$started;?>),
<a href data-toggle="modal" data-container="<?=$container;?>" data-target="#RestartContainer">Restart</a></small>
<span class="pull-right label label-<?=($container_stats['State']['Running'] == 1) ? 'success' : 'danger';?>">&nbsp;&nbsp;&nbsp;</span>
</li>
<?php
}
?>
</ul>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-postfix-logs">
<div class="panel panel-default">
<div class="panel-heading">Postfix <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="postfix_log" data-log-url="postfix" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_postfix_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="postfix_log"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
<div class="panel panel-default">
<div class="panel-heading">Dovecot <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="dovecot_log" data-log-url="dovecot" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_dovecot_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="dovecot_log"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-sogo-logs">
<div class="panel panel-default">
<div class="panel-heading">SOGo <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="sogo_log" data-log-url="sogo" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_sogo_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="sogo_log"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-fail2ban-logs">
<div class="panel panel-default">
<div class="panel-heading">Fail2ban <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="fail2ban_log" data-log-url="fail2ban" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="fail2ban_log" data-log-url="fail2ban" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_fail2ban_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="fail2ban_log"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-rspamd-history">
<div class="panel panel-default">
<div class="panel-heading">Rspamd history <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd-history" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="rspamd_history" data-table="rspamd_history" data-log-url="rspamd-history" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_rspamd_history"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed log-table" id="rspamd_history"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-autodiscover-logs">
<div class="panel panel-default">
<div class="panel-heading">Autodiscover <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="autodiscover_log" data-table="autodiscover_log" data-log-url="autodiscover" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_autodiscover_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="autodiscover_log"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-watchdog-logs">
<div class="panel panel-default">
<div class="panel-heading">Watchdog <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="watchdog" data-table="watchdog_log" data-log-url="watchdog" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="watchdog" data-table="watchdog_log" data-log-url="watchdog" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_watchdog_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="watchdog_log"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-acme-logs">
<div class="panel panel-default">
<div class="panel-heading">ACME <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="acme_log" data-log-url="acme" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="general_syslog" data-table="acme_log" data-log-url="acme" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_acme_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="acme_log"></table>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane" id="tab-api-logs">
<div class="panel panel-default">
<div class="panel-heading">API <span class="badge badge-info log-lines"></span>
<div class="btn-group pull-right">
<button class="btn btn-xs btn-default add_log_lines" data-post-process="apilog" data-table="api_log" data-log-url="api" data-nrows="100">+ 100</button>
<button class="btn btn-xs btn-default add_log_lines" data-post-process="apilog" data-table="api_log" data-log-url="api" data-nrows="1000">+ 1000</button>
<button class="btn btn-xs btn-default" id="refresh_api_log"><?=$lang['admin']['refresh'];?></button>
</div>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped table-condensed" id="api_log"></table>
</div>
</div>
</div>
</div>
</div> <!-- /tab-content -->
</div> <!-- /col-md-12 -->
</div> <!-- /row -->
</div> <!-- /container -->
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/debug.php';
?>
<script type='text/javascript'>
<?php
$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";
?>
</script>
<script src="js/footable.min.js"></script>
<script src="js/debug.js"></script>
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
} else {
header('Location: /');
exit();
}
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -0,0 +1,53 @@
<?php
session_start();
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
if (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != 'admin') {
exit();
}
if (preg_match('/^[a-z\-]{0,}-mailcow/', $_GET['service'])) {
if ($_GET['action'] == "start") {
header('Content-Type: text/html; charset=utf-8');
$retry = 0;
while (docker($_GET['service'], 'info')['State']['Running'] != 1 && $retry <= 3) {
$response = docker($_GET['service'], 'post', 'start');
$response = json_decode($response, true);
$last_response = ($response['type'] == "success") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response['msg'] . '</span></b>';
if ($response['type'] == "success") {
break;
}
usleep(1500000);
$retry++;
}
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Already running</span></b>' : $last_response;
}
if ($_GET['action'] == "stop") {
header('Content-Type: text/html; charset=utf-8');
$retry = 0;
while (docker($_GET['service'], 'info')['State']['Running'] == 1 && $retry <= 3) {
$response = docker($_GET['service'], 'post', 'stop');
$response = json_decode($response, true);
$last_response = ($response['type'] == "success") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response['msg'] . '</span></b>';
if ($response['type'] == "success") {
break;
}
usleep(1500000);
$retry++;
}
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Not running</span></b>' : $last_response;
}
if ($_GET['action'] == "restart") {
header('Content-Type: text/html; charset=utf-8');
$response = docker($_GET['service'], 'post', 'restart');
$response = json_decode($response, true);
$last_response = ($response['type'] == "success") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response['msg'] . '</span></b>';
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Cannot restart container</span></b>' : $last_response;
}
if ($_GET['action'] == "logs") {
$lines = (empty($_GET['lines']) || !is_numeric($_GET['lines'])) ? 1000 : $_GET['lines'];
header('Content-Type: text/plain; charset=utf-8');
print_r(preg_split('/\n/', docker($_GET['service'], 'logs', $lines)));
}
}
?>

View File

@ -0,0 +1,12 @@
<?php
session_start();
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
header('Content-Type: text/plain');
if (!isset($_SESSION['mailcow_cc_role'])) {
exit();
}
if (isset($_GET['type']) && isset($_GET['msg'])) {
global $mailcow_hostname;
//empty
}
?>

View File

@ -0,0 +1,83 @@
<?php
session_start();
header("Content-Type: application/json");
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
if (!isset($_SESSION['mailcow_cc_role'])) {
exit();
}
function rrmdir($src) {
$dir = opendir($src);
while(false !== ( $file = readdir($dir)) ) {
if (( $file != '.' ) && ( $file != '..' )) {
$full = $src . '/' . $file;
if ( is_dir($full) ) {
rrmdir($full);
}
else {
unlink($full);
}
}
}
closedir($dir);
rmdir($src);
}
if (!empty($_GET['id']) && ctype_alnum($_GET['id'])) {
$tmpdir = '/tmp/' . $_GET['id'] . '/';
$mailc = quarantaine('details', $_GET['id']);
if (strlen($mailc['msg']) > 10485760) {
echo json_encode(array('error' => 'Message size exceeds 10 MiB.'));
exit;
}
if (!empty($mailc['msg'])) {
// Init message array
$data = array();
// Init parser
$mail_parser = new PhpMimeMailParser\Parser();
// Load msg to parser
$mail_parser->setText($mailc['msg']);
// Get text/plain content
$data['text_plain'] = $mail_parser->getMessageBody('text');
// Get subject
$data['subject'] = $mail_parser->getHeader('subject');
// Get attachments
if (is_dir($tmpdir)) {
rrmdir($tmpdir);
}
mkdir('/tmp/' . $_GET['id']);
$mail_parser->saveAttachments($tmpdir, true);
$atts = $mail_parser->getAttachments(true);
if (count($atts) > 0) {
foreach ($atts as $key => $val) {
$data['attachments'][$key] = array(
// Index
// 0 => file name
// 1 => mime type
// 2 => file size
// 3 => vt link by sha256
$val->getFilename(),
$val->getContentType(),
filesize($tmpdir . $val->getFilename()),
'https://www.virustotal.com/file/' . hash_file('SHA256', $tmpdir . $val->getFilename()) . '/analysis/'
);
}
}
if (isset($_GET['att'])) {
$dl_id = intval($_GET['att']);
$dl_filename = $data['attachments'][$dl_id][0];
if (!is_dir($tmpdir . $dl_filename) && file_exists($tmpdir . $dl_filename)) {
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private', false);
header('Content-Type: ' . $data['attachments'][$dl_id][1]);
header('Content-Disposition: attachment; filename="'. $dl_filename . '";');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . $data['attachments'][$dl_id][2]);
readfile($tmpdir . $dl_filename);
exit;
}
}
echo json_encode($data);
}
}
?>

View File

@ -1,39 +0,0 @@
<?php
session_start();
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
header('Content-Type: text/html; charset=utf-8');
if (!isset($_SESSION['mailcow_cc_role']) || $_SESSION['mailcow_cc_role'] != 'admin') {
exit();
}
if ($_GET['ACTION'] == "start") {
$retry = 0;
while (docker('sogo-mailcow', 'info')['State']['Running'] != 1 && $retry <= 3) {
$response = docker('sogo-mailcow', 'post', 'start');
$response = json_decode($response, true);
$last_response = ($response['type'] == "success") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response['msg'] . '</span></b>';
if ($response['type'] == "success") {
break;
}
usleep(1500000);
$retry++;
}
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Already running</span></b>' : $last_response;
}
if ($_GET['ACTION'] == "stop") {
$retry = 0;
while (docker('sogo-mailcow', 'info')['State']['Running'] == 1 && $retry <= 3) {
$response = docker('sogo-mailcow', 'post', 'stop');
$response = json_decode($response, true);
$last_response = ($response['type'] == "success") ? '<b><span class="pull-right text-success">OK</span></b>' : '<b><span class="pull-right text-danger">Error: ' . $response['msg'] . '</span></b>';
if ($response['type'] == "success") {
break;
}
usleep(1500000);
$retry++;
}
echo (!isset($last_response)) ? '<b><span class="pull-right text-warning">Not running</span></b>' : $last_response;
}
?>

View File

@ -8,6 +8,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/footer.php';
<script src="/js/bootstrap-select.min.js"></script> <script src="/js/bootstrap-select.min.js"></script>
<script src="/js/bootstrap-filestyle.min.js"></script> <script src="/js/bootstrap-filestyle.min.js"></script>
<script src="/js/notifications.min.js"></script> <script src="/js/notifications.min.js"></script>
<script src="/js/formcache.min.js"></script>
<script src="/js/numberedtextarea.min.js"></script> <script src="/js/numberedtextarea.min.js"></script>
<script src="/js/u2f-api.js"></script> <script src="/js/u2f-api.js"></script>
<script src="/js/api.js"></script> <script src="/js/api.js"></script>
@ -26,11 +27,19 @@ $(document).ready(function() {
msg = $('<span/>').html(message).text(); msg = $('<span/>').html(message).text();
if (type == 'danger') { if (type == 'danger') {
auto_hide = 0; auto_hide = 0;
$('#' + localStorage.getItem("add_modal")).modal('show');
localStorage.removeItem("add_modal");
} else { } else {
auto_hide = 5000; auto_hide = 5000;
} }
$.ajax({
url: '/inc/ajax/log_driver.php',
data: {"type": type,"msg": msg},
type: "GET"
});
$.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}}); $.notify({message: msg},{z_index: 20000, delay: auto_hide, type: type,placement: {from: "bottom",align: "right"},animate: {enter: 'animated fadeInUp',exit: 'animated fadeOutDown'}});
} }
$('[data-cached-form="true"]').formcache({key: $(this).data('id')});
<?php if (isset($_SESSION['return'])): ?> <?php if (isset($_SESSION['return'])): ?>
mailcow_alert_box(<?=json_encode($_SESSION['return']['msg']); ?>, "<?= $_SESSION['return']['type']; ?>"); mailcow_alert_box(<?=json_encode($_SESSION['return']['msg']); ?>, "<?= $_SESSION['return']['type']; ?>");
<?php endif; unset($_SESSION['return']); ?> <?php endif; unset($_SESSION['return']); ?>
@ -118,13 +127,8 @@ $(document).ready(function() {
} }
}); });
// Activate tooltips
$(function () { $(function () {
$('[data-toggle="tooltip"]').tooltip() $('[data-toggle="tooltip"]').tooltip()
})
// Hide alerts after n seconds
$("#alert-fade").fadeTo(7000, 500).slideUp(500, function(){
$("#alert-fade").alert('close');
}); });
// Remember last navigation pill // Remember last navigation pill
@ -173,36 +177,32 @@ $(document).ready(function() {
// Init Bootstrap Selectpicker // Init Bootstrap Selectpicker
$('select').selectpicker(); $('select').selectpicker();
// Trigger SOGo restart // Trigger container restart
$('#triggerRestartSogo').click(function(){ $('#RestartContainer').on('show.bs.modal', function(e) {
var container = $(e.relatedTarget).data('container');
$('#containerName').text(container);
$('#triggerRestartContainer').click(function(){
$(this).prop("disabled",true); $(this).prop("disabled",true);
$(this).html('<span class="glyphicon glyphicon-refresh glyphicon-spin"></span> '); $(this).html('<span class="glyphicon glyphicon-refresh glyphicon-spin"></span> ');
$('#statusTriggerRestartSogo').text('Stopping SOGo workers, this may take a while... '); $('#statusTriggerRestartContainer').text('Restarting container, this may take a while... ');
$.ajax({ $.ajax({
method: 'get', method: 'get',
url: '/inc/ajax/sogo_ctrl.php', url: '/inc/ajax/container_ctrl.php',
timeout: 3000,
data: { data: {
'ajax': true, 'service': container,
'ACTION': 'stop' 'action': 'restart'
},
error: function() {
window.location = window.location.href.split("#")[0];
}, },
success: function(data) { success: function(data) {
$('#statusTriggerRestartSogo').append(data); $('#statusTriggerRestartContainer').append(data);
$('#statusTriggerRestartSogo').append('<br>Starting SOGo...'); $('#triggerRestartContainer').html('<span class="glyphicon glyphicon-ok"></span> ');
$.ajax({
method: 'get',
url: '/inc/ajax/sogo_ctrl.php',
data: {
'ajax': true,
'ACTION': 'start'
},
success: function(data) {
$('#statusTriggerRestartSogo').append(data);
$('#triggerRestartSogo').html('<span class="glyphicon glyphicon-ok"></span> ');
}
});
} }
}); });
}); });
})
// CSRF // CSRF
$('<input type="hidden" value="<?= $_SESSION['CSRF']['TOKEN']; ?>">').attr('id', 'csrf_token').attr('name', 'csrf_token').appendTo('form'); $('<input type="hidden" value="<?= $_SESSION['CSRF']['TOKEN']; ?>">').attr('id', 'csrf_token').attr('name', 'csrf_token').appendTo('form');

View File

@ -18,7 +18,7 @@ function customize($_action, $_item, $_data = null) {
if (file_exists($_data['main_logo']['tmp_name']) !== true) { if (file_exists($_data['main_logo']['tmp_name']) !== true) {
$_SESSION['return'] = array( $_SESSION['return'] = array(
'type' => 'danger', 'type' => 'danger',
'msg' => 'Cannot validate image file: Temporary file not found' 'msg' => $lang['danger']['img_tmp_missing']
); );
return false; return false;
} }
@ -26,7 +26,7 @@ function customize($_action, $_item, $_data = null) {
if ($image->valid() !== true) { if ($image->valid() !== true) {
$_SESSION['return'] = array( $_SESSION['return'] = array(
'type' => 'danger', 'type' => 'danger',
'msg' => 'Cannot validate image file' 'msg' => $lang['danger']['img_invalid']
); );
return false; return false;
} }
@ -35,7 +35,7 @@ function customize($_action, $_item, $_data = null) {
catch (ImagickException $e) { catch (ImagickException $e) {
$_SESSION['return'] = array( $_SESSION['return'] = array(
'type' => 'danger', 'type' => 'danger',
'msg' => 'Cannot validate image file' 'msg' => $lang['danger']['img_invalid']
); );
return false; return false;
} }
@ -43,7 +43,7 @@ function customize($_action, $_item, $_data = null) {
else { else {
$_SESSION['return'] = array( $_SESSION['return'] = array(
'type' => 'danger', 'type' => 'danger',
'msg' => 'Invalid mime type' 'msg' => $lang['danger']['invalid_mime_type']
); );
return false; return false;
} }
@ -59,7 +59,7 @@ function customize($_action, $_item, $_data = null) {
} }
$_SESSION['return'] = array( $_SESSION['return'] = array(
'type' => 'success', 'type' => 'success',
'msg' => 'File uploaded successfully' 'msg' => $lang['success']['upload_success']
); );
break; break;
} }
@ -77,7 +77,7 @@ function customize($_action, $_item, $_data = null) {
$apps = (array)$_data['app']; $apps = (array)$_data['app'];
$links = (array)$_data['href']; $links = (array)$_data['href'];
$out = array(); $out = array();
if (count($apps) == count($links)) {; if (count($apps) == count($links)) {
for ($i = 0; $i < count($apps); $i++) { for ($i = 0; $i < count($apps); $i++) {
$out[] = array($apps[$i] => $links[$i]); $out[] = array($apps[$i] => $links[$i]);
} }
@ -94,7 +94,28 @@ function customize($_action, $_item, $_data = null) {
} }
$_SESSION['return'] = array( $_SESSION['return'] = array(
'type' => 'success', 'type' => 'success',
'msg' => 'Saved changes to app links' 'msg' => $lang['success']['app_links']
);
break;
case 'ui_texts':
$main_name = $_data['main_name'];
$apps_name = $_data['apps_name'];
$help_text = $_data['help_text'];
try {
$redis->set('MAIN_NAME', htmlspecialchars($main_name));
$redis->set('APPS_NAME', htmlspecialchars($apps_name));
$redis->set('HELP_TEXT', $help_text);
}
catch (RedisException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Redis: '.$e
);
return false;
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => $lang['success']['ui_texts']
); );
break; break;
} }
@ -113,7 +134,7 @@ function customize($_action, $_item, $_data = null) {
if ($redis->del('MAIN_LOGO')) { if ($redis->del('MAIN_LOGO')) {
$_SESSION['return'] = array( $_SESSION['return'] = array(
'type' => 'success', 'type' => 'success',
'msg' => 'Reset default logo' 'msg' => $lang['success']['reset_main_logo']
); );
return true; return true;
} }
@ -155,6 +176,21 @@ function customize($_action, $_item, $_data = null) {
return false; return false;
} }
break; break;
case 'ui_texts':
try {
$data['main_name'] = ($main_name = $redis->get('MAIN_NAME')) ? $main_name : 'mailcow UI';
$data['apps_name'] = ($apps_name = $redis->get('APPS_NAME')) ? $apps_name : 'mailcow Apps';
$data['help_text'] = ($help_text = $redis->get('HELP_TEXT')) ? $help_text : false;
return $data;
}
catch (RedisException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Redis: '.$e
);
return false;
}
break;
case 'main_logo_specs': case 'main_logo_specs':
try { try {
$image = new Imagick(); $image = new Imagick();
@ -167,7 +203,7 @@ function customize($_action, $_item, $_data = null) {
catch (ImagickException $e) { catch (ImagickException $e) {
$_SESSION['return'] = array( $_SESSION['return'] = array(
'type' => 'danger', 'type' => 'danger',
'msg' => 'Error: Imagick exception while reading image' 'msg' => $lang['danger']['imagick_exception']
); );
return false; return false;
} }

View File

@ -1,5 +1,12 @@
<?php <?php
function docker($service_name, $action, $post_action = null, $post_fields = null) { function docker($service_name, $action, $attr1 = null, $attr2 = null, $extra_headers = null) {
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
$curl = curl_init(); $curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER,array( 'Content-Type: application/json' )); curl_setopt($curl, CURLOPT_HTTPHEADER,array( 'Content-Type: application/json' ));
switch($action) { switch($action) {
@ -52,14 +59,44 @@ function docker($service_name, $action, $post_action = null, $post_fields = null
return false; return false;
} }
break; break;
case 'post': case 'logs':
if (!empty($post_action)) {
$container_id = docker($service_name, 'get_id'); $container_id = docker($service_name, 'get_id');
if (ctype_xdigit($container_id) && ctype_alnum($post_action)) { if (ctype_xdigit($container_id)) {
curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/' . $container_id . '/' . $post_action); $lines = (empty($attr1) || !is_numeric($attr1)) ? 100 : $attr1;
curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/' . $container_id . '/logs/' . $lines);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
if (empty($response)) {
return true;
}
else {
return json_decode($response, true);
}
}
}
else {
return false;
}
break;
case 'post':
if (!empty($attr1)) {
$container_id = docker($service_name, 'get_id');
if (ctype_xdigit($container_id) && ctype_alnum($attr1)) {
curl_setopt($curl, CURLOPT_URL, 'http://dockerapi:8080/containers/' . $container_id . '/' . $attr1);
curl_setopt($curl, CURLOPT_POST, 1); curl_setopt($curl, CURLOPT_POST, 1);
if (!empty($post_fields)) { if (!empty($attr2)) {
curl_setopt( $curl, CURLOPT_POSTFIELDS, json_encode($post_fields)); curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($attr2));
}
if (!empty($extra_headers) && is_array($extra_headers)) {
curl_setopt($curl, CURLOPT_HTTPHEADER, $extra_headers);
} }
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($curl); $response = curl_exec($curl);

View File

@ -1,5 +1,4 @@
<?php <?php
if (F2B == 1) {
function fail2ban($_action, $_data = null) { function fail2ban($_action, $_data = null) {
global $redis; global $redis;
global $lang; global $lang;
@ -97,4 +96,3 @@ function fail2ban($_action, $_data = null) {
break; break;
} }
} }
}

View File

@ -443,14 +443,31 @@ function user_get_alias_details($username) {
} }
try { try {
$data['address'] = $username; $data['address'] = $username;
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `aliases` FROM `alias` $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `shared_aliases` FROM `alias`
WHERE `goto` REGEXP :username_goto WHERE `goto` REGEXP :username_goto
AND `address` NOT LIKE '@%' AND `address` NOT LIKE '@%'
AND `goto` != :username_goto2
AND `address` != :username_address"); AND `address` != :username_address");
$stmt->execute(array(':username_goto' => '(^|,)'.$username.'($|,)', ':username_address' => $username)); $stmt->execute(array(
':username_goto' => '(^|,)'.$username.'($|,)',
':username_goto2' => $username,
':username_address' => $username
));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC); $run = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($run)) { while ($row = array_shift($run)) {
$data['aliases'] = $row['aliases']; $data['shared_aliases'] = $row['shared_aliases'];
}
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(`address` SEPARATOR ', '), '&#10008;') AS `direct_aliases` FROM `alias`
WHERE `goto` = :username_goto
AND `address` != :username_address");
$stmt->execute(
array(
':username_goto' => $username,
':username_address' => $username
));
$run = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($run)) {
$data['direct_aliases'] = $row['direct_aliases'];
} }
$stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ', '), '&#10008;') AS `ad_alias` FROM `mailbox` $stmt = $pdo->prepare("SELECT IFNULL(GROUP_CONCAT(local_part, '@', alias_domain SEPARATOR ', '), '&#10008;') AS `ad_alias` FROM `mailbox`
LEFT OUTER JOIN `alias_domain` on `target_domain` = `domain` LEFT OUTER JOIN `alias_domain` on `target_domain` = `domain`
@ -851,6 +868,135 @@ function verify_tfa_login($username, $token) {
} }
return false; return false;
} }
function admin_api($action, $data = null) {
global $pdo;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
switch ($action) {
case "edit":
$regen_key = $data['admin_api_regen_key'];
$active = (isset($data['active'])) ? 1 : 0;
$allow_from = array_map('trim', preg_split( "/( |,|;|\n)/", $data['allow_from']));
foreach ($allow_from as $key => $val) {
if (!filter_var($val, FILTER_VALIDATE_IP)) {
unset($allow_from[$key]);
continue;
}
}
$allow_from = implode(',', array_unique(array_filter($allow_from)));
if (empty($allow_from)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'List of allowed IPs cannot be empty'
);
return false;
}
$api_key = implode('-', array(
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3)))
));
$stmt = $pdo->prepare("INSERT INTO `api` (`username`, `api_key`, `active`, `allow_from`)
SELECT `username`, :api_key, :active, :allow_from FROM `admin` WHERE `superadmin`='1' AND `active`='1'
ON DUPLICATE KEY UPDATE `active` = :active_u, `allow_from` = :allow_from_u ;");
$stmt->execute(array(
':api_key' => $api_key,
':active' => $active,
':active_u' => $active,
':allow_from' => $allow_from,
':allow_from_u' => $allow_from
));
break;
case "regen_key":
$api_key = implode('-', array(
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3))),
strtoupper(bin2hex(random_bytes(3)))
));
$stmt = $pdo->prepare("UPDATE `api` SET `api_key` = :api_key WHERE `username` IN
(SELECT `username` FROM `admin` WHERE `superadmin`='1' AND `active`='1')");
$stmt->execute(array(
':api_key' => $api_key
));
break;
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => sprintf($lang['success']['admin_modified'])
);
}
function rspamd_ui($action, $data = null) {
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
switch ($action) {
case "edit":
$rspamd_ui_pass = $data['rspamd_ui_pass'];
$rspamd_ui_pass2 = $data['rspamd_ui_pass2'];
if (empty($rspamd_ui_pass) || empty($rspamd_ui_pass2)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Password cannot be empty'
);
return false;
}
if ($rspamd_ui_pass != $rspamd_ui_pass2) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Passwords do not match'
);
return false;
}
if (strlen($rspamd_ui_pass) < 6) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Please use at least 6 characters for your password'
);
return false;
}
$docker_return = docker('rspamd-mailcow', 'post', 'exec', array('cmd' => '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(
'type' => 'success',
'msg' => 'Rspamd UI password set successfully'
);
return true;
}
else {
$_SESSION['return'] = array(
'type' => $docker_return_array['type'],
'msg' => $docker_return_array['msg']
);
return false;
}
}
else {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Unknown error'
);
return false;
}
break;
}
}
function get_admin_details() { function get_admin_details() {
// No parameter to be given, only one admin should exist // No parameter to be given, only one admin should exist
global $pdo; global $pdo;
@ -860,8 +1006,10 @@ function get_admin_details() {
return false; return false;
} }
try { try {
$stmt = $pdo->prepare("SELECT `username`, `modified`, `created` FROM `admin` WHERE `superadmin`='1' AND active='1'"); $stmt = $pdo->query("SELECT `admin`.`username`, `api`.`active` AS `api_active`, `api`.`api_key`, `api`.`allow_from` FROM `admin`
$stmt->execute(); INNER JOIN `api` ON `admin`.`username` = `api`.`username`
WHERE `admin`.`superadmin`='1'
AND `admin`.`active`='1'");
$data = $stmt->fetch(PDO::FETCH_ASSOC); $data = $stmt->fetch(PDO::FETCH_ASSOC);
} }
catch(PDOException $e) { catch(PDOException $e) {
@ -932,6 +1080,51 @@ function get_logs($container, $lines = false) {
return $data_array; return $data_array;
} }
} }
if ($container == "watchdog-mailcow") {
if (!is_numeric($lines)) {
list ($from, $to) = explode('-', $lines);
$data = $redis->lRange('WATCHDOG_LOG', intval($from), intval($to));
}
else {
$data = $redis->lRange('WATCHDOG_LOG', 0, intval($lines));
}
if ($data) {
foreach ($data as $json_line) {
$data_array[] = json_decode($json_line, true);
}
return $data_array;
}
}
if ($container == "acme-mailcow") {
if (!is_numeric($lines)) {
list ($from, $to) = explode('-', $lines);
$data = $redis->lRange('ACME_LOG', intval($from), intval($to));
}
else {
$data = $redis->lRange('ACME_LOG', 0, intval($lines));
}
if ($data) {
foreach ($data as $json_line) {
$data_array[] = json_decode($json_line, true);
}
return $data_array;
}
}
if ($container == "api-mailcow") {
if (!is_numeric($lines)) {
list ($from, $to) = explode('-', $lines);
$data = $redis->lRange('API_LOG', intval($from), intval($to));
}
else {
$data = $redis->lRange('API_LOG', 0, intval($lines));
}
if ($data) {
foreach ($data as $json_line) {
$data_array[] = json_decode($json_line, true);
}
return $data_array;
}
}
if ($container == "fail2ban-mailcow") { if ($container == "fail2ban-mailcow") {
if (!is_numeric($lines)) { if (!is_numeric($lines)) {
list ($from, $to) = explode('-', $lines); list ($from, $to) = explode('-', $lines);

View File

@ -490,9 +490,20 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
if (in_array($address, $gotos)) { if (in_array($address, $gotos)) {
continue; continue;
} }
$domain = idn_to_ascii(substr(strstr($address, '@'), 1));
$local_part = strstr($address, '@', true);
$address = $local_part.'@'.$domain;
$stmt = $pdo->prepare("SELECT `address` FROM `alias` $stmt = $pdo->prepare("SELECT `address` FROM `alias`
WHERE `address`= :address"); WHERE `address`= :address OR `address` IN (
$stmt->execute(array(':address' => $address)); SELECT `username` FROM `mailbox`, `alias_domain`
WHERE (
`alias_domain`.`alias_domain` = :address_d
AND `mailbox`.`username` = CONCAT(:address_l, '@', alias_domain.target_domain)))");
$stmt->execute(array(
':address' => $address,
':address_l' => $local_part,
':address_d' => $domain
));
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) { if ($num_results != 0) {
$_SESSION['return'] = array( $_SESSION['return'] = array(
@ -501,9 +512,6 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
); );
return false; return false;
} }
$domain = idn_to_ascii(substr(strstr($address, '@'), 1));
$local_part = strstr($address, '@', true);
$address = $local_part.'@'.$domain;
$domaindata = mailbox('get', 'domain_details', $domain); $domaindata = mailbox('get', 'domain_details', $domain);
if (is_array($domaindata) && $domaindata['aliases_left'] == "0") { if (is_array($domaindata) && $domaindata['aliases_left'] == "0") {
$_SESSION['return'] = array( $_SESSION['return'] = array(
@ -722,7 +730,7 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
} }
$active = intval($_data['active']); $active = intval($_data['active']);
$quota_b = ($quota_m * 1048576); $quota_b = ($quota_m * 1048576);
$maildir = $domain . "/" . $local_part . "/mail-" . time() . "/"; $maildir = $domain . "/" . $local_part . "/mails/";
if (!is_valid_domain_name($domain)) { if (!is_valid_domain_name($domain)) {
$_SESSION['return'] = array( $_SESSION['return'] = array(
'type' => 'danger', 'type' => 'danger',
@ -2302,7 +2310,7 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
} }
else { else {
try { try {
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND `domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role"); $stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `kind` NOT REGEXP 'location|thing|group' AND (`domain` IN (SELECT `domain` FROM `domain_admins` WHERE `active` = '1' AND `username` = :username) OR 'admin' = :role)");
$stmt->execute(array( $stmt->execute(array(
':username' => $_SESSION['mailcow_cc_username'], ':username' => $_SESSION['mailcow_cc_username'],
':role' => $_SESSION['mailcow_cc_role'], ':role' => $_SESSION['mailcow_cc_role'],
@ -3360,7 +3368,7 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
)); ));
$stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `local_dest` = :domain"); $stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `local_dest` = :domain");
$stmt->execute(array( $stmt->execute(array(
':domain' => '%@'.$domain, ':domain' => $domain,
)); ));
} }
catch (PDOException $e) { catch (PDOException $e) {
@ -3484,7 +3492,7 @@ function mailbox($_action, $_type, $_data = null, $attr = null) {
)); ));
$stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `local_dest` = :alias_domain"); $stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `local_dest` = :alias_domain");
$stmt->execute(array( $stmt->execute(array(
':domain' => '%@'.$alias_domain, ':domain' => $alias_domain,
)); ));
} }
catch (PDOException $e) { catch (PDOException $e) {

View File

@ -0,0 +1,282 @@
<?php
function quarantaine($_action, $_data = null) {
global $pdo;
global $redis;
global $lang;
switch ($_action) {
case 'delete':
if (!is_array($_data['id'])) {
$ids = array();
$ids[] = $_data['id'];
}
else {
$ids = $_data['id'];
}
if (!isset($_SESSION['acl']['quarantaine']) || $_SESSION['acl']['quarantaine'] != "1" ) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
foreach ($ids as $id) {
if (!is_numeric($id)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
try {
$stmt = $pdo->prepare('SELECT `rcpt` FROM `quarantaine` WHERE `id` = :id');
$stmt->execute(array(':id' => $id));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['rcpt'])) {
try {
$stmt = $pdo->prepare("DELETE FROM `quarantaine` WHERE `id` = :id");
$stmt->execute(array(
':id' => $id
));
}
catch (PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
return false;
}
}
else {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
}
catch(PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
}
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => sprintf($lang['success']['items_deleted'], implode(', ', $ids))
);
break;
case 'edit':
if (!isset($_SESSION['acl']['quarantaine']) || $_SESSION['acl']['quarantaine'] != "1" ) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
// Edit settings
if ($_data['action'] == 'settings') {
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
$retention_size = $_data['retention_size'];
$max_size = $_data['max_size'];
$exclude_domains = (array)$_data['exclude_domains'];
try {
$redis->Set('Q_RETENTION_SIZE', intval($retention_size));
$redis->Set('Q_MAX_SIZE', intval($max_size));
$redis->Set('Q_EXCLUDE_DOMAINS', json_encode($exclude_domains));
}
catch (RedisException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Redis: '.$e
);
return false;
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => 'Saved settings'
);
}
// Release item
elseif ($_data['action'] == 'release') {
if (!is_array($_data['id'])) {
$ids = array();
$ids[] = $_data['id'];
}
else {
$ids = $_data['id'];
}
foreach ($ids as $id) {
if (!is_numeric($id)) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
try {
$stmt = $pdo->prepare('SELECT `msg`, `qid`, `sender`, `rcpt` FROM `quarantaine` WHERE `id` = :id');
$stmt->execute(array(':id' => $id));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['rcpt'])) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
}
catch(PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
}
$sender = (isset($row['sender'])) ? $row['sender'] : 'sender-unknown@rspamd';
try {
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->SMTPDebug = 0;
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
if (!empty(gethostbynamel('postfix-mailcow'))) {
$postfix = 'apostfix-mailcow';
}
if (!empty(gethostbynamel('postfix'))) {
$postfix = 'postfix';
}
else {
$_SESSION['return'] = array(
'type' => 'warning',
'msg' => sprintf($lang['danger']['release_send_failed'], 'Cannot determine Postfix host')
);
return false;
}
$mail->Host = $postfix;
$mail->Port = 590;
$mail->setFrom($sender);
$mail->CharSet = 'UTF-8';
$mail->Subject = sprintf($lang['quarantaine']['release_subject'], $row['qid']);
$mail->addAddress($row['rcpt']);
$mail->IsHTML(false);
$msg_tmpf = tempnam("/tmp", $row['qid']);
file_put_contents($msg_tmpf, $row['msg']);
$mail->addAttachment($msg_tmpf, $row['qid'] . '.eml');
$mail->Body = sprintf($lang['quarantaine']['release_body']);
$mail->send();
unlink($msg_tmpf);
}
catch (phpmailerException $e) {
unlink($msg_tmpf);
$_SESSION['return'] = array(
'type' => 'warning',
'msg' => sprintf($lang['danger']['release_send_failed'], $e->errorMessage())
);
return false;
}
try {
$stmt = $pdo->prepare("DELETE FROM `quarantaine` WHERE `id` = :id");
$stmt->execute(array(
':id' => $id
));
}
catch (PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
return false;
}
}
$_SESSION['return'] = array(
'type' => 'success',
'msg' => $lang['success']['items_released']
);
}
return true;
break;
case 'get':
try {
if ($_SESSION['mailcow_cc_role'] == "user") {
$stmt = $pdo->prepare('SELECT `id`, `qid`, `rcpt`, `sender`, UNIX_TIMESTAMP(`created`) AS `created` FROM `quarantaine` WHERE `rcpt` = :mbox');
$stmt->execute(array(':mbox' => $_SESSION['mailcow_cc_username']));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) {
$q_meta[] = $row;
}
}
else {
foreach (mailbox('get', 'mailboxes') as $mbox) {
$stmt = $pdo->prepare('SELECT `id`, `qid`, `rcpt`, `sender`, UNIX_TIMESTAMP(`created`) AS `created` FROM `quarantaine` WHERE `rcpt` = :mbox');
$stmt->execute(array(':mbox' => $mbox));
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while($row = array_shift($rows)) {
$q_meta[] = $row;
}
}
}
}
catch(PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
}
return $q_meta;
break;
case 'settings':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => sprintf($lang['danger']['access_denied'])
);
return false;
}
try {
$settings['exclude_domains'] = json_decode($redis->Get('Q_EXCLUDE_DOMAINS'), true);
$settings['max_size'] = $redis->Get('Q_MAX_SIZE');
$settings['retention_size'] = $redis->Get('Q_RETENTION_SIZE');
}
catch (RedisException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Redis: '.$e
);
return false;
}
return $settings;
break;
case 'details':
if (!is_numeric($_data) || empty($_data)) {
return false;
}
try {
$stmt = $pdo->prepare('SELECT `rcpt`, `symbols`, `msg`, `domain` FROM `quarantaine` WHERE `id`= :id');
$stmt->execute(array(':id' => $_data));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['rcpt'])) {
return $row;
}
return false;
}
catch(PDOException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'MySQL: '.$e
);
}
return false;
break;
}
}

View File

@ -15,6 +15,7 @@
<?php else: ?> <?php else: ?>
<link rel="stylesheet" href="/css/bootstrap.min.css"> <link rel="stylesheet" href="/css/bootstrap.min.css">
<?php endif; ?> <?php endif; ?>
<link rel="stylesheet" href="/css/breakpoint.min.css">
<link rel="stylesheet" href="/css/bootstrap-select.min.css"> <link rel="stylesheet" href="/css/bootstrap-select.min.css">
<link rel="stylesheet" href="/css/bootstrap-slider.min.css"> <link rel="stylesheet" href="/css/bootstrap-slider.min.css">
<link rel="stylesheet" href="/css/bootstrap-switch.min.css"> <link rel="stylesheet" href="/css/bootstrap-switch.min.css">
@ -27,6 +28,8 @@
<?= (preg_match("/admin.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/admin.css">' : null; ?> <?= (preg_match("/admin.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/admin.css">' : null; ?>
<?= (preg_match("/user.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/user.css">' : null; ?> <?= (preg_match("/user.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/user.css">' : null; ?>
<?= (preg_match("/edit.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/edit.css">' : null; ?> <?= (preg_match("/edit.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/edit.css">' : null; ?>
<?= (preg_match("/quarantaine.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/quarantaine.css">' : null; ?>
<?= (preg_match("/debug.php/i", $_SERVER['REQUEST_URI'])) ? '<link rel="stylesheet" href="/css/debug.css">' : null; ?>
<link rel="shortcut icon" href="/favicon.png" type="image/png"> <link rel="shortcut icon" href="/favicon.png" type="image/png">
<link rel="icon" href="/favicon.png" type="image/png"> <link rel="icon" href="/favicon.png" type="image/png">
</head> </head>
@ -35,7 +38,6 @@
<div class="container-fluid"> <div class="container-fluid">
<div class="navbar-header"> <div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
@ -70,6 +72,7 @@
if (isset($_SESSION['mailcow_cc_role'])) { if (isset($_SESSION['mailcow_cc_role'])) {
if ($_SESSION['mailcow_cc_role'] == 'admin') { if ($_SESSION['mailcow_cc_role'] == 'admin') {
?> ?>
<li<?= (preg_match("/debug/i", $_SERVER['REQUEST_URI'])) ? ' class="active"' : ''; ?>><a href="/debug.php"><?= $lang['header']['debug']; ?></a></li>
<li<?= (preg_match("/admin/i", $_SERVER['REQUEST_URI'])) ? ' class="active"' : ''; ?>><a href="/admin.php"><?= $lang['header']['administration']; ?></a></li> <li<?= (preg_match("/admin/i", $_SERVER['REQUEST_URI'])) ? ' class="active"' : ''; ?>><a href="/admin.php"><?= $lang['header']['administration']; ?></a></li>
<?php <?php
} }
@ -88,14 +91,19 @@
</ul> </ul>
</li> </li>
<?php <?php
if (isset($_SESSION['mailcow_cc_role'])) {
?>
<li<?= (preg_match("/quarantaine/i", $_SERVER['REQUEST_URI'])) ? ' class="active"' : ''; ?>><a href="/quarantaine.php"><span style="font-size: 12px;" class="glyphicon glyphicon-briefcase"></span> <?= $lang['header']['quarantaine']; ?></a></li>
<?php
}
if ($_SESSION['mailcow_cc_role'] == 'admin') { if ($_SESSION['mailcow_cc_role'] == 'admin') {
?> ?>
<li><a href data-toggle="modal" data-target="#RestartSOGo"><span style="font-size: 12px;" class="glyphicon glyphicon-refresh" aria-hidden="true"></span> <?= $lang['header']['restart_sogo']; ?></a></li> <li><a href data-toggle="modal" data-container="sogo-mailcow" data-target="#RestartContainer"><span style="font-size: 12px;" class="glyphicon glyphicon-refresh"></span> <?= $lang['header']['restart_sogo']; ?></a></li>
<?php <?php
} }
?> ?>
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="glyphicon glyphicon-link" aria-hidden="true"></span> Apps <span class="caret"></span></a> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"><span class="glyphicon glyphicon-link"></span> Apps <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu"> <ul class="dropdown-menu" role="menu">
<?php <?php
foreach ($MAILCOW_APPS as $app): foreach ($MAILCOW_APPS as $app):

View File

@ -3,7 +3,7 @@ function init_db_schema() {
try { try {
global $pdo; global $pdo;
$db_version = "16112017_2259"; $db_version = "29112017_1515";
$stmt = $pdo->query("SHOW TABLES LIKE 'versions'"); $stmt = $pdo->query("SHOW TABLES LIKE 'versions'");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
@ -103,6 +103,30 @@ function init_db_schema() {
), ),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
), ),
"api" => array(
"cols" => array(
"username" => "VARCHAR(255) NOT NULL",
"api_key" => "VARCHAR(255) NOT NULL",
"allow_from" => "VARCHAR(512) NOT NULL",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE NOW(0)",
"active" => "TINYINT(1) NOT NULL DEFAULT '1'"
),
"keys" => array(
"primary" => array(
"" => array("username")
),
"fkey" => array(
"fk_username_api" => array(
"col" => "username",
"ref" => "admin.username",
"delete" => "CASCADE",
"update" => "NO ACTION"
)
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"sender_acl" => array( "sender_acl" => array(
"cols" => array( "cols" => array(
"logged_in_as" => "VARCHAR(255) NOT NULL", "logged_in_as" => "VARCHAR(255) NOT NULL",
@ -133,6 +157,28 @@ function init_db_schema() {
), ),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
), ),
"quarantaine" => array(
"cols" => array(
"id" => "INT NOT NULL AUTO_INCREMENT",
"qid" => "VARCHAR(30) NOT NULL",
"score" => "FLOAT(8,2)",
"ip" => "VARBINARY(16)",
"action" => "CHAR(20) NOT NULL DEFAULT 'unknown'",
"symbols" => "JSON",
"sender" => "VARCHAR(255) NOT NULL DEFAULT 'unknown'",
"rcpt" => "VARCHAR(255)",
"msg" => "LONGTEXT",
"domain" => "VARCHAR(255)",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"user" => "VARCHAR(255) NOT NULL DEFAULT 'unknown'",
),
"keys" => array(
"primary" => array(
"" => array("id")
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"mailbox" => array( "mailbox" => array(
"cols" => array( "cols" => array(
"username" => "VARCHAR(255) NOT NULL", "username" => "VARCHAR(255) NOT NULL",
@ -191,6 +237,51 @@ function init_db_schema() {
), ),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC" "attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
), ),
"imap_user_shares" => array(
"cols" => array(
"from_user" => "VARCHAR(255) NOT NULL",
"to_user" => "VARCHAR(255) NOT NULL",
"dummy" => "CHAR(1) DEFAULT '1'",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP"
),
"keys" => array(
"primary" => array(
"" => array("from_user", "to_user")
),
"fkey" => array(
"fk_from_user_user_shares" => array(
"col" => "from_user",
"ref" => "mailbox.username",
"delete" => "CASCADE",
"update" => "NO ACTION"
)
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"imap_anyone_shares" => array(
"cols" => array(
"from_user" => "VARCHAR(255) NOT NULL",
"dummy" => "CHAR(1) DEFAULT '1'",
"created" => "DATETIME(0) NOT NULL DEFAULT NOW(0)",
"modified" => "DATETIME ON UPDATE CURRENT_TIMESTAMP"
),
"keys" => array(
"primary" => array(
"" => array("from_user")
),
"fkey" => array(
"fk_from_anyone_user_shares" => array(
"col" => "from_user",
"ref" => "mailbox.username",
"delete" => "CASCADE",
"update" => "NO ACTION"
)
)
),
"attr" => "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC"
),
"user_acl" => array( "user_acl" => array(
"cols" => array( "cols" => array(
"username" => "VARCHAR(255) NOT NULL", "username" => "VARCHAR(255) NOT NULL",
@ -202,6 +293,7 @@ function init_db_schema() {
"syncjobs" => "TINYINT(1) NOT NULL DEFAULT '1'", "syncjobs" => "TINYINT(1) NOT NULL DEFAULT '1'",
"eas_reset" => "TINYINT(1) NOT NULL DEFAULT '1'", "eas_reset" => "TINYINT(1) NOT NULL DEFAULT '1'",
"filters" => "TINYINT(1) NOT NULL DEFAULT '1'", "filters" => "TINYINT(1) NOT NULL DEFAULT '1'",
"quarantaine" => "TINYINT(1) NOT NULL DEFAULT '1'",
"bcc_maps" => "TINYINT(1) NOT NULL DEFAULT '1'", "bcc_maps" => "TINYINT(1) NOT NULL DEFAULT '1'",
), ),
"keys" => array( "keys" => array(
@ -708,6 +800,20 @@ function init_db_schema() {
$pdo->query($create); $pdo->query($create);
} }
// Create events to clean database
$events[] = 'DROP EVENT IF EXISTS clean_spamalias;
DELIMITER //
CREATE EVENT clean_spamalias
ON SCHEDULE EVERY 1 DAY DO
BEGIN
DELETE FROM spamalias WHERE validity < UNIX_TIMESTAMP();
END;
//
DELIMITER ;';
foreach ($events as $event) {
$pdo->exec($event);
}
// Inject admin if not exists // Inject admin if not exists
$stmt = $pdo->query("SELECT NULL FROM `admin`"); $stmt = $pdo->query("SELECT NULL FROM `admin`");
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC)); $num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));

View File

@ -2,6 +2,7 @@
"require": { "require": {
"robthree/twofactorauth": "^1.6", "robthree/twofactorauth": "^1.6",
"yubico/u2flib-server": "^1.0", "yubico/u2flib-server": "^1.0",
"phpmailer/phpmailer": "^5.2" "phpmailer/phpmailer": "^5.2",
"php-mime-mail-parser/php-mime-mail-parser": "^2.9"
} }
} }

View File

@ -4,20 +4,100 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "d51ef1712a74b0dfed729f2bdd85d1e3", "content-hash": "ee4c9e269c29282221ce88bc23f1bda9",
"packages": [ "packages": [
{ {
"name": "phpmailer/phpmailer", "name": "php-mime-mail-parser/php-mime-mail-parser",
"version": "v5.2.26", "version": "2.9.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git", "url": "https://github.com/php-mime-mail-parser/php-mime-mail-parser.git",
"reference": "70362997bda4376378be7d92d81e2200550923f7" "reference": "c6884c7bc77adbf55979db99841195b232fd30f1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/70362997bda4376378be7d92d81e2200550923f7", "url": "https://api.github.com/repos/php-mime-mail-parser/php-mime-mail-parser/zipball/c6884c7bc77adbf55979db99841195b232fd30f1",
"reference": "70362997bda4376378be7d92d81e2200550923f7", "reference": "c6884c7bc77adbf55979db99841195b232fd30f1",
"shasum": ""
},
"require": {
"ext-mailparse": "*",
"php": "^5.4.0 || ^7.0"
},
"replace": {
"exorus/php-mime-mail-parser": "*",
"messaged/php-mime-mail-parser": "*"
},
"require-dev": {
"phpunit/php-token-stream": "^1.3.0",
"phpunit/phpunit": "^4.0 || ^5.0",
"satooshi/php-coveralls": "0.*",
"squizlabs/php_codesniffer": "2.*"
},
"type": "library",
"autoload": {
"psr-4": {
"PhpMimeMailParser\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "bucabay",
"email": "gabe@fijiwebdesign.com",
"homepage": "http://www.fijiwebdesign.com",
"role": "Developer"
},
{
"name": "eXorus",
"email": "exorus.spam@gmail.com",
"homepage": "https://github.com/eXorus/",
"role": "Developer"
},
{
"name": "M.Valinskis",
"email": "M.Valins@gmail.com",
"homepage": "https://code.google.com/p/php-mime-mail-parser",
"role": "Developer"
},
{
"name": "eugene.emmett.wood",
"email": "gene_w@cementhorizon.com",
"homepage": "https://code.google.com/p/php-mime-mail-parser",
"role": "Developer"
},
{
"name": "alknetso",
"email": "alkne@gmail.com",
"homepage": "https://code.google.com/p/php-mime-mail-parser",
"role": "Developer"
}
],
"description": "Fully Tested Mailparse Extension Wrapper for PHP 5.4+",
"homepage": "https://github.com/php-mime-mail-parser/php-mime-mail-parser",
"keywords": [
"MimeMailParser",
"mail",
"mailparse",
"mime"
],
"time": "2017-11-02T05:49:00+00:00"
},
{
"name": "phpmailer/phpmailer",
"version": "v5.2.25",
"source": {
"type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "2baf20b01690fba8cf720c1ebcf9b988eda50915"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2baf20b01690fba8cf720c1ebcf9b988eda50915",
"reference": "2baf20b01690fba8cf720c1ebcf9b988eda50915",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -81,20 +161,20 @@
} }
], ],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP", "description": "PHPMailer is a full-featured email creation and transfer class for PHP",
"time": "2017-11-04T09:26:05+00:00" "time": "2017-08-28T11:12:07+00:00"
}, },
{ {
"name": "robthree/twofactorauth", "name": "robthree/twofactorauth",
"version": "1.6.1", "version": "1.6",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/RobThree/TwoFactorAuth.git", "url": "https://github.com/RobThree/TwoFactorAuth.git",
"reference": "a77e7d822343bb88112baef808839cfae7bc5abb" "reference": "5093ab230cd8f1296d792afb6a49545f37e7fd5a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/a77e7d822343bb88112baef808839cfae7bc5abb", "url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/5093ab230cd8f1296d792afb6a49545f37e7fd5a",
"reference": "a77e7d822343bb88112baef808839cfae7bc5abb", "reference": "5093ab230cd8f1296d792afb6a49545f37e7fd5a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -132,7 +212,7 @@
"php", "php",
"tfa" "tfa"
], ],
"time": "2017-11-06T17:55:56+00:00" "time": "2017-02-17T15:24:54+00:00"
}, },
{ {
"name": "yubico/u2flib-server", "name": "yubico/u2flib-server",

View File

@ -7,4 +7,5 @@ $baseDir = dirname($vendorDir);
return array( return array(
'RobThree\\Auth\\' => array($vendorDir . '/robthree/twofactorauth/lib'), 'RobThree\\Auth\\' => array($vendorDir . '/robthree/twofactorauth/lib'),
'PhpMimeMailParser\\' => array($vendorDir . '/php-mime-mail-parser/php-mime-mail-parser/src'),
); );

View File

@ -11,6 +11,10 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
array ( array (
'RobThree\\Auth\\' => 14, 'RobThree\\Auth\\' => 14,
), ),
'P' =>
array (
'PhpMimeMailParser\\' => 18,
),
); );
public static $prefixDirsPsr4 = array ( public static $prefixDirsPsr4 = array (
@ -18,6 +22,10 @@ class ComposerStaticInit873464e4bd965a3168f133248b1b218b
array ( array (
0 => __DIR__ . '/..' . '/robthree/twofactorauth/lib', 0 => __DIR__ . '/..' . '/robthree/twofactorauth/lib',
), ),
'PhpMimeMailParser\\' =>
array (
0 => __DIR__ . '/..' . '/php-mime-mail-parser/php-mime-mail-parser/src',
),
); );
public static $classMap = array ( public static $classMap = array (

View File

@ -1,4 +1,57 @@
[ [
{
"name": "robthree/twofactorauth",
"version": "1.6",
"version_normalized": "1.6.0.0",
"source": {
"type": "git",
"url": "https://github.com/RobThree/TwoFactorAuth.git",
"reference": "5093ab230cd8f1296d792afb6a49545f37e7fd5a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/5093ab230cd8f1296d792afb6a49545f37e7fd5a",
"reference": "5093ab230cd8f1296d792afb6a49545f37e7fd5a",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "@stable"
},
"time": "2017-02-17T15:24:54+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"RobThree\\Auth\\": "lib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Janssen",
"homepage": "http://robiii.me",
"role": "Developer"
}
],
"description": "Two Factor Authentication",
"homepage": "https://github.com/RobThree/TwoFactorAuth",
"keywords": [
"Authentication",
"MFA",
"Multi Factor Authentication",
"Two Factor Authentication",
"authenticator",
"authy",
"php",
"tfa"
]
},
{ {
"name": "yubico/u2flib-server", "name": "yubico/u2flib-server",
"version": "1.0.1", "version": "1.0.1",
@ -36,72 +89,19 @@
"description": "Library for U2F implementation", "description": "Library for U2F implementation",
"homepage": "https://developers.yubico.com/php-u2flib-server" "homepage": "https://developers.yubico.com/php-u2flib-server"
}, },
{
"name": "robthree/twofactorauth",
"version": "1.6.1",
"version_normalized": "1.6.1.0",
"source": {
"type": "git",
"url": "https://github.com/RobThree/TwoFactorAuth.git",
"reference": "a77e7d822343bb88112baef808839cfae7bc5abb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/RobThree/TwoFactorAuth/zipball/a77e7d822343bb88112baef808839cfae7bc5abb",
"reference": "a77e7d822343bb88112baef808839cfae7bc5abb",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "@stable"
},
"time": "2017-11-06T17:55:56+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"RobThree\\Auth\\": "lib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Janssen",
"homepage": "http://robiii.me",
"role": "Developer"
}
],
"description": "Two Factor Authentication",
"homepage": "https://github.com/RobThree/TwoFactorAuth",
"keywords": [
"Authentication",
"MFA",
"Multi Factor Authentication",
"Two Factor Authentication",
"authenticator",
"authy",
"php",
"tfa"
]
},
{ {
"name": "phpmailer/phpmailer", "name": "phpmailer/phpmailer",
"version": "v5.2.26", "version": "v5.2.25",
"version_normalized": "5.2.26.0", "version_normalized": "5.2.25.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/PHPMailer/PHPMailer.git", "url": "https://github.com/PHPMailer/PHPMailer.git",
"reference": "70362997bda4376378be7d92d81e2200550923f7" "reference": "2baf20b01690fba8cf720c1ebcf9b988eda50915"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/70362997bda4376378be7d92d81e2200550923f7", "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/2baf20b01690fba8cf720c1ebcf9b988eda50915",
"reference": "70362997bda4376378be7d92d81e2200550923f7", "reference": "2baf20b01690fba8cf720c1ebcf9b988eda50915",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -131,7 +131,7 @@
"suggest": { "suggest": {
"league/oauth2-google": "Needed for Google XOAUTH2 authentication" "league/oauth2-google": "Needed for Google XOAUTH2 authentication"
}, },
"time": "2017-11-04T09:26:05+00:00", "time": "2017-08-28T11:12:07+00:00",
"type": "library", "type": "library",
"installation-source": "dist", "installation-source": "dist",
"autoload": { "autoload": {
@ -167,5 +167,87 @@
} }
], ],
"description": "PHPMailer is a full-featured email creation and transfer class for PHP" "description": "PHPMailer is a full-featured email creation and transfer class for PHP"
},
{
"name": "php-mime-mail-parser/php-mime-mail-parser",
"version": "2.9.3",
"version_normalized": "2.9.3.0",
"source": {
"type": "git",
"url": "https://github.com/php-mime-mail-parser/php-mime-mail-parser.git",
"reference": "c6884c7bc77adbf55979db99841195b232fd30f1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-mime-mail-parser/php-mime-mail-parser/zipball/c6884c7bc77adbf55979db99841195b232fd30f1",
"reference": "c6884c7bc77adbf55979db99841195b232fd30f1",
"shasum": ""
},
"require": {
"ext-mailparse": "*",
"php": "^5.4.0 || ^7.0"
},
"replace": {
"exorus/php-mime-mail-parser": "*",
"messaged/php-mime-mail-parser": "*"
},
"require-dev": {
"phpunit/php-token-stream": "^1.3.0",
"phpunit/phpunit": "^4.0 || ^5.0",
"satooshi/php-coveralls": "0.*",
"squizlabs/php_codesniffer": "2.*"
},
"time": "2017-11-02T05:49:00+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"PhpMimeMailParser\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "bucabay",
"email": "gabe@fijiwebdesign.com",
"homepage": "http://www.fijiwebdesign.com",
"role": "Developer"
},
{
"name": "eXorus",
"email": "exorus.spam@gmail.com",
"homepage": "https://github.com/eXorus/",
"role": "Developer"
},
{
"name": "M.Valinskis",
"email": "M.Valins@gmail.com",
"homepage": "https://code.google.com/p/php-mime-mail-parser",
"role": "Developer"
},
{
"name": "eugene.emmett.wood",
"email": "gene_w@cementhorizon.com",
"homepage": "https://code.google.com/p/php-mime-mail-parser",
"role": "Developer"
},
{
"name": "alknetso",
"email": "alkne@gmail.com",
"homepage": "https://code.google.com/p/php-mime-mail-parser",
"role": "Developer"
}
],
"description": "Fully Tested Mailparse Extension Wrapper for PHP 5.4+",
"homepage": "https://github.com/php-mime-mail-parser/php-mime-mail-parser",
"keywords": [
"MimeMailParser",
"mail",
"mailparse",
"mime"
]
} }
] ]

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Vincent Dauce
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1,167 @@
# php-mime-mail-parser
A fully tested mailparse extension wrapper for PHP 5.4+
[![Latest Version](https://img.shields.io/packagist/v/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://github.com/php-mime-mail-parser/php-mime-mail-parser/releases)
[![Total Downloads](https://img.shields.io/packagist/dt/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://packagist.org/packages/php-mime-mail-parser/php-mime-mail-parser)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE)
## Why?
This extension can be used to...
* Parse and read email from Postfix
* Create webmail
* Store email information such a subject, HTML body, attachments, and etc. into a database
## Is it reliable?
Yes. All known issues have been reproduced, fixed and tested.
We use Travis CI to help ensure code quality. You can see real-time statistics below:
[![Build Status](https://img.shields.io/travis/php-mime-mail-parser/php-mime-mail-parser/master.svg?style=flat-square)](https://travis-ci.org/php-mime-mail-parser/php-mime-mail-parser)
[![Coverage](https://img.shields.io/coveralls/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://coveralls.io/r/php-mime-mail-parser/php-mime-mail-parser)
[![Quality Score](https://img.shields.io/scrutinizer/g/php-mime-mail-parser/php-mime-mail-parser.svg?style=flat-square)](https://scrutinizer-ci.com/g/php-mime-mail-parser/php-mime-mail-parser)
## How do I install it?
The easiest way is via [Composer](https://getcomposer.org/).
To install the latest version of PHP MIME Mail Parser, run the command below:
composer require php-mime-mail-parser/php-mime-mail-parser
## Requirements
The following versions of PHP are supported:
* PHP 5.4
* PHP 5.5
* PHP 5.6
* PHP 7
* HHVM
```
sudo apt install php-cli php-pear php-dev php-mbstring
```
Make sure you have the mailparse extension (http://php.net/manual/en/book.mailparse.php) properly installed. The command line `php -m | grep mailparse` need to return "mailparse" else install it:
* PHP version > 7.0: mailparse
* PHP version < 7.0: mailparse 2.1.6
Follow this steps to install mailparse:
* Compile in the temp folder the extension mailparse or mailparse-2.1.6 (workaround because pecl install doesn't work yet)
```
cd
pecl download mailparse
tar -xvf mailparse-3.0.2.tgz
cd mailparse-3.0.2/
phpize
./configure
sed -i 's/#if\s!HAVE_MBSTRING/#ifndef MBFL_MBFILTER_H/' ./mailparse.c
make
sudo mv modules/mailparse.so /usr/lib/php/20160303/
```
* Add the extension mailparse and activate it
```
echo "extension=mailparse.so" | sudo tee /etc/php/7.1/mods-available/mailparse.ini
sudo phpenmod mailparse
```
On Windows, you need to download mailparse DLL from http://pecl.php.net/package/mailparse and add the line "extension=php_mailparse.dll" to php.ini accordingly.
## How do I use it?
```php
<?php
// Include the library first
require_once __DIR__.'/vendor/autoload.php';
$path = 'path/to/mail.txt';
$Parser = new PhpMimeMailParser\Parser();
// There are four methods available to indicate which mime mail to parse.
// You only need to use one of the following four:
// 1. Specify a file path to the mime mail.
$Parser->setPath($path);
// 2. Specify a php file resource (stream) to the mime mail.
$Parser->setStream(fopen($path, "r"));
// 3. Specify the raw mime mail text.
$Parser->setText(file_get_contents($path));
// 4. Specify a stream to work with mail server
$Parser->setStream(fopen("php://stdin", "r"));
// Once we've indicated where to find the mail, we can parse out the data
$to = $Parser->getHeader('to'); // "test" <test@example.com>, "test2" <test2@example.com>
$addressesTo = $Parser->getAddresses('to'); //Return an array : [[test, test@example.com, false],[test2, test2@example.com, false]]
$from = $Parser->getHeader('from'); // "test" <test@example.com>
$addressesFrom = $Parser->getAddresses('from'); //Return an array : test, test@example.com, false
$subject = $Parser->getHeader('subject');
$text = $Parser->getMessageBody('text');
$html = $Parser->getMessageBody('html');
$htmlEmbedded = $Parser->getMessageBody('htmlEmbedded'); //HTML Body included data
$stringHeaders = $Parser->getHeadersRaw(); // Get all headers as a string, no charset conversion
$arrayHeaders = $Parser->getHeaders(); // Get all headers as an array, with charset conversion
// Pass in a writeable path to save attachments
$attach_dir = '/path/to/save/attachments/'; // Be sure to include the trailing slash
$include_inline = true; // Optional argument to include inline attachments (default: true)
$Parser->saveAttachments($attach_dir [,$include_inline]);
// Get an array of Attachment items from $Parser
$attachments = $Parser->getAttachments([$include_inline]);
// Loop through all the Attachments
if (count($attachments) > 0) {
foreach ($attachments as $attachment) {
echo 'Filename : '.$attachment->getFilename().'<br />'; // logo.jpg
echo 'Filesize : '.filesize($attach_dir.$attachment->getFilename()).'<br />'; // 1000
echo 'Filetype : '.$attachment->getContentType().'<br />'; // image/jpeg
echo 'MIME part string : '.$attachment->getMimePartStr().'<br />'; // (the whole MIME part of the attachment)
}
}
?>
```
Next you need to forward emails to this script above. For that I'm using [Postfix](http://www.postfix.org/) like a mail server, you need to configure /etc/postfix/master.cf
Add this line at the end of the file (specify myhook to send all emails to the script test.php)
```
myhook unix - n n - - pipe
flags=F user=www-data argv=php -c /etc/php5/apache2/php.ini -f /var/www/test.php ${sender} ${size} ${recipient}
```
Edit this line (register myhook)
```
smtp inet n - - - - smtpd
-o content_filter=myhook:dummy
```
The php script must use the fourth method to work with this configuration.
## Can I contribute?
Feel free to contribute!
git clone https://github.com/php-mime-mail-parser/php-mime-mail-parser
cd php-mime-mail-parser
composer install
./vendor/bin/phpunit
If you report an issue, please provide the raw email that triggered it. This helps us reproduce the issue and fix it more quickly.
### License
The php-mime-mail-parser/php-mime-mail-parser is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)

View File

@ -0,0 +1,61 @@
{
"name": "php-mime-mail-parser/php-mime-mail-parser",
"type": "library",
"description": "Fully Tested Mailparse Extension Wrapper for PHP 5.4+",
"keywords": ["mime", "mail", "mailparse", "MimeMailParser"],
"homepage": "https://github.com/php-mime-mail-parser/php-mime-mail-parser",
"license": "MIT",
"authors": [
{
"name":"eXorus",
"email":"exorus.spam@gmail.com",
"homepage":"https://github.com/eXorus/",
"role":"Developer"
},
{
"name":"M.Valinskis",
"email":"M.Valins@gmail.com",
"homepage":"https://code.google.com/p/php-mime-mail-parser",
"role":"Developer"
},
{
"name":"eugene.emmett.wood",
"email":"gene_w@cementhorizon.com",
"homepage":"https://code.google.com/p/php-mime-mail-parser",
"role":"Developer"
},
{
"name":"alknetso",
"email":"alkne@gmail.com",
"homepage":"https://code.google.com/p/php-mime-mail-parser",
"role":"Developer"
},
{
"name":"bucabay",
"email":"gabe@fijiwebdesign.com",
"homepage":"http://www.fijiwebdesign.com",
"role":"Developer"
}
],
"repository":{
"type":"git",
"url":"https://github.com/php-mime-mail-parser/php-mime-mail-parser.git"
},
"require": {
"php": "^5.4.0 || ^7.0",
"ext-mailparse": "*"
},
"require-dev": {
"phpunit/phpunit": "^4.0 || ^5.0",
"phpunit/php-token-stream": "^1.3.0",
"satooshi/php-coveralls": "0.*",
"squizlabs/PHP_CodeSniffer": "2.*"
},
"replace": {
"exorus/php-mime-mail-parser": "*",
"messaged/php-mime-mail-parser": "*"
},
"autoload": {
"psr-4": { "PhpMimeMailParser\\": "src/" }
}
}

View File

@ -0,0 +1,303 @@
<?php
/**
* @link http://php.net/manual/en/mailparse.constants.php
*/
define('MAILPARSE_EXTRACT_OUTPUT', 0);
/**
* @link http://php.net/manual/en/mailparse.constants.php
*/
define('MAILPARSE_EXTRACT_STREAM', 1);
/**
* @link http://php.net/manual/en/mailparse.constants.php
*/
define('MAILPARSE_EXTRACT_RETURN', 2);
/**
* Parses a file. This is the optimal way of parsing a mail file that you have on
* disk.
*
* @link http://php.net/manual/en/functions.mailparse-msg-parse-file.php
*
* @param string $filename Path to the file holding the message. The file is opened
* and streamed through the parser
*
* @return resource Returns a MIME resource representing the structure, or false on error
*/
function mailparse_msg_parse_file($filename)
{
}
/**
* .
*
* @link http://php.net/manual/en/functions.mailparse-msg-get-part.php
*
* @param resource $mimemail A valid MIME resource
* @param string $mimesection
*
* @return resource
*/
function mailparse_msg_get_part($mimemail, $mimesection)
{
}
/**
* .
*
* @link http://php.net/manual/en/functions.mailparse-msg-get-structure.php
*
* @param resource $mimemail A valid MIME resource
*
* @return array
*/
function mailparse_msg_get_structure($mimemail)
{
}
/**
* .
*
* @link http://php.net/manual/en/functions.mailparse-msg-get-part-data.php
*
* @param resource $mimemail A valid MIME resource
*
* @return array
*/
function mailparse_msg_get_part_data($mimemail)
{
}
/**
* .
*
* @link http://php.net/manual/en/functions.mailparse-msg-extract-part.php
*
* @param resource $mimemail A valid MIME resource
* @param string $msgbody
* @param callable $callbackfunc
*
* @return void
*/
function mailparse_msg_extract_part($mimemail, $msgbody, $callbackfunc)
{
}
/**
* Extracts/decodes a message section from the supplied filename.
*
* @link http://php.net/manual/en/functions.mailparse-msg-extract-part-file.php
*
* @param resource $mimemail A valid MIME resource, created with
* mailparse_msg_create
* @param mixed $filename Can be a file name or a valid stream resource
* @param callable $callbackfunc If set, this must be either a valid callback that
* will be passed the extracted section, or null to make this function return the
* extracted section
*
* @return string If $callbackfunc is not null returns true on success
*/
function mailparse_msg_extract_part_file($mimemail, $filename, $callbackfunc = false)
{
}
/**
* .
*
* @link http://php.net/manual/en/functions.mailparse-msg-extract-whole-part-file.php
*
* @param resource $mimemail A valid MIME resource
* @param string $filename
* @param callable $callbackfunc
*
* @return string
*/
function mailparse_msg_extract_whole_part_file($mimemail, $filename, $callbackfunc)
{
}
/**
* Create a MIME mail resource.
*
* @link http://php.net/manual/en/functions.mailparse-msg-create.php
* @return resource Returns a handle that can be used to parse a message
*/
function mailparse_msg_create()
{
}
/**
* Frees a MIME resource.
*
* @link http://php.net/manual/en/functions.mailparse-msg-free.php
*
* @param resource $mimemail A valid MIME resource allocated by
* mailparse_msg_create or mailparse_msg_parse_file
*
* @return bool
*/
function mailparse_msg_free($mimemail)
{
}
/**
* Incrementally parse data into the supplied mime mail resource.
*
* @link http://php.net/manual/en/functions.mailparse-msg-parse.php
*
* @param resource $mimemail A valid MIME resource
* @param string $data
*
* @return bool
*/
function mailparse_msg_parse($mimemail, $data)
{
}
/**
* Parses a RFC 822 compliant recipient list, such as that found in the To: header.
*
* @link http://php.net/manual/en/functions.mailparse-rfc822-parse-addresses.php
*
* @param string $addresses A string containing addresses, like in: Wez Furlong
* wez@example.com, doe@example.com
*
* @return array Returns an array of associative arrays with the following keys for each
* recipient: display The recipient name, for display purpose. If this part is not
* set for a recipient, this key will hold the same value as address. address The
* email address is_group true if the recipient is a newsgroup, false otherwise
*/
function mailparse_rfc822_parse_addresses($addresses)
{
}
/**
* Figures out the best way of encoding the content read from the given file
* pointer.
*
* @link http://php.net/manual/en/functions.mailparse-determine-best-xfer-encoding.php
*
* @param resource $fp A valid file pointer, which must be seek-able
*
* @return string Returns one of the character encodings supported by the mbstring module
*/
function mailparse_determine_best_xfer_encoding($fp)
{
}
/**
* Streams data from the source file pointer, apply $encoding and write to the
* destination file pointer.
*
* @link http://php.net/manual/en/functions.mailparse-stream-encode.php
*
* @param resource $sourcefp A valid file handle. The file is streamed through the
* parser
* @param resource $destfp The destination file handle in which the encoded data
* will be written
* @param string $encoding One of the character encodings supported by the mbstring
* module
*
* @return bool
*/
function mailparse_stream_encode($sourcefp, $destfp, $encoding)
{
}
/**
* Scans the data from the given file pointer and extract each embedded uuencoded
* file into a temporary file.
*
* @link http://php.net/manual/en/functions.mailparse-uudecode-all.php
*
* @param resource $fp A valid file pointer
*
* @return array Returns an array of associative arrays listing filename information.
* filename Path to the temporary file name created origfilename The original
* filename, for uuencoded parts only The first filename entry is the message body.
* The next entries are the decoded uuencoded files
*/
function mailparse_uudecode_all($fp)
{
}
/**
* @return
*/
function mailparse_test()
{
}
class mimemessage
{
/**
* @return
*/
public function mimemessage()
{
}
/**
* @return
*/
public function get_child()
{
}
/**
* @return
*/
public function get_child_count()
{
}
/**
* @return
*/
public function get_parent()
{
}
/**
* @return
*/
public function extract_headers()
{
}
/**
* @return
*/
public function extract_body()
{
}
/**
* @return
*/
public function enum_uue()
{
}
/**
* @return
*/
public function extract_uue()
{
}
/**
* @return
*/
public function remove()
{
}
/**
* @return
*/
public function add_child()
{
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" bootstrap="vendor/autoload.php">
<testsuite name="eXorus PhpMimeMailParser Test Suite">
<directory suffix="Test.php">tests</directory>
</testsuite>
</phpunit>

View File

@ -0,0 +1,183 @@
<?php
namespace PhpMimeMailParser;
/**
* Attachment of php-mime-mail-parser
*
* Fully Tested Mailparse Extension Wrapper for PHP 5.4+
*
*/
class Attachment
{
/**
* @var string $filename Filename
*/
protected $filename;
/**
* @var string $contentType Mime Type
*/
protected $contentType;
/**
* @var string $content File Content
*/
protected $content;
/**
* @var string $contentDisposition Content-Disposition (attachment or inline)
*/
protected $contentDisposition;
/**
* @var string $contentId Content-ID
*/
protected $contentId;
/**
* @var array $headers An Array of the attachment headers
*/
protected $headers;
/**
* @var resource $stream
*/
protected $stream;
/**
* @var string $mimePartStr
*/
protected $mimePartStr;
/**
* Attachment constructor.
*
* @param string $filename
* @param string $contentType
* @param resource $stream
* @param string $contentDisposition
* @param string $contentId
* @param array $headers
* @param string $mimePartStr
*/
public function __construct(
$filename,
$contentType,
$stream,
$contentDisposition = 'attachment',
$contentId = '',
$headers = [],
$mimePartStr = ''
) {
$this->filename = $filename;
$this->contentType = $contentType;
$this->stream = $stream;
$this->content = null;
$this->contentDisposition = $contentDisposition;
$this->contentId = $contentId;
$this->headers = $headers;
$this->mimePartStr = $mimePartStr;
}
/**
* retrieve the attachment filename
*
* @return string
*/
public function getFilename()
{
return $this->filename;
}
/**
* Retrieve the Attachment Content-Type
*
* @return string
*/
public function getContentType()
{
return $this->contentType;
}
/**
* Retrieve the Attachment Content-Disposition
*
* @return string
*/
public function getContentDisposition()
{
return $this->contentDisposition;
}
/**
* Retrieve the Attachment Content-ID
*
* @return string
*/
public function getContentID()
{
return $this->contentId;
}
/**
* Retrieve the Attachment Headers
*
* @return array
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Get a handle to the stream
*
* @return stream
*/
public function getStream()
{
return $this->stream;
}
/**
* Read the contents a few bytes at a time until completed
* Once read to completion, it always returns false
*
* @param int $bytes (default: 2082)
*
* @return string|bool
*/
public function read($bytes = 2082)
{
return feof($this->stream) ? false : fread($this->stream, $bytes);
}
/**
* Retrieve the file content in one go
* Once you retrieve the content you cannot use MimeMailParser_attachment::read()
*
* @return string
*/
public function getContent()
{
if ($this->content === null) {
fseek($this->stream, 0);
while (($buf = $this->read()) !== false) {
$this->content .= $buf;
}
}
return $this->content;
}
/**
* Get mime part string for this attachment
*
* @return string
*/
public function getMimePartStr()
{
return $this->mimePartStr;
}
}

View File

@ -0,0 +1,338 @@
<?php namespace PhpMimeMailParser;
use PhpMimeMailParser\Contracts\CharsetManager;
class Charset implements CharsetManager
{
/**
* Charset Aliases
*/
private $charsetAlias = [
'ascii' => 'us-ascii',
'us-ascii' => 'us-ascii',
'ansi_x3.4-1968' => 'us-ascii',
'646' => 'us-ascii',
'iso-8859-1' => 'ISO-8859-1',
'iso-8859-2' => 'ISO-8859-2',
'iso-8859-3' => 'ISO-8859-3',
'iso-8859-4' => 'ISO-8859-4',
'iso-8859-5' => 'ISO-8859-5',
'iso-8859-6' => 'ISO-8859-6',
'iso-8859-6-i' => 'ISO-8859-6-I',
'iso-8859-6-e' => 'ISO-8859-6-E',
'iso-8859-7' => 'ISO-8859-7',
'iso-8859-8' => 'ISO-8859-8',
'iso-8859-8-i' => 'ISO-8859-8',
'iso-8859-8-e' => 'ISO-8859-8-E',
'iso-8859-9' => 'ISO-8859-9',
'iso-8859-10' => 'ISO-8859-10',
'iso-8859-11' => 'ISO-8859-11',
'iso-8859-13' => 'ISO-8859-13',
'iso-8859-14' => 'ISO-8859-14',
'iso-8859-15' => 'ISO-8859-15',
'iso-8859-16' => 'ISO-8859-16',
'iso-ir-111' => 'ISO-IR-111',
'iso-2022-cn' => 'ISO-2022-CN',
'iso-2022-cn-ext' => 'ISO-2022-CN',
'iso-2022-kr' => 'ISO-2022-KR',
'iso-2022-jp' => 'ISO-2022-JP',
'utf-16be' => 'UTF-16BE',
'utf-16le' => 'UTF-16LE',
'utf-16' => 'UTF-16',
'windows-1250' => 'windows-1250',
'windows-1251' => 'windows-1251',
'windows-1252' => 'windows-1252',
'windows-1253' => 'windows-1253',
'windows-1254' => 'windows-1254',
'windows-1255' => 'windows-1255',
'windows-1256' => 'windows-1256',
'windows-1257' => 'windows-1257',
'windows-1258' => 'windows-1258',
'ibm866' => 'IBM866',
'ibm850' => 'IBM850',
'ibm852' => 'IBM852',
'ibm855' => 'IBM855',
'ibm857' => 'IBM857',
'ibm862' => 'IBM862',
'ibm864' => 'IBM864',
'utf-8' => 'UTF-8',
'utf-7' => 'UTF-7',
'shift_jis' => 'Shift_JIS',
'big5' => 'Big5',
'euc-jp' => 'EUC-JP',
'euc-kr' => 'EUC-KR',
'gb2312' => 'GB2312',
'gb18030' => 'gb18030',
'viscii' => 'VISCII',
'koi8-r' => 'KOI8-R',
'koi8_r' => 'KOI8-R',
'cskoi8r' => 'KOI8-R',
'koi' => 'KOI8-R',
'koi8' => 'KOI8-R',
'koi8-u' => 'KOI8-U',
'tis-620' => 'TIS-620',
't.61-8bit' => 'T.61-8bit',
'hz-gb-2312' => 'HZ-GB-2312',
'big5-hkscs' => 'Big5-HKSCS',
'gbk' => 'gbk',
'cns11643' => 'x-euc-tw',
'x-imap4-modified-utf7' => 'x-imap4-modified-utf7',
'x-euc-tw' => 'x-euc-tw',
'x-mac-ce' => 'x-mac-ce',
'x-mac-turkish' => 'x-mac-turkish',
'x-mac-greek' => 'x-mac-greek',
'x-mac-icelandic' => 'x-mac-icelandic',
'x-mac-croatian' => 'x-mac-croatian',
'x-mac-romanian' => 'x-mac-romanian',
'x-mac-cyrillic' => 'x-mac-cyrillic',
'x-mac-ukrainian' => 'x-mac-cyrillic',
'x-mac-hebrew' => 'x-mac-hebrew',
'x-mac-arabic' => 'x-mac-arabic',
'x-mac-farsi' => 'x-mac-farsi',
'x-mac-devanagari' => 'x-mac-devanagari',
'x-mac-gujarati' => 'x-mac-gujarati',
'x-mac-gurmukhi' => 'x-mac-gurmukhi',
'armscii-8' => 'armscii-8',
'x-viet-tcvn5712' => 'x-viet-tcvn5712',
'x-viet-vps' => 'x-viet-vps',
'iso-10646-ucs-2' => 'UTF-16BE',
'x-iso-10646-ucs-2-be' => 'UTF-16BE',
'x-iso-10646-ucs-2-le' => 'UTF-16LE',
'x-user-defined' => 'x-user-defined',
'x-johab' => 'x-johab',
'latin1' => 'ISO-8859-1',
'iso_8859-1' => 'ISO-8859-1',
'iso8859-1' => 'ISO-8859-1',
'iso8859-2' => 'ISO-8859-2',
'iso8859-3' => 'ISO-8859-3',
'iso8859-4' => 'ISO-8859-4',
'iso8859-5' => 'ISO-8859-5',
'iso8859-6' => 'ISO-8859-6',
'iso8859-7' => 'ISO-8859-7',
'iso8859-8' => 'ISO-8859-8',
'iso8859-9' => 'ISO-8859-9',
'iso8859-10' => 'ISO-8859-10',
'iso8859-11' => 'ISO-8859-11',
'iso8859-13' => 'ISO-8859-13',
'iso8859-14' => 'ISO-8859-14',
'iso8859-15' => 'ISO-8859-15',
'iso_8859-1:1987' => 'ISO-8859-1',
'iso-ir-100' => 'ISO-8859-1',
'l1' => 'ISO-8859-1',
'ibm819' => 'ISO-8859-1',
'cp819' => 'ISO-8859-1',
'csisolatin1' => 'ISO-8859-1',
'latin2' => 'ISO-8859-2',
'iso_8859-2' => 'ISO-8859-2',
'iso_8859-2:1987' => 'ISO-8859-2',
'iso-ir-101' => 'ISO-8859-2',
'l2' => 'ISO-8859-2',
'csisolatin2' => 'ISO-8859-2',
'latin3' => 'ISO-8859-3',
'iso_8859-3' => 'ISO-8859-3',
'iso_8859-3:1988' => 'ISO-8859-3',
'iso-ir-109' => 'ISO-8859-3',
'l3' => 'ISO-8859-3',
'csisolatin3' => 'ISO-8859-3',
'latin4' => 'ISO-8859-4',
'iso_8859-4' => 'ISO-8859-4',
'iso_8859-4:1988' => 'ISO-8859-4',
'iso-ir-110' => 'ISO-8859-4',
'l4' => 'ISO-8859-4',
'csisolatin4' => 'ISO-8859-4',
'cyrillic' => 'ISO-8859-5',
'iso_8859-5' => 'ISO-8859-5',
'iso_8859-5:1988' => 'ISO-8859-5',
'iso-ir-144' => 'ISO-8859-5',
'csisolatincyrillic' => 'ISO-8859-5',
'arabic' => 'ISO-8859-6',
'iso_8859-6' => 'ISO-8859-6',
'iso_8859-6:1987' => 'ISO-8859-6',
'iso-ir-127' => 'ISO-8859-6',
'ecma-114' => 'ISO-8859-6',
'asmo-708' => 'ISO-8859-6',
'csisolatinarabic' => 'ISO-8859-6',
'csiso88596i' => 'ISO-8859-6-I',
'csiso88596e' => 'ISO-8859-6-E',
'greek' => 'ISO-8859-7',
'greek8' => 'ISO-8859-7',
'sun_eu_greek' => 'ISO-8859-7',
'iso_8859-7' => 'ISO-8859-7',
'iso_8859-7:1987' => 'ISO-8859-7',
'iso-ir-126' => 'ISO-8859-7',
'elot_928' => 'ISO-8859-7',
'ecma-118' => 'ISO-8859-7',
'csisolatingreek' => 'ISO-8859-7',
'hebrew' => 'ISO-8859-8',
'iso_8859-8' => 'ISO-8859-8',
'visual' => 'ISO-8859-8',
'iso_8859-8:1988' => 'ISO-8859-8',
'iso-ir-138' => 'ISO-8859-8',
'csisolatinhebrew' => 'ISO-8859-8',
'csiso88598i' => 'ISO-8859-8',
'iso-8859-8i' => 'ISO-8859-8',
'logical' => 'ISO-8859-8',
'csiso88598e' => 'ISO-8859-8-E',
'latin5' => 'ISO-8859-9',
'iso_8859-9' => 'ISO-8859-9',
'iso_8859-9:1989' => 'ISO-8859-9',
'iso-ir-148' => 'ISO-8859-9',
'l5' => 'ISO-8859-9',
'csisolatin5' => 'ISO-8859-9',
'unicode-1-1-utf-8' => 'UTF-8',
'utf8' => 'UTF-8',
'x-sjis' => 'Shift_JIS',
'shift-jis' => 'Shift_JIS',
'ms_kanji' => 'Shift_JIS',
'csshiftjis' => 'Shift_JIS',
'windows-31j' => 'Shift_JIS',
'cp932' => 'Shift_JIS',
'sjis' => 'Shift_JIS',
'cseucpkdfmtjapanese' => 'EUC-JP',
'x-euc-jp' => 'EUC-JP',
'csiso2022jp' => 'ISO-2022-JP',
'iso-2022-jp-2' => 'ISO-2022-JP',
'csiso2022jp2' => 'ISO-2022-JP',
'csbig5' => 'Big5',
'cn-big5' => 'Big5',
'x-x-big5' => 'Big5',
'zh_tw-big5' => 'Big5',
'cseuckr' => 'EUC-KR',
'ks_c_5601-1987' => 'EUC-KR',
'iso-ir-149' => 'EUC-KR',
'ks_c_5601-1989' => 'EUC-KR',
'ksc_5601' => 'EUC-KR',
'ksc5601' => 'EUC-KR',
'korean' => 'EUC-KR',
'csksc56011987' => 'EUC-KR',
'5601' => 'EUC-KR',
'windows-949' => 'EUC-KR',
'gb_2312-80' => 'GB2312',
'iso-ir-58' => 'GB2312',
'chinese' => 'GB2312',
'csiso58gb231280' => 'GB2312',
'csgb2312' => 'GB2312',
'zh_cn.euc' => 'GB2312',
'gb_2312' => 'GB2312',
'x-cp1250' => 'windows-1250',
'x-cp1251' => 'windows-1251',
'x-cp1252' => 'windows-1252',
'x-cp1253' => 'windows-1253',
'x-cp1254' => 'windows-1254',
'x-cp1255' => 'windows-1255',
'x-cp1256' => 'windows-1256',
'x-cp1257' => 'windows-1257',
'x-cp1258' => 'windows-1258',
'windows-874' => 'windows-874',
'ibm874' => 'windows-874',
'dos-874' => 'windows-874',
'macintosh' => 'macintosh',
'x-mac-roman' => 'macintosh',
'mac' => 'macintosh',
'csmacintosh' => 'macintosh',
'cp866' => 'IBM866',
'cp-866' => 'IBM866',
'866' => 'IBM866',
'csibm866' => 'IBM866',
'cp850' => 'IBM850',
'850' => 'IBM850',
'csibm850' => 'IBM850',
'cp852' => 'IBM852',
'852' => 'IBM852',
'csibm852' => 'IBM852',
'cp855' => 'IBM855',
'855' => 'IBM855',
'csibm855' => 'IBM855',
'cp857' => 'IBM857',
'857' => 'IBM857',
'csibm857' => 'IBM857',
'cp862' => 'IBM862',
'862' => 'IBM862',
'csibm862' => 'IBM862',
'cp864' => 'IBM864',
'864' => 'IBM864',
'csibm864' => 'IBM864',
'ibm-864' => 'IBM864',
't.61' => 'T.61-8bit',
'iso-ir-103' => 'T.61-8bit',
'csiso103t618bit' => 'T.61-8bit',
'x-unicode-2-0-utf-7' => 'UTF-7',
'unicode-2-0-utf-7' => 'UTF-7',
'unicode-1-1-utf-7' => 'UTF-7',
'csunicode11utf7' => 'UTF-7',
'csunicode' => 'UTF-16BE',
'csunicode11' => 'UTF-16BE',
'iso-10646-ucs-basic' => 'UTF-16BE',
'csunicodeascii' => 'UTF-16BE',
'iso-10646-unicode-latin1' => 'UTF-16BE',
'csunicodelatin1' => 'UTF-16BE',
'iso-10646' => 'UTF-16BE',
'iso-10646-j-1' => 'UTF-16BE',
'latin6' => 'ISO-8859-10',
'iso-ir-157' => 'ISO-8859-10',
'l6' => 'ISO-8859-10',
'csisolatin6' => 'ISO-8859-10',
'iso_8859-15' => 'ISO-8859-15',
'csisolatin9' => 'ISO-8859-15',
'l9' => 'ISO-8859-15',
'ecma-cyrillic' => 'ISO-IR-111',
'csiso111ecmacyrillic' => 'ISO-IR-111',
'csiso2022kr' => 'ISO-2022-KR',
'csviscii' => 'VISCII',
'zh_tw-euc' => 'x-euc-tw',
'iso88591' => 'ISO-8859-1',
'iso88592' => 'ISO-8859-2',
'iso88593' => 'ISO-8859-3',
'iso88594' => 'ISO-8859-4',
'iso88595' => 'ISO-8859-5',
'iso88596' => 'ISO-8859-6',
'iso88597' => 'ISO-8859-7',
'iso88598' => 'ISO-8859-8',
'iso88599' => 'ISO-8859-9',
'iso885910' => 'ISO-8859-10',
'iso885911' => 'ISO-8859-11',
'iso885912' => 'ISO-8859-12',
'iso885913' => 'ISO-8859-13',
'iso885914' => 'ISO-8859-14',
'iso885915' => 'ISO-8859-15',
'tis620' => 'TIS-620',
'cp1250' => 'windows-1250',
'cp1251' => 'windows-1251',
'cp1252' => 'windows-1252',
'cp1253' => 'windows-1253',
'cp1254' => 'windows-1254',
'cp1255' => 'windows-1255',
'cp1256' => 'windows-1256',
'cp1257' => 'windows-1257',
'cp1258' => 'windows-1258',
'x-gbk' => 'gbk',
'windows-936' => 'gbk',
'ansi-1251' => 'windows-1251',
];
/**
* {@inheritdoc}
*/
public function decodeCharset($encodedString, $charset)
{
if (strtolower($charset) == 'utf-8' || strtolower($charset) == 'us-ascii') {
return $encodedString;
} else {
return iconv($this->getCharsetAlias($charset), 'UTF-8//TRANSLIT//IGNORE', $encodedString);
}
}
/**
* {@inheritdoc}
*/
public function getCharsetAlias($charset)
{
$charset = strtolower($charset);
if (array_key_exists($charset, $this->charsetAlias)) {
return $this->charsetAlias[$charset];
} else {
return null;
}
}
}

View File

@ -0,0 +1,24 @@
<?php namespace PhpMimeMailParser\Contracts;
interface CharsetManager
{
/**
* Decode the string from Charset
*
* @param string $encodedString The string in its original encoded state
* @param string $charset The Charset header of the part.
*
* @return string The decoded string
*/
public function decodeCharset($encodedString, $charset);
/**
* Get charset alias
*
* @param string $charset .
*
* @return string The charset alias
*/
public function getCharsetAlias($charset);
}

View File

@ -0,0 +1,8 @@
<?php
namespace PhpMimeMailParser;
class Exception extends \RuntimeException
{
}

View File

@ -0,0 +1,893 @@
<?php
namespace PhpMimeMailParser;
use PhpMimeMailParser\Contracts\CharsetManager;
/**
* Parser of php-mime-mail-parser
*
* Fully Tested Mailparse Extension Wrapper for PHP 5.4+
*
*/
class Parser
{
/**
* Attachment filename argument option for ->saveAttachments().
*/
const ATTACHMENT_DUPLICATE_THROW = 'DuplicateThrow';
const ATTACHMENT_DUPLICATE_SUFFIX = 'DuplicateSuffix';
const ATTACHMENT_RANDOM_FILENAME = 'RandomFilename';
/**
* PHP MimeParser Resource ID
*
* @var resource $resource
*/
protected $resource;
/**
* A file pointer to email
*
* @var resource $stream
*/
protected $stream;
/**
* A text of an email
*
* @var string $data
*/
protected $data;
/**
* Parts of an email
*
* @var array $parts
*/
protected $parts;
/**
* @var CharsetManager object
*/
protected $charset;
/**
* Parser constructor.
*
* @param CharsetManager|null $charset
*/
public function __construct(CharsetManager $charset = null)
{
if ($charset == null) {
$charset = new Charset();
}
$this->charset = $charset;
}
/**
* Free the held resources
*
* @return void
*/
public function __destruct()
{
// clear the email file resource
if (is_resource($this->stream)) {
fclose($this->stream);
}
// clear the MailParse resource
if (is_resource($this->resource)) {
mailparse_msg_free($this->resource);
}
}
/**
* Set the file path we use to get the email text
*
* @param string $path File path to the MIME mail
*
* @return Parser MimeMailParser Instance
*/
public function setPath($path)
{
// should parse message incrementally from file
$this->resource = mailparse_msg_parse_file($path);
$this->stream = fopen($path, 'r');
$this->parse();
return $this;
}
/**
* Set the Stream resource we use to get the email text
*
* @param resource $stream
*
* @return Parser MimeMailParser Instance
* @throws Exception
*/
public function setStream($stream)
{
// streams have to be cached to file first
$meta = @stream_get_meta_data($stream);
if (!$meta || !$meta['mode'] || $meta['mode'][0] != 'r' || $meta['eof']) {
throw new Exception(
'setStream() expects parameter stream to be readable stream resource.'
);
}
/** @var resource $tmp_fp */
$tmp_fp = tmpfile();
if ($tmp_fp) {
while (!feof($stream)) {
fwrite($tmp_fp, fread($stream, 2028));
}
fseek($tmp_fp, 0);
$this->stream = &$tmp_fp;
} else {
throw new Exception(
'Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.'
);
}
fclose($stream);
$this->resource = mailparse_msg_create();
// parses the message incrementally (low memory usage but slower)
while (!feof($this->stream)) {
mailparse_msg_parse($this->resource, fread($this->stream, 2082));
}
$this->parse();
return $this;
}
/**
* Set the email text
*
* @param string $data
*
* @return Parser MimeMailParser Instance
*/
public function setText($data)
{
if (!$data) {
throw new Exception('You must not call MimeMailParser::setText with an empty string parameter');
}
$this->resource = mailparse_msg_create();
// does not parse incrementally, fast memory hog might explode
mailparse_msg_parse($this->resource, $data);
$this->data = $data;
$this->parse();
return $this;
}
/**
* Parse the Message into parts
*
* @return void
*/
protected function parse()
{
$structure = mailparse_msg_get_structure($this->resource);
$this->parts = [];
foreach ($structure as $part_id) {
$part = mailparse_msg_get_part($this->resource, $part_id);
$this->parts[$part_id] = mailparse_msg_get_part_data($part);
}
}
/**
* Retrieve a specific Email Header, without charset conversion.
*
* @param string $name Header name (case-insensitive)
*
* @return string
* @throws Exception
*/
public function getRawHeader($name)
{
$name = strtolower($name);
if (isset($this->parts[1])) {
$headers = $this->getPart('headers', $this->parts[1]);
return (isset($headers[$name])) ? $headers[$name] : false;
} else {
throw new Exception(
'setPath() or setText() or setStream() must be called before retrieving email headers.'
);
}
}
/**
* Retrieve a specific Email Header
*
* @param string $name Header name (case-insensitive)
*
* @return string
*/
public function getHeader($name)
{
$rawHeader = $this->getRawHeader($name);
if ($rawHeader === false) {
return false;
}
return $this->decodeHeader($rawHeader);
}
/**
* Retrieve all mail headers
*
* @return array
* @throws Exception
*/
public function getHeaders()
{
if (isset($this->parts[1])) {
$headers = $this->getPart('headers', $this->parts[1]);
foreach ($headers as $name => &$value) {
if (is_array($value)) {
foreach ($value as &$v) {
$v = $this->decodeSingleHeader($v);
}
} else {
$value = $this->decodeSingleHeader($value);
}
}
return $headers;
} else {
throw new Exception(
'setPath() or setText() or setStream() must be called before retrieving email headers.'
);
}
}
/**
* Retrieve the raw mail headers as a string
*
* @return string
* @throws Exception
*/
public function getHeadersRaw()
{
if (isset($this->parts[1])) {
return $this->getPartHeader($this->parts[1]);
} else {
throw new Exception(
'setPath() or setText() or setStream() must be called before retrieving email headers.'
);
}
}
/**
* Retrieve the raw Header of a MIME part
*
* @return String
* @param $part Object
* @throws Exception
*/
protected function getPartHeader(&$part)
{
$header = '';
if ($this->stream) {
$header = $this->getPartHeaderFromFile($part);
} elseif ($this->data) {
$header = $this->getPartHeaderFromText($part);
}
return $header;
}
/**
* Retrieve the Header from a MIME part from file
*
* @return String Mime Header Part
* @param $part Array
*/
protected function getPartHeaderFromFile(&$part)
{
$start = $part['starting-pos'];
$end = $part['starting-pos-body'];
fseek($this->stream, $start, SEEK_SET);
$header = fread($this->stream, $end-$start);
return $header;
}
/**
* Retrieve the Header from a MIME part from text
*
* @return String Mime Header Part
* @param $part Array
*/
protected function getPartHeaderFromText(&$part)
{
$start = $part['starting-pos'];
$end = $part['starting-pos-body'];
$header = substr($this->data, $start, $end-$start);
return $header;
}
/**
* Checks whether a given part ID is a child of another part
* eg. an RFC822 attachment may have one or more text parts
*
* @param string $partId
* @param string $parentPartId
* @return bool
*/
protected function partIdIsChildOfPart($partId, $parentPartId)
{
return substr($partId, 0, strlen($parentPartId)) == $parentPartId;
}
/**
* Whether the given part ID is a child of any attachment part in the message.
*
* @param string $checkPartId
* @return bool
*/
protected function partIdIsChildOfAnAttachment($checkPartId)
{
foreach ($this->parts as $partId => $part) {
if ($this->getPart('content-disposition', $part) == 'attachment') {
if ($this->partIdIsChildOfPart($checkPartId, $partId)) {
return true;
}
}
}
return false;
}
/**
* Returns the email message body in the specified format
*
* @param string $type text, html or htmlEmbedded
*
* @return false|string Body or False if not found
* @throws Exception
*/
public function getMessageBody($type = 'text')
{
$body = false;
$mime_types = [
'text' => 'text/plain',
'html' => 'text/html',
'htmlEmbedded' => 'text/html',
];
if (in_array($type, array_keys($mime_types))) {
$part_type = $type === 'htmlEmbedded' ? 'html' : $type;
$inline_parts = $this->getInlineParts($part_type);
$body = empty($inline_parts) ? '' : $inline_parts[0];
} else {
throw new Exception(
'Invalid type specified for getMessageBody(). Expected: text, html or htmlEmbeded.'
);
}
if ($type == 'htmlEmbedded') {
$attachments = $this->getAttachments();
foreach ($attachments as $attachment) {
if ($attachment->getContentID() != '') {
$body = str_replace(
'"cid:'.$attachment->getContentID().'"',
'"'.$this->getEmbeddedData($attachment->getContentID()).'"',
$body
);
}
}
}
return $body;
}
/**
* Returns the embedded data structure
*
* @param string $contentId Content-Id
*
* @return string
*/
protected function getEmbeddedData($contentId)
{
foreach ($this->parts as $part) {
if ($this->getPart('content-id', $part) == $contentId) {
$embeddedData = 'data:';
$embeddedData .= $this->getPart('content-type', $part);
$embeddedData .= ';'.$this->getPart('transfer-encoding', $part);
$embeddedData .= ','.$this->getPartBody($part);
return $embeddedData;
}
}
return '';
}
/**
* Return an array with the following keys display, address, is_group
*
* @param string $name Header name (case-insensitive)
*
* @return array
*/
public function getAddresses($name)
{
$value = $this->getHeader($name);
return mailparse_rfc822_parse_addresses($value);
}
/**
* Returns the attachments contents in order of appearance
*
* @return Attachment[]
*/
public function getInlineParts($type = 'text')
{
$inline_parts = [];
$dispositions = ['inline'];
$mime_types = [
'text' => 'text/plain',
'html' => 'text/html',
];
if (!in_array($type, array_keys($mime_types))) {
throw new Exception('Invalid type specified for getInlineParts(). "type" can either be text or html.');
}
foreach ($this->parts as $partId => $part) {
if ($this->getPart('content-type', $part) == $mime_types[$type]
&& $this->getPart('content-disposition', $part) != 'attachment'
&& !$this->partIdIsChildOfAnAttachment($partId)
) {
$headers = $this->getPart('headers', $part);
$encodingType = array_key_exists('content-transfer-encoding', $headers) ?
$headers['content-transfer-encoding'] : '';
if (is_array($encodingType)) {
$encodingType = $encodingType[0];
}
$undecoded_body = $this->decodeContentTransfer($this->getPartBody($part), $encodingType);
$inline_parts[] = $this->charset->decodeCharset($undecoded_body, $this->getPartCharset($part));
}
}
return $inline_parts;
}
/**
* Returns the attachments contents in order of appearance
*
* @return Attachment[]
*/
public function getAttachments($include_inline = true)
{
$attachments = [];
$dispositions = $include_inline ?
['attachment', 'inline'] :
['attachment'];
$non_attachment_types = ['text/plain', 'text/html'];
$nonameIter = 0;
foreach ($this->parts as $part) {
$disposition = $this->getPart('content-disposition', $part);
$filename = 'noname';
if (isset($part['disposition-filename'])) {
$filename = $this->decodeHeader($part['disposition-filename']);
// Escape all potentially unsafe characters from the filename
$filename = preg_replace('((^\.)|\/|(\.$))', '_', $filename);
} elseif (isset($part['content-name'])) {
// if we have no disposition but we have a content-name, it's a valid attachment.
// we simulate the presence of an attachment disposition with a disposition filename
$filename = $this->decodeHeader($part['content-name']);
// Escape all potentially unsafe characters from the filename
$filename = preg_replace('((^\.)|\/|(\.$))', '_', $filename);
$disposition = 'attachment';
} elseif (in_array($part['content-type'], $non_attachment_types, true)
&& $disposition !== 'attachment') {
// it is a message body, no attachment
continue;
} elseif (substr($part['content-type'], 0, 10) !== 'multipart/') {
// if we cannot get it by getMessageBody(), we assume it is an attachment
$disposition = 'attachment';
}
if (in_array($disposition, $dispositions) === true) {
if ($filename == 'noname') {
$nonameIter++;
$filename = 'noname'.$nonameIter;
}
$headersAttachments = $this->getPart('headers', $part);
$contentidAttachments = $this->getPart('content-id', $part);
$mimePartStr = $this->getPartComplete($part);
$attachments[] = new Attachment(
$filename,
$this->getPart('content-type', $part),
$this->getAttachmentStream($part),
$disposition,
$contentidAttachments,
$headersAttachments,
$mimePartStr
);
}
}
return $attachments;
}
/**
* Save attachments in a folder
*
* @param string $attach_dir directory
* @param bool $include_inline
* @param string $filenameStrategy How to generate attachment filenames
*
* @return array Saved attachments paths
* @throws Exception
*/
public function saveAttachments(
$attach_dir,
$include_inline = true,
$filenameStrategy = self::ATTACHMENT_DUPLICATE_SUFFIX
) {
$attachments = $this->getAttachments($include_inline);
if (empty($attachments)) {
return false;
}
if (!is_dir($attach_dir)) {
mkdir($attach_dir);
}
$attachments_paths = [];
foreach ($attachments as $attachment) {
// Determine filename
switch ($filenameStrategy) {
case self::ATTACHMENT_RANDOM_FILENAME:
$attachment_path = tempnam($attach_dir, '');
break;
case self::ATTACHMENT_DUPLICATE_THROW:
case self::ATTACHMENT_DUPLICATE_SUFFIX:
$attachment_path = $attach_dir . $attachment->getFilename();
break;
default:
throw new Exception('Invalid filename strategy argument provided.');
}
// Handle duplicate filename
if (file_exists($attachment_path)) {
switch ($filenameStrategy) {
case self::ATTACHMENT_DUPLICATE_THROW:
throw new Exception('Could not create file for attachment: duplicate filename.');
case self::ATTACHMENT_DUPLICATE_SUFFIX:
$attachment_path = tempnam($attach_dir, $attachment->getFilename());
break;
}
}
/** @var resource $fp */
if ($fp = fopen($attachment_path, 'w')) {
while ($bytes = $attachment->read()) {
fwrite($fp, $bytes);
}
fclose($fp);
$attachments_paths[] = realpath($attachment_path);
} else {
throw new Exception('Could not write attachments. Your directory may be unwritable by PHP.');
}
}
return $attachments_paths;
}
/**
* Read the attachment Body and save temporary file resource
*
* @param array $part
*
* @return resource Mime Body Part
* @throws Exception
*/
protected function getAttachmentStream(&$part)
{
/** @var resource $temp_fp */
$temp_fp = tmpfile();
$headers = $this->getPart('headers', $part);
$encodingType = array_key_exists('content-transfer-encoding', $headers) ?
$headers['content-transfer-encoding'] : '';
if ($temp_fp) {
if ($this->stream) {
$start = $part['starting-pos-body'];
$end = $part['ending-pos-body'];
fseek($this->stream, $start, SEEK_SET);
$len = $end - $start;
$written = 0;
while ($written < $len) {
$write = $len;
$part = fread($this->stream, $write);
fwrite($temp_fp, $this->decodeContentTransfer($part, $encodingType));
$written += $write;
}
} elseif ($this->data) {
$attachment = $this->decodeContentTransfer($this->getPartBodyFromText($part), $encodingType);
fwrite($temp_fp, $attachment, strlen($attachment));
}
fseek($temp_fp, 0, SEEK_SET);
} else {
throw new Exception(
'Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.'
);
}
return $temp_fp;
}
/**
* Decode the string from Content-Transfer-Encoding
*
* @param string $encodedString The string in its original encoded state
* @param string $encodingType The encoding type from the Content-Transfer-Encoding header of the part.
*
* @return string The decoded string
*/
protected function decodeContentTransfer($encodedString, $encodingType)
{
$encodingType = strtolower($encodingType);
if ($encodingType == 'base64') {
return base64_decode($encodedString);
} elseif ($encodingType == 'quoted-printable') {
return quoted_printable_decode($encodedString);
} else {
return $encodedString; //8bit, 7bit, binary
}
}
/**
* $input can be a string or array
*
* @param string|array $input
*
* @return string
*/
protected function decodeHeader($input)
{
//Sometimes we have 2 label From so we take only the first
if (is_array($input)) {
return $this->decodeSingleHeader($input[0]);
}
return $this->decodeSingleHeader($input);
}
/**
* Decodes a single header (= string)
*
* @param string $input
*
* @return string
*/
protected function decodeSingleHeader($input)
{
// For each encoded-word...
while (preg_match('/(=\?([^?]+)\?(q|b)\?([^?]*)\?=)((\s+)=\?)?/i', $input, $matches)) {
$encoded = $matches[1];
$charset = $matches[2];
$encoding = $matches[3];
$text = $matches[4];
$space = isset($matches[6]) ? $matches[6] : '';
switch (strtolower($encoding)) {
case 'b':
$text = $this->decodeContentTransfer($text, 'base64');
break;
case 'q':
$text = str_replace('_', ' ', $text);
preg_match_all('/=([a-f0-9]{2})/i', $text, $matches);
foreach ($matches[1] as $value) {
$text = str_replace('='.$value, chr(hexdec($value)), $text);
}
break;
}
$text = $this->charset->decodeCharset($text, $this->charset->getCharsetAlias($charset));
$input = str_replace($encoded . $space, $text, $input);
}
return $input;
}
/**
* Return the charset of the MIME part
*
* @param array $part
*
* @return string|false
*/
protected function getPartCharset($part)
{
if (isset($part['charset'])) {
return $this->charset->getCharsetAlias($part['charset']);
} else {
return false;
}
}
/**
* Retrieve a specified MIME part
*
* @param string $type
* @param array $parts
*
* @return string|array
*/
protected function getPart($type, $parts)
{
return (isset($parts[$type])) ? $parts[$type] : false;
}
/**
* Retrieve the Body of a MIME part
*
* @param array $part
*
* @return string
*/
protected function getPartBody(&$part)
{
$body = '';
if ($this->stream) {
$body = $this->getPartBodyFromFile($part);
} elseif ($this->data) {
$body = $this->getPartBodyFromText($part);
}
return $body;
}
/**
* Retrieve the Body from a MIME part from file
*
* @param array $part
*
* @return string Mime Body Part
*/
protected function getPartBodyFromFile(&$part)
{
$start = $part['starting-pos-body'];
$end = $part['ending-pos-body'];
$body = '';
if ($end - $start > 0) {
fseek($this->stream, $start, SEEK_SET);
$body = fread($this->stream, $end - $start);
}
return $body;
}
/**
* Retrieve the Body from a MIME part from text
*
* @param array $part
*
* @return string Mime Body Part
*/
protected function getPartBodyFromText(&$part)
{
$start = $part['starting-pos-body'];
$end = $part['ending-pos-body'];
return substr($this->data, $start, $end - $start);
}
/**
* Retrieve the content of a MIME part
*
* @param array $part
*
* @return string
*/
protected function getPartComplete(&$part)
{
$body = '';
if ($this->stream) {
$body = $this->getPartFromFile($part);
} elseif ($this->data) {
$body = $this->getPartFromText($part);
}
return $body;
}
/**
* Retrieve the content from a MIME part from file
*
* @param array $part
*
* @return string Mime Content
*/
protected function getPartFromFile(&$part)
{
$start = $part['starting-pos'];
$end = $part['ending-pos'];
$body = '';
if ($end - $start > 0) {
fseek($this->stream, $start, SEEK_SET);
$body = fread($this->stream, $end - $start);
}
return $body;
}
/**
* Retrieve the content from a MIME part from text
*
* @param array $part
*
* @return string Mime Content
*/
protected function getPartFromText(&$part)
{
$start = $part['starting-pos'];
$end = $part['ending-pos'];
return substr($this->data, $start, $end - $start);
}
/**
* Retrieve the resource
*
* @return resource resource
*/
public function getResource()
{
return $this->resource;
}
/**
* Retrieve the file pointer to email
*
* @return resource stream
*/
public function getStream()
{
return $this->stream;
}
/**
* Retrieve the text of an email
*
* @return string data
*/
public function getData()
{
return $this->data;
}
/**
* Retrieve the parts of an email
*
* @return array parts
*/
public function getParts()
{
return $this->parts;
}
/**
* Retrieve the charset manager object
*
* @return CharsetManager charset
*/
public function getCharset()
{
return $this->charset;
}
}

View File

@ -1 +1 @@
5.2.26 5.2.25

View File

@ -31,7 +31,7 @@ class PHPMailer
* The PHPMailer Version number. * The PHPMailer Version number.
* @var string * @var string
*/ */
public $Version = '5.2.26'; public $Version = '5.2.25';
/** /**
* Email priority. * Email priority.
@ -659,8 +659,6 @@ class PHPMailer
if ($exceptions !== null) { if ($exceptions !== null) {
$this->exceptions = (boolean)$exceptions; $this->exceptions = (boolean)$exceptions;
} }
//Pick an appropriate debug output format automatically
$this->Debugoutput = (strpos(PHP_SAPI, 'cli') !== false ? 'echo' : 'html');
} }
/** /**

View File

@ -34,7 +34,7 @@ class POP3
* @var string * @var string
* @access public * @access public
*/ */
public $Version = '5.2.26'; public $Version = '5.2.25';
/** /**
* Default POP3 port number. * Default POP3 port number.

View File

@ -30,7 +30,7 @@ class SMTP
* The PHPMailer SMTP version number. * The PHPMailer SMTP version number.
* @var string * @var string
*/ */
const VERSION = '5.2.26'; const VERSION = '5.2.25';
/** /**
* SMTP line break constant. * SMTP line break constant.
@ -81,7 +81,7 @@ class SMTP
* @deprecated Use the `VERSION` constant instead * @deprecated Use the `VERSION` constant instead
* @see SMTP::VERSION * @see SMTP::VERSION
*/ */
public $Version = '5.2.26'; public $Version = '5.2.25';
/** /**
* SMTP server port number. * SMTP server port number.

View File

@ -184,6 +184,3 @@ FakesAssemblies/
# Composer # Composer
/vendor /vendor
# .vs
.vs/

View File

@ -1,18 +1,11 @@
language: php language: php
dist: trusty
matrix:
include:
- php: 5.3
dist: precise
php: php:
- 5.3
- 5.4 - 5.4
- 5.5 - 5.5
- 5.6 - 5.6
- 7.0 - 7
- 7.1
- hhvm - hhvm
script: script: phpunit --coverage-text tests
- if [[ "$TRAVIS_PHP_VERSION" == '5.6' ]]; then phpunit --coverage-text tests ; fi

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ PHP library for [two-factor (or multi-factor) authentication](http://en.wikipedi
## Requirements ## Requirements
* Tested on PHP 5.3, 5.4, 5.5 and 5.6, 7.0, 7.1 and HHVM * Tested on PHP 5.3, 5.4, 5.5 and 5.6, 7 and HHVM
* [cURL](http://php.net/manual/en/book.curl.php) when using the provided `GoogleQRCodeProvider` (default), `QRServerProvider` or `QRicketProvider` but you can also provide your own QR-code provider. * [cURL](http://php.net/manual/en/book.curl.php) when using the provided `GoogleQRCodeProvider` (default), `QRServerProvider` or `QRicketProvider` but you can also provide your own QR-code provider.
* [random_bytes()](http://php.net/manual/en/function.random-bytes.php), [MCrypt](http://php.net/manual/en/book.mcrypt.php), [OpenSSL](http://php.net/manual/en/book.openssl.php) or [Hash](http://php.net/manual/en/book.hash.php) depending on which built-in RNG you use (TwoFactorAuth will try to 'autodetect' and use the best available); however: feel free to provide your own (CS)RNG. * [random_bytes()](http://php.net/manual/en/function.random-bytes.php), [MCrypt](http://php.net/manual/en/book.mcrypt.php), [OpenSSL](http://php.net/manual/en/book.openssl.php) or [Hash](http://php.net/manual/en/book.hash.php) depending on which built-in RNG you use (TwoFactorAuth will try to 'autodetect' and use the best available); however: feel free to provide your own (CS)RNG.

View File

@ -1,7 +1,7 @@
{ {
"name": "robthree/twofactorauth", "name": "robthree/twofactorauth",
"description": "Two Factor Authentication", "description": "Two Factor Authentication",
"version": "1.6.1", "version": "1.6",
"type": "library", "type": "library",
"keywords": [ "Authentication", "Two Factor Authentication", "Multi Factor Authentication", "TFA", "MFA", "PHP", "Authenticator", "Authy" ], "keywords": [ "Authentication", "Two Factor Authentication", "Multi Factor Authentication", "TFA", "MFA", "PHP", "Authenticator", "Authy" ],
"homepage": "https://github.com/RobThree/TwoFactorAuth", "homepage": "https://github.com/RobThree/TwoFactorAuth",

View File

@ -6,7 +6,7 @@ class ConvertUnixTimeDotComTimeProvider implements ITimeProvider
{ {
public function getTime() { public function getTime() {
$json = @json_decode( $json = @json_decode(
@file_get_contents('http://www.convert-unix-time.com/api?timestamp=now&r=' . uniqid(null, true)) @file_get_contents('http://www.convert-unix-time.com/api?timestamp=now')
); );
if ($json === null || !is_int($json->timestamp)) if ($json === null || !is_int($json->timestamp))
throw new \TimeException('Unable to retrieve time from convert-unix-time.com'); throw new \TimeException('Unable to retrieve time from convert-unix-time.com');

View File

@ -26,8 +26,7 @@ class HttpTimeProvider implements ITimeProvider
'request_fulluri' => true, 'request_fulluri' => true,
'header' => array( 'header' => array(
'Connection: close', 'Connection: close',
'User-agent: TwoFactorAuth HttpTimeProvider (https://github.com/RobThree/TwoFactorAuth)', 'User-agent: TwoFactorAuth HttpTimeProvider (https://github.com/RobThree/TwoFactorAuth)'
'Cache-Control: no-cache'
) )
) )
); );

View File

@ -1,507 +0,0 @@
<?php
/* Copyright (c) 2014 Yubico AB
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace u2flib_server;
/** Constant for the version of the u2f protocol */
const U2F_VERSION = "U2F_V2";
/** Error for the authentication message not matching any outstanding
* authentication request */
const ERR_NO_MATCHING_REQUEST = 1;
/** Error for the authentication message not matching any registration */
const ERR_NO_MATCHING_REGISTRATION = 2;
/** Error for the signature on the authentication message not verifying with
* the correct key */
const ERR_AUTHENTICATION_FAILURE = 3;
/** Error for the challenge in the registration message not matching the
* registration challenge */
const ERR_UNMATCHED_CHALLENGE = 4;
/** Error for the attestation signature on the registration message not
* verifying */
const ERR_ATTESTATION_SIGNATURE = 5;
/** Error for the attestation verification not verifying */
const ERR_ATTESTATION_VERIFICATION = 6;
/** Error for not getting good random from the system */
const ERR_BAD_RANDOM = 7;
/** Error when the counter is lower than expected */
const ERR_COUNTER_TOO_LOW = 8;
/** Error decoding public key */
const ERR_PUBKEY_DECODE = 9;
/** Error user-agent returned error */
const ERR_BAD_UA_RETURNING = 10;
/** Error old OpenSSL version */
const ERR_OLD_OPENSSL = 11;
/** @internal */
const PUBKEY_LEN = 65;
class U2F
{
/** @var string */
private $appId;
/** @var null|string */
private $attestDir;
/** @internal */
private $FIXCERTS = array(
'349bca1031f8c82c4ceca38b9cebf1a69df9fb3b94eed99eb3fb9aa3822d26e8',
'dd574527df608e47ae45fbba75a2afdd5c20fd94a02419381813cd55a2a3398f',
'1d8764f0f7cd1352df6150045c8f638e517270e8b5dda1c63ade9c2280240cae',
'd0edc9a91a1677435a953390865d208c55b3183c6759c9b5a7ff494c322558eb',
'6073c436dcd064a48127ddbf6032ac1a66fd59a0c24434f070d4e564c124c897',
'ca993121846c464d666096d35f13bf44c1b05af205f9b4a1e00cf6cc10c5e511'
);
/**
* @param string $appId Application id for the running application
* @param string|null $attestDir Directory where trusted attestation roots may be found
* @throws Error If OpenSSL older than 1.0.0 is used
*/
public function __construct($appId, $attestDir = null)
{
if(OPENSSL_VERSION_NUMBER < 0x10000000) {
throw new Error('OpenSSL has to be at least version 1.0.0, this is ' . OPENSSL_VERSION_TEXT, ERR_OLD_OPENSSL);
}
$this->appId = $appId;
$this->attestDir = $attestDir;
}
/**
* Called to get a registration request to send to a user.
* Returns an array of one registration request and a array of sign requests.
*
* @param array $registrations List of current registrations for this
* user, to prevent the user from registering the same authenticator several
* times.
* @return array An array of two elements, the first containing a
* RegisterRequest the second being an array of SignRequest
* @throws Error
*/
public function getRegisterData(array $registrations = array())
{
$challenge = $this->createChallenge();
$request = new RegisterRequest($challenge, $this->appId);
$signs = $this->getAuthenticateData($registrations);
return array($request, $signs);
}
/**
* Called to verify and unpack a registration message.
*
* @param RegisterRequest $request this is a reply to
* @param object $response response from a user
* @param bool $includeCert set to true if the attestation certificate should be
* included in the returned Registration object
* @return Registration
* @throws Error
*/
public function doRegister($request, $response, $includeCert = true)
{
if( !is_object( $request ) ) {
throw new \InvalidArgumentException('$request of doRegister() method only accepts object.');
}
if( !is_object( $response ) ) {
throw new \InvalidArgumentException('$response of doRegister() method only accepts object.');
}
if( property_exists( $response, 'errorCode') && $response->errorCode !== 0 ) {
throw new Error('User-agent returned error. Error code: ' . $response->errorCode, ERR_BAD_UA_RETURNING );
}
if( !is_bool( $includeCert ) ) {
throw new \InvalidArgumentException('$include_cert of doRegister() method only accepts boolean.');
}
$rawReg = $this->base64u_decode($response->registrationData);
$regData = array_values(unpack('C*', $rawReg));
$clientData = $this->base64u_decode($response->clientData);
$cli = json_decode($clientData);
if($cli->challenge !== $request->challenge) {
throw new Error('Registration challenge does not match', ERR_UNMATCHED_CHALLENGE );
}
$registration = new Registration();
$offs = 1;
$pubKey = substr($rawReg, $offs, PUBKEY_LEN);
$offs += PUBKEY_LEN;
// decode the pubKey to make sure it's good
$tmpKey = $this->pubkey_to_pem($pubKey);
if($tmpKey === null) {
throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE );
}
$registration->publicKey = base64_encode($pubKey);
$khLen = $regData[$offs++];
$kh = substr($rawReg, $offs, $khLen);
$offs += $khLen;
$registration->keyHandle = $this->base64u_encode($kh);
// length of certificate is stored in byte 3 and 4 (excluding the first 4 bytes)
$certLen = 4;
$certLen += ($regData[$offs + 2] << 8);
$certLen += $regData[$offs + 3];
$rawCert = $this->fixSignatureUnusedBits(substr($rawReg, $offs, $certLen));
$offs += $certLen;
$pemCert = "-----BEGIN CERTIFICATE-----\r\n";
$pemCert .= chunk_split(base64_encode($rawCert), 64);
$pemCert .= "-----END CERTIFICATE-----";
if($includeCert) {
$registration->certificate = base64_encode($rawCert);
}
if($this->attestDir) {
if(openssl_x509_checkpurpose($pemCert, -1, $this->get_certs()) !== true) {
throw new Error('Attestation certificate can not be validated', ERR_ATTESTATION_VERIFICATION );
}
}
if(!openssl_pkey_get_public($pemCert)) {
throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE );
}
$signature = substr($rawReg, $offs);
$dataToVerify = chr(0);
$dataToVerify .= hash('sha256', $request->appId, true);
$dataToVerify .= hash('sha256', $clientData, true);
$dataToVerify .= $kh;
$dataToVerify .= $pubKey;
if(openssl_verify($dataToVerify, $signature, $pemCert, 'sha256') === 1) {
return $registration;
} else {
throw new Error('Attestation signature does not match', ERR_ATTESTATION_SIGNATURE );
}
}
/**
* Called to get an authentication request.
*
* @param array $registrations An array of the registrations to create authentication requests for.
* @return array An array of SignRequest
* @throws Error
*/
public function getAuthenticateData(array $registrations)
{
$sigs = array();
$challenge = $this->createChallenge();
foreach ($registrations as $reg) {
if( !is_object( $reg ) ) {
throw new \InvalidArgumentException('$registrations of getAuthenticateData() method only accepts array of object.');
}
$sig = new SignRequest();
$sig->appId = $this->appId;
$sig->keyHandle = $reg->keyHandle;
$sig->challenge = $challenge;
$sigs[] = $sig;
}
return $sigs;
}
/**
* Called to verify an authentication response
*
* @param array $requests An array of outstanding authentication requests
* @param array $registrations An array of current registrations
* @param object $response A response from the authenticator
* @return Registration
* @throws Error
*
* The Registration object returned on success contains an updated counter
* that should be saved for future authentications.
* If the Error returned is ERR_COUNTER_TOO_LOW this is an indication of
* token cloning or similar and appropriate action should be taken.
*/
public function doAuthenticate(array $requests, array $registrations, $response)
{
if( !is_object( $response ) ) {
throw new \InvalidArgumentException('$response of doAuthenticate() method only accepts object.');
}
if( property_exists( $response, 'errorCode') && $response->errorCode !== 0 ) {
throw new Error('User-agent returned error. Error code: ' . $response->errorCode, ERR_BAD_UA_RETURNING );
}
/** @var object|null $req */
$req = null;
/** @var object|null $reg */
$reg = null;
$clientData = $this->base64u_decode($response->clientData);
$decodedClient = json_decode($clientData);
foreach ($requests as $req) {
if( !is_object( $req ) ) {
throw new \InvalidArgumentException('$requests of doAuthenticate() method only accepts array of object.');
}
if($req->keyHandle === $response->keyHandle && $req->challenge === $decodedClient->challenge) {
break;
}
$req = null;
}
if($req === null) {
throw new Error('No matching request found', ERR_NO_MATCHING_REQUEST );
}
foreach ($registrations as $reg) {
if( !is_object( $reg ) ) {
throw new \InvalidArgumentException('$registrations of doAuthenticate() method only accepts array of object.');
}
if($reg->keyHandle === $response->keyHandle) {
break;
}
$reg = null;
}
if($reg === null) {
throw new Error('No matching registration found', ERR_NO_MATCHING_REGISTRATION );
}
$pemKey = $this->pubkey_to_pem($this->base64u_decode($reg->publicKey));
if($pemKey === null) {
throw new Error('Decoding of public key failed', ERR_PUBKEY_DECODE );
}
$signData = $this->base64u_decode($response->signatureData);
$dataToVerify = hash('sha256', $req->appId, true);
$dataToVerify .= substr($signData, 0, 5);
$dataToVerify .= hash('sha256', $clientData, true);
$signature = substr($signData, 5);
if(openssl_verify($dataToVerify, $signature, $pemKey, 'sha256') === 1) {
$ctr = unpack("Nctr", substr($signData, 1, 4));
$counter = $ctr['ctr'];
/* TODO: wrap-around should be handled somehow.. */
if($counter > $reg->counter) {
$reg->counter = $counter;
return $reg;
} else {
throw new Error('Counter too low.', ERR_COUNTER_TOO_LOW );
}
} else {
throw new Error('Authentication failed', ERR_AUTHENTICATION_FAILURE );
}
}
/**
* @return array
*/
private function get_certs()
{
$files = array();
$dir = $this->attestDir;
if($dir && $handle = opendir($dir)) {
while(false !== ($entry = readdir($handle))) {
if(is_file("$dir/$entry")) {
$files[] = "$dir/$entry";
}
}
closedir($handle);
}
return $files;
}
/**
* @param string $data
* @return string
*/
private function base64u_encode($data)
{
return trim(strtr(base64_encode($data), '+/', '-_'), '=');
}
/**
* @param string $data
* @return string
*/
private function base64u_decode($data)
{
return base64_decode(strtr($data, '-_', '+/'));
}
/**
* @param string $key
* @return null|string
*/
private function pubkey_to_pem($key)
{
if(strlen($key) !== PUBKEY_LEN || $key[0] !== "\x04") {
return null;
}
/*
* Convert the public key to binary DER format first
* Using the ECC SubjectPublicKeyInfo OIDs from RFC 5480
*
* SEQUENCE(2 elem) 30 59
* SEQUENCE(2 elem) 30 13
* OID1.2.840.10045.2.1 (id-ecPublicKey) 06 07 2a 86 48 ce 3d 02 01
* OID1.2.840.10045.3.1.7 (secp256r1) 06 08 2a 86 48 ce 3d 03 01 07
* BIT STRING(520 bit) 03 42 ..key..
*/
$der = "\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01";
$der .= "\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07\x03\x42";
$der .= "\0".$key;
$pem = "-----BEGIN PUBLIC KEY-----\r\n";
$pem .= chunk_split(base64_encode($der), 64);
$pem .= "-----END PUBLIC KEY-----";
return $pem;
}
/**
* @return string
* @throws Error
*/
private function createChallenge()
{
$challenge = openssl_random_pseudo_bytes(32, $crypto_strong );
if( $crypto_strong !== true ) {
throw new Error('Unable to obtain a good source of randomness', ERR_BAD_RANDOM);
}
$challenge = $this->base64u_encode( $challenge );
return $challenge;
}
/**
* Fixes a certificate where the signature contains unused bits.
*
* @param string $cert
* @return mixed
*/
private function fixSignatureUnusedBits($cert)
{
if(in_array(hash('sha256', $cert), $this->FIXCERTS)) {
$cert[strlen($cert) - 257] = "\0";
}
return $cert;
}
}
/**
* Class for building a registration request
*
* @package u2flib_server
*/
class RegisterRequest
{
/** Protocol version */
public $version = U2F_VERSION;
/** Registration challenge */
public $challenge;
/** Application id */
public $appId;
/**
* @param string $challenge
* @param string $appId
* @internal
*/
public function __construct($challenge, $appId)
{
$this->challenge = $challenge;
$this->appId = $appId;
}
}
/**
* Class for building up an authentication request
*
* @package u2flib_server
*/
class SignRequest
{
/** Protocol version */
public $version = U2F_VERSION;
/** Authentication challenge */
public $challenge;
/** Key handle of a registered authenticator */
public $keyHandle;
/** Application id */
public $appId;
}
/**
* Class returned for successful registrations
*
* @package u2flib_server
*/
class Registration
{
/** The key handle of the registered authenticator */
public $keyHandle;
/** The public key of the registered authenticator */
public $publicKey;
/** The attestation certificate of the registered authenticator */
public $certificate;
/** The counter associated with this registration */
public $counter = -1;
}
/**
* Error class, returned on errors
*
* @package u2flib_server
*/
class Error extends \Exception
{
/**
* Override constructor and make message and code mandatory
* @param string $message
* @param int $code
* @param \Exception|null $previous
*/
public function __construct($message, $code, \Exception $previous = null) {
parent::__construct($message, $code, $previous);
}
}

View File

@ -6,8 +6,6 @@ if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/inc/vars.local.inc.php')) {
} }
$autodiscover_config = array_merge($default_autodiscover_config, $autodiscover_config); $autodiscover_config = array_merge($default_autodiscover_config, $autodiscover_config);
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
header_remove("X-Powered-By"); header_remove("X-Powered-By");
// Yubi OTP API // Yubi OTP API
@ -49,15 +47,27 @@ try {
} }
catch (PDOException $e) { catch (PDOException $e) {
?> ?>
<center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>Connection failed, database may be in warm-up state, please try again later.<br /><br />The following error was reported:<br/> <?=$e->getMessage();?></center> <center style='font-family: "Lucida Sans Unicode", "Lucida Grande", Verdana, Arial, Helvetica, sans-serif;'>Connection to database failed.<br /><br />The following error was reported:<br/> <?=$e->getMessage();?></center>
<?php <?php
exit; exit;
} }
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/sessions.inc.php';
// Set language // Set language
if (!isset($_SESSION['mailcow_locale'])) { if (!isset($_SESSION['mailcow_locale'])) {
if ($DETECT_LANGUAGE && isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$header_lang = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
foreach ($AVAILABLE_LANGUAGES as $available_lang) {
if ($header_lang == $available_lang) {
$_SESSION['mailcow_locale'] = strtolower(trim($header_lang));
}
}
}
else {
$_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG)); $_SESSION['mailcow_locale'] = strtolower(trim($DEFAULT_LANG));
} }
}
if (isset($_GET['lang']) && in_array($_GET['lang'], $AVAILABLE_LANGUAGES)) { if (isset($_GET['lang']) && in_array($_GET['lang'], $AVAILABLE_LANGUAGES)) {
$_SESSION['mailcow_locale'] = $_GET['lang']; $_SESSION['mailcow_locale'] = $_GET['lang'];
} }
@ -69,6 +79,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.mailbox.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.customize.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.customize.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.bcc.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.bcc.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.domain_admin.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.domain_admin.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.quarantaine.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.policy.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.dkim.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fwdhost.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/functions.fwdhost.inc.php';
@ -81,3 +92,4 @@ init_db_schema();
if (isset($_SESSION['mailcow_cc_role'])) { if (isset($_SESSION['mailcow_cc_role'])) {
set_acl(); set_acl();
} }
$UI_TEXTS = customize('get', 'ui_texts');

View File

@ -26,11 +26,29 @@ if (!isset($_SESSION['SESS_REMOTE_UA'])) {
$_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT']; $_SESSION['SESS_REMOTE_UA'] = $_SERVER['HTTP_USER_AGENT'];
} }
// API
if (!empty($_SERVER['HTTP_X_API_KEY'])) {
$stmt = $pdo->prepare("SELECT `username`, `allow_from` FROM `api` WHERE `api_key` = :api_key AND `active` = '1';");
$stmt->execute(array(
':api_key' => preg_replace('/[^A-Z0-9-]/', '', $_SERVER['HTTP_X_API_KEY'])
));
$api_return = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($api_return['username'])) {
if (in_array($_SERVER['REMOTE_ADDR'], explode(',', $api_return['allow_from']))) {
$_SESSION['mailcow_cc_username'] = $api_return['username'];
$_SESSION['mailcow_cc_role'] = 'admin';
$_SESSION['mailcow_cc_api'] = true;
}
}
}
// Update session cookie // Update session cookie
// setcookie(session_name() ,session_id(), time() + $SESSION_LIFETIME); // setcookie(session_name() ,session_id(), time() + $SESSION_LIFETIME);
// Check session // Check session
function session_check() { function session_check() {
if ($_SESSION['mailcow_cc_api'] === true) {
return true;
}
if (!isset($_SESSION['SESS_REMOTE_UA'])) { if (!isset($_SESSION['SESS_REMOTE_UA'])) {
return false; return false;
} }

View File

@ -64,6 +64,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
} }
} }
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") { if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
// TODO: Move file upload to API?
if (isset($_POST["submit_main_logo"])) { if (isset($_POST["submit_main_logo"])) {
if ($_FILES['main_logo']['error'] == 0) { if ($_FILES['main_logo']['error'] == 0) {
customize('add', 'main_logo', $_FILES); customize('add', 'main_logo', $_FILES);
@ -72,5 +73,16 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admi
if (isset($_POST["reset_main_logo"])) { if (isset($_POST["reset_main_logo"])) {
customize('delete', 'main_logo'); customize('delete', 'main_logo');
} }
// API cannot be controlled by API
if (isset($_POST["admin_api"])) {
admin_api('edit', $_POST);
}
if (isset($_POST["admin_api_regen_key"])) {
admin_api('regen_key', $_POST);
}
// Not available via API
if (isset($_POST["rspamd_ui"])) {
rspamd_ui('edit', $_POST);
}
} }
?> ?>

View File

@ -68,8 +68,12 @@ $autodiscover_config = array(
); );
unset($https_port); unset($https_port);
// If false, we will use DEFAULT_LANG
// Uses HTTP_ACCEPT_LANGUAGE header
$DETECT_LANGUAGE = true;
// Change default language, "de", "en", "es", "nl", "pt", "ru" // Change default language, "de", "en", "es", "nl", "pt", "ru"
$DEFAULT_LANG = 'en'; $DEFAULT_LANG = 'de';
// Available languages // Available languages
$AVAILABLE_LANGUAGES = array('de', 'en', 'es', 'fr', 'nl', 'pl', 'pt', 'ru', 'it'); $AVAILABLE_LANGUAGES = array('de', 'en', 'es', 'fr', 'nl', 'pl', 'pt', 'ru', 'it');
@ -92,13 +96,7 @@ $MAILCOW_APPS = array(
array( array(
'name' => 'SOGo', 'name' => 'SOGo',
'link' => '/SOGo/', 'link' => '/SOGo/',
'description' => 'SOGo is a web-based client for email, address book and calendar.' )
),
// array(
// 'name' => 'Roundcube',
// 'link' => '/rc/',
// 'description' => 'Roundcube is a web-based email client.',
// ),
); );
// Rows until pagination begins // Rows until pagination begins
@ -119,5 +117,6 @@ $OTP_LABEL = "mailcow UI";
// Default "to" address in relay test tool // Default "to" address in relay test tool
$RELAY_TO = "null@hosted.mailcow.de"; $RELAY_TO = "null@hosted.mailcow.de";
// Internal constants, can be ignored // Quarantaine data age in days to keep
define("F2B", 1); $QUARANTAINE_AGE = 10;

View File

@ -13,9 +13,9 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
header('Location: /user.php'); header('Location: /user.php');
exit(); exit();
} }
require_once 'inc/header.inc.php'; require_once 'inc/header.inc.php';
$_SESSION['return_to'] = $_SERVER['REQUEST_URI']; $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
?> ?>
<div class="container"> <div class="container">
<div class="row"> <div class="row">
@ -24,7 +24,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<div class="panel-heading"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?= $lang['login']['login']; ?></div> <div class="panel-heading"><span class="glyphicon glyphicon-user" aria-hidden="true"></span> <?= $lang['login']['login']; ?></div>
<div class="panel-body"> <div class="panel-body">
<div class="text-center mailcow-logo"><img src="<?=($main_logo = customize('get', 'main_logo')) ? $main_logo : '/img/cow_mailcow.svg';?>" alt="mailcow"></div> <div class="text-center mailcow-logo"><img src="<?=($main_logo = customize('get', 'main_logo')) ? $main_logo : '/img/cow_mailcow.svg';?>" alt="mailcow"></div>
<legend>mailcow UI</legend> <legend><?=$UI_TEXTS['main_name'];?></legend>
<form method="post" autofill="off"> <form method="post" autofill="off">
<div class="form-group"> <div class="form-group">
<label class="sr-only" for="login_user"><?= $lang['login']['username']; ?></label> <label class="sr-only" for="login_user"><?= $lang['login']['username']; ?></label>
@ -65,7 +65,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<?php <?php
endif; endif;
?> ?>
<legend>mailcow Apps</legend> <legend><?=$UI_TEXTS['apps_name'];?></legend>
<?php <?php
foreach ($MAILCOW_APPS as $app): foreach ($MAILCOW_APPS as $app):
?> ?>
@ -93,10 +93,14 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
</div> </div>
<div id="collapse1" class="panel-collapse collapse"> <div id="collapse1" class="panel-collapse collapse">
<div class="panel-body"> <div class="panel-body">
<p><span style="border-bottom: 1px dotted #999;">mailcow UI</span></p> <?php if ($UI_TEXTS['help_text']): ?>
<p><?=$UI_TEXTS['help_text'];?></p>
<?php else: ?>
<p><span style="border-bottom: 1px dotted #999;"><?=$UI_TEXTS['main_name'];?></span></p>
<p><?= $lang['start']['mailcow_panel_detail']; ?></p> <p><?= $lang['start']['mailcow_panel_detail']; ?></p>
<p><span style="border-bottom: 1px dotted #999;">mailcow Apps</span></p> <p><span style="border-bottom: 1px dotted #999;"><?=$UI_TEXTS['apps_name'];?></span></p>
<p><?= $lang['start']['mailcow_apps_detail']; ?></p> <p><?= $lang['start']['mailcow_apps_detail']; ?></p>
<?php endif; ?>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,199 +5,10 @@ jQuery(function($){
var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"}; var entityMap={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;","/":"&#x2F;","`":"&#x60;","=":"&#x3D;"};
function escapeHtml(n){return String(n).replace(/[&<>"'`=\/]/g,function(n){return entityMap[n]})} 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]} 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_postfix_log").on('click', function(e) {
e.preventDefault();
draw_postfix_logs();
});
$("#refresh_autodiscover_log").on('click', function(e) {
e.preventDefault();
draw_autodiscover_logs();
});
$("#refresh_dovecot_log").on('click', function(e) {
e.preventDefault();
draw_dovecot_logs();
});
$("#refresh_sogo_log").on('click', function(e) {
e.preventDefault();
draw_sogo_logs();
});
$("#refresh_fail2ban_log").on('click', function(e) {
e.preventDefault();
draw_fail2ban_logs();
});
$("#refresh_rspamd_history").on('click', function(e) {
e.preventDefault();
draw_rspamd_history();
});
$("#import_dkim_legend").on('click', function(e) { $("#import_dkim_legend").on('click', function(e) {
e.preventDefault(); e.preventDefault();
$('#import_dkim_arrow').toggleClass("animation"); $('#import_dkim_arrow').toggleClass("animation");
}); });
function draw_autodiscover_logs() {
ft_autodiscover_logs = FooTable.init('#autodiscover_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"ua","title":"User-Agent","style":{"min-width":"200px"}},
{"name":"user","title":"Username","style":{"min-width":"200px"}},
{"name":"service","title":"Service"},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/autodiscover/100',
jsonp: false,
error: function () {
console.log('Cannot draw autodiscover log table');
},
success: function (data) {
return process_table_data(data, 'autodiscover_log');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_postfix_logs() {
ft_postfix_logs = FooTable.init('#postfix_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
{"name":"message","title":lang.message},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/postfix',
jsonp: false,
error: function () {
console.log('Cannot draw postfix log table');
},
success: function (data) {
return process_table_data(data, 'general_syslog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_fail2ban_logs() {
ft_fail2ban_logs = FooTable.init('#fail2ban_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
{"name":"message","title":lang.message},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/fail2ban',
jsonp: false,
error: function () {
console.log('Cannot draw fail2ban log table');
},
success: function (data) {
return process_table_data(data, 'general_syslog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_sogo_logs() {
ft_sogo_logs = FooTable.init('#sogo_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
{"name":"message","title":lang.message},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/sogo',
jsonp: false,
error: function () {
console.log('Cannot draw sogo log table');
},
success: function (data) {
return process_table_data(data, 'general_syslog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_dovecot_logs() {
ft_dovecot_logs = FooTable.init('#dovecot_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
{"name":"message","title":lang.message},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/dovecot',
jsonp: false,
error: function () {
console.log('Cannot draw dovecot log table');
},
success: function (data) {
return process_table_data(data, 'general_syslog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_domain_admins() { function draw_domain_admins() {
ft_domainadmins = FooTable.init('#domainadminstable', { ft_domainadmins = FooTable.init('#domainadminstable', {
"columns": [ "columns": [
@ -278,108 +89,9 @@ jQuery(function($){
"sorting": {"enabled": true} "sorting": {"enabled": true}
}); });
} }
function draw_rspamd_history() {
ft_rspamd_history = FooTable.init('#rspamd_history', {
"columns": [
{"name":"unix_time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name": "ip","title": "IP address","breakpoints": "all","style": {"minWidth": 88}},
{"name": "sender_mime","title": "From","breakpoints": "xs sm md","style": {"minWidth": 100}},
{"name": "rcpt_mime","title": "To","breakpoints": "xs sm md","style": {"minWidth": 100}},
{"name": "subject","title": "Subject","breakpoints": "all","style": {"word-break": "break-all","minWidth": 150}},
{"name": "action","title": "Action","style": {"minwidth": 82}},
{"name": "score","title": "Score","style": {"maxWidth": 110},},
{"name": "symbols","title": "Symbols","breakpoints": "all",},
{"name": "size","title": "Msg size","breakpoints": "all","style": {"minwidth": 50},"formatter": function(value){return humanFileSize(value);}},
{"name": "scan_time","title": "Scan time","breakpoints": "all","style": {"maxWidth": 72},},
{"name": "message-id","title": "ID","breakpoints": "all","style": {"minWidth": 130,"overflow": "hidden","textOverflow": "ellipsis","wordBreak": "break-all","whiteSpace": "normal"}},
{"name": "user","title": "Authenticated user","breakpoints": "xs sm md","style": {"minWidth": 100}}
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/rspamd-history',
jsonp: false,
error: function () {
console.log('Cannot draw rspamd history table');
},
success: function (data) {
return process_table_data(data, 'rspamd_history');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function process_table_data(data, table) { function process_table_data(data, table) {
if (table == 'rspamd_history') { if (table == 'relayhoststable') {
$.each(data, function (i, item) {
item.rcpt_mime = item.rcpt_mime.join(",&#8203;");
Object.keys(item.symbols).map(function(key) {
var sym = item.symbols[key];
if (sym.score <= 0) {
sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)'
}
else {
sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)'
}
var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
if (sym.options) {
str += ' [' + sym.options.join(",") + "]";
}
item.symbols[key].str = str;
});
item.symbols = Object.keys(item.symbols).
map(function(key) {
return item.symbols[key];
}).sort(function(e1, e2) {
return Math.abs(e1.score) < Math.abs(e2.score);
}).map(function(e) {
return e.str;
}).join("<br>\n");
var scan_time = item.time_real.toFixed(3) + ' / ' + item.time_virtual.toFixed(3);
item.scan_time = {
"options": {
"sortValue": item.time_real
},
"value": scan_time
};
if (item.action === 'clean' || item.action === 'no action') {
item.action = "<div class='label label-success'>" + item.action + "</div>";
} else if (item.action === 'rewrite subject' || item.action === 'add header' || item.action === 'probable spam') {
item.action = "<div class='label label-warning'>" + item.action + "</div>";
} else if (item.action === 'spam' || item.action === 'reject') {
item.action = "<div class='label label-danger'>" + item.action + "</div>";
} else {
item.action = "<div class='label label-info'>" + item.action + "</div>";
}
var score_content;
if (item.score < item.required_score) {
score_content = "[ <span class='text-success'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
} else {
score_content = "[ <span class='text-danger'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
}
item.score = {
"options": {
"sortValue": item.score
},
"value": score_content
};
if (item.user == null) {
item.user = "none";
}
});
} else if (table == 'relayhoststable') {
$.each(data, function (i, item) { $.each(data, function (i, item) {
item.action = '<div class="btn-group">' + item.action = '<div class="btn-group">' +
'<a href="#" data-toggle="modal" id="miau" data-target="#testRelayhostModal" data-relayhost-id="' + encodeURI(item.id) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-stats"></span> Test</a>' + '<a href="#" data-toggle="modal" id="miau" data-target="#testRelayhostModal" data-relayhost-id="' + encodeURI(item.id) + '" class="btn btn-xs btn-default"><span class="glyphicon glyphicon-stats"></span> Test</a>' +
@ -409,48 +121,13 @@ jQuery(function($){
'<a href="#" id="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' + '<a href="#" id="delete_selected" data-id="single-domain-admin" data-api-url="delete/domain-admin" data-item="' + encodeURI(item.username) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
'</div>'; '</div>';
}); });
} else if (table == 'autodiscover_log') {
$.each(data, function (i, item) {
item.ua = '<span style="font-size:small">' + item.ua + '</span>';
if (item.service == "activesync") {
item.service = '<span class="label label-info">ActiveSync</span>';
}
else if (item.service == "imap") {
item.service = '<span class="label label-success">IMAP, SMTP, Cal-/CardDAV</span>';
}
else {
item.service = '<span class="label label-danger">' + item.service + '</span>';
}
});
} else if (table == 'general_syslog') {
$.each(data, function (i, item) {
item.message = escapeHtml(item.message);
var danger_class = ["emerg", "alert", "crit", "err"];
var warning_class = ["warning", "warn"];
var info_class = ["notice", "info", "debug"];
if (jQuery.inArray(item.priority, danger_class) !== -1) {
item.priority = '<span class="label label-danger">' + item.priority + '</span>';
}
else if (jQuery.inArray(item.priority, warning_class) !== -1) {
item.priority = '<span class="label label-warning">' + item.priority + '</span>';
}
else if (jQuery.inArray(item.priority, info_class) !== -1) {
item.priority = '<span class="label label-info">' + item.priority + '</span>';
}
});
} }
return data return data
}; };
// Initial table drawings // Initial table drawings
draw_postfix_logs();
draw_autodiscover_logs();
draw_dovecot_logs();
draw_sogo_logs();
draw_fail2ban_logs();
draw_domain_admins(); draw_domain_admins();
draw_fwd_hosts(); draw_fwd_hosts();
draw_relayhosts(); draw_relayhosts();
draw_rspamd_history();
// Relayhost // Relayhost
$('#testRelayhostModal').on('show.bs.modal', function (e) { $('#testRelayhostModal').on('show.bs.modal', function (e) {
$('#test_relayhost_result').text("-"); $('#test_relayhost_result').text("-");
@ -485,31 +162,6 @@ jQuery(function($){
$('#priv_key_pre').text(decoded_key); $('#priv_key_pre').text(decoded_key);
} }
}) })
$('.add_log_lines').on('click', function (e) {
e.preventDefault();
var log_table= $(this).data("table")
var new_nrows = ($(this).data("nrows") - 1)
var post_process = $(this).data("post-process")
var log_url = $(this).data("log-url")
if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
console.log("no data-table or data-nrows or log_url or data-post-process attr found");
return;
}
if (ft = FooTable.get($('#' + log_table))) {
var heading = ft.$el.parents('.tab-pane').find('.panel-heading')
var ft_paging = ft.use(FooTable.Paging)
var load_rows = ft_paging.totalRows + '-' + (ft_paging.totalRows + new_nrows)
$.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
var rows = process_table_data(data, post_process);
var rows_now = (ft_paging.totalRows + data.length);
$(heading).children('.log-lines').text(rows_now)
mailcow_alert_box(data.length + lang.additional_rows, "success");
ft.rows.load(rows, true);
});
}
})
// App links // App links
function add_table_row(table_id) { function add_table_row(table_id) {
var row = $('<tr />'); var row = $('<tr />');

View File

@ -102,6 +102,7 @@ $(document).ready(function() {
return false; return false;
} }
} }
// alert(JSON.stringify(api_attr));
// If clicked element #edit_selected has data-item attribute, it is added to "items" // If clicked element #edit_selected has data-item attribute, it is added to "items"
if (typeof $(this).data('item') !== 'undefined') { if (typeof $(this).data('item') !== 'undefined') {
var id = $(this).data('id'); var id = $(this).data('id');
@ -126,7 +127,9 @@ $(document).ready(function() {
jsonp: false, jsonp: false,
complete: function(data) { complete: function(data) {
var response = (data.responseText); var response = (data.responseText);
if (typeof response !== 'undefined' && response.length !== 0) {
response_obj = JSON.parse(response); response_obj = JSON.parse(response);
}
if (api_reload_window === true) { if (api_reload_window === true) {
window.location = window.location.href.split("#")[0]; window.location = window.location.href.split("#")[0];
} }
@ -141,6 +144,11 @@ $(document).ready(function() {
var id = $(this).data('id'); var id = $(this).data('id');
var api_url = $(this).data('api-url'); var api_url = $(this).data('api-url');
var api_attr = $(this).data('api-attr'); var api_attr = $(this).data('api-attr');
if (typeof $(this).data('api-reload-window') !== 'undefined') {
api_reload_window = $(this).data('api-reload-window');
} else {
api_reload_window = true;
}
// If clicked button is in a form with the same data-id as the button, // If clicked button is in a form with the same data-id as the button,
// we merge all input fields by {"name":"value"} into api-attr // we merge all input fields by {"name":"value"} into api-attr
if ($(this).closest("form").data('id') == id) { if ($(this).closest("form").data('id') == id) {
@ -188,11 +196,21 @@ $(document).ready(function() {
url: '/api/v1/' + api_url, url: '/api/v1/' + api_url,
jsonp: false, jsonp: false,
complete: function(data) { complete: function(data) {
// var reponse = (JSON.parse(data.responseText)); var response = (data.responseText);
// console.log(reponse.type); if (typeof response !== 'undefined' && response.length !== 0) {
// console.log(reponse.msg); response_obj = JSON.parse(response);
if (response_obj.type == 'success') {
$('form').formcache('clear');
}
else {
var add_modal = $('.modal.in').attr('id');
localStorage.setItem("add_modal", add_modal);
}
}
if (api_reload_window === true) {
window.location = window.location.href.split("#")[0]; window.location = window.location.href.split("#")[0];
} }
}
}); });
}); });

View File

@ -0,0 +1,503 @@
jQuery(function($){
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
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]}
$("#refresh_postfix_log").on('click', function(e) {
e.preventDefault();
draw_postfix_logs();
});
$("#refresh_autodiscover_log").on('click', function(e) {
e.preventDefault();
draw_autodiscover_logs();
});
$("#refresh_dovecot_log").on('click', function(e) {
e.preventDefault();
draw_dovecot_logs();
});
$("#refresh_sogo_log").on('click', function(e) {
e.preventDefault();
draw_sogo_logs();
});
$("#refresh_watchdog_log").on('click', function(e) {
e.preventDefault();
draw_watchdog_logs();
});
$("#refresh_api_log").on('click', function(e) {
e.preventDefault();
draw_api_logs();
});
$("#refresh_acme_log").on('click', function(e) {
e.preventDefault();
draw_acme_logs();
});
$("#refresh_fail2ban_log").on('click', function(e) {
e.preventDefault();
draw_fail2ban_logs();
});
$("#refresh_rspamd_history").on('click', function(e) {
e.preventDefault();
draw_rspamd_history();
});
function draw_autodiscover_logs() {
ft_autodiscover_logs = FooTable.init('#autodiscover_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"ua","title":"User-Agent","style":{"min-width":"200px"}},
{"name":"user","title":"Username","style":{"min-width":"200px"}},
{"name":"service","title":"Service"},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/autodiscover/100',
jsonp: false,
error: function () {
console.log('Cannot draw autodiscover log table');
},
success: function (data) {
return process_table_data(data, 'autodiscover_log');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_postfix_logs() {
ft_postfix_logs = FooTable.init('#postfix_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
{"name":"message","title":lang.message},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/postfix',
jsonp: false,
error: function () {
console.log('Cannot draw postfix log table');
},
success: function (data) {
return process_table_data(data, 'general_syslog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_watchdog_logs() {
ft_watchdog_logs = FooTable.init('#watchdog_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"service","title":"Service"},
{"name":"trend","title":"Trend"},
{"name":"message","title":lang.message},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/watchdog',
jsonp: false,
error: function () {
console.log('Cannot draw watchdog log table');
},
success: function (data) {
return process_table_data(data, 'watchdog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_api_logs() {
ft_api_logs = FooTable.init('#api_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"uri","title":"URI","style":{"width":"310px"}},
{"name":"method","title":"Method","style":{"width":"80px"}},
{"name":"remote","title":"IP","style":{"width":"80px"}},
{"name":"data","title":"Data","style":{"word-break":"break-all"}},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/api',
jsonp: false,
error: function () {
console.log('Cannot draw api log table');
},
success: function (data) {
return process_table_data(data, 'apilog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_acme_logs() {
ft_acme_logs = FooTable.init('#acme_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"message","title":lang.message},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/acme',
jsonp: false,
error: function () {
console.log('Cannot draw acme log table');
},
success: function (data) {
return process_table_data(data, 'general_syslog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_fail2ban_logs() {
ft_fail2ban_logs = FooTable.init('#fail2ban_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
{"name":"message","title":lang.message},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/fail2ban',
jsonp: false,
error: function () {
console.log('Cannot draw fail2ban log table');
},
success: function (data) {
return process_table_data(data, 'general_syslog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_sogo_logs() {
ft_sogo_logs = FooTable.init('#sogo_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
{"name":"message","title":lang.message},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/sogo',
jsonp: false,
error: function () {
console.log('Cannot draw sogo log table');
},
success: function (data) {
return process_table_data(data, 'general_syslog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_dovecot_logs() {
ft_dovecot_logs = FooTable.init('#dovecot_log', {
"columns": [
{"name":"time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name":"priority","title":lang.priority,"style":{"width":"80px"}},
{"name":"message","title":lang.message},
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/dovecot',
jsonp: false,
error: function () {
console.log('Cannot draw dovecot log table');
},
success: function (data) {
return process_table_data(data, 'general_syslog');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function draw_rspamd_history() {
ft_rspamd_history = FooTable.init('#rspamd_history', {
"columns": [
{"name":"unix_time","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.time,"style":{"width":"170px"}},
{"name": "ip","title": "IP address","breakpoints": "all","style": {"minWidth": 88}},
{"name": "sender_mime","title": "From","breakpoints": "xs sm md","style": {"minWidth": 100}},
{"name": "rcpt_mime","title": "To","breakpoints": "xs sm md","style": {"minWidth": 100}},
{"name": "subject","title": "Subject","breakpoints": "all","style": {"word-break": "break-all","minWidth": 150}},
{"name": "action","title": "Action","style": {"minwidth": 82}},
{"name": "score","title": "Score","style": {"maxWidth": 110},},
{"name": "symbols","title": "Symbols","breakpoints": "all",},
{"name": "size","title": "Msg size","breakpoints": "all","style": {"minwidth": 50},"formatter": function(value){return humanFileSize(value);}},
{"name": "scan_time","title": "Scan time","breakpoints": "all","style": {"maxWidth": 72},},
{"name": "message-id","title": "ID","breakpoints": "all","style": {"minWidth": 130,"overflow": "hidden","textOverflow": "ellipsis","wordBreak": "break-all","whiteSpace": "normal"}},
{"name": "user","title": "Authenticated user","breakpoints": "xs sm md","style": {"minWidth": 100}}
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/logs/rspamd-history',
jsonp: false,
error: function () {
console.log('Cannot draw rspamd history table');
},
success: function (data) {
return process_table_data(data, 'rspamd_history');
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": log_pagination_size},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(e, ft){
heading = ft.$el.parents('.tab-pane').find('.panel-heading')
$(heading).children('.log-lines').text(function(){
var ft_paging = ft.use(FooTable.Paging)
return ft_paging.totalRows;
})
}
}
});
}
function process_table_data(data, table) {
if (table == 'rspamd_history') {
$.each(data, function (i, item) {
item.rcpt_mime = item.rcpt_mime.join(",&#8203;");
Object.keys(item.symbols).map(function(key) {
var sym = item.symbols[key];
if (sym.score <= 0) {
sym.score_formatted = '(<span class="text-success"><b>' + sym.score + '</b></span>)'
}
else {
sym.score_formatted = '(<span class="text-danger"><b>' + sym.score + '</b></span>)'
}
var str = '<strong>' + key + '</strong> ' + sym.score_formatted;
if (sym.options) {
str += ' [' + sym.options.join(",") + "]";
}
item.symbols[key].str = str;
});
item.symbols = Object.keys(item.symbols).
map(function(key) {
return item.symbols[key];
}).sort(function(e1, e2) {
return Math.abs(e1.score) < Math.abs(e2.score);
}).map(function(e) {
return e.str;
}).join("<br>\n");
var scan_time = item.time_real.toFixed(3) + ' / ' + item.time_virtual.toFixed(3);
item.scan_time = {
"options": {
"sortValue": item.time_real
},
"value": scan_time
};
if (item.action === 'clean' || item.action === 'no action') {
item.action = "<div class='label label-success'>" + item.action + "</div>";
} else if (item.action === 'rewrite subject' || item.action === 'add header' || item.action === 'probable spam') {
item.action = "<div class='label label-warning'>" + item.action + "</div>";
} else if (item.action === 'spam' || item.action === 'reject') {
item.action = "<div class='label label-danger'>" + item.action + "</div>";
} else {
item.action = "<div class='label label-info'>" + item.action + "</div>";
}
var score_content;
if (item.score < item.required_score) {
score_content = "[ <span class='text-success'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
} else {
score_content = "[ <span class='text-danger'>" + item.score.toFixed(2) + " / " + item.required_score + "</span> ]";
}
item.score = {
"options": {
"sortValue": item.score
},
"value": score_content
};
if (item.user == null) {
item.user = "none";
}
});
} else if (table == 'autodiscover_log') {
$.each(data, function (i, item) {
item.ua = '<span style="font-size:small">' + item.ua + '</span>';
if (item.service == "activesync") {
item.service = '<span class="label label-info">ActiveSync</span>';
}
else if (item.service == "imap") {
item.service = '<span class="label label-success">IMAP, SMTP, Cal-/CardDAV</span>';
}
else {
item.service = '<span class="label label-danger">' + item.service + '</span>';
}
});
} else if (table == 'watchdog') {
$.each(data, function (i, item) {
if (item.message == null) {
item.message = 'Health level: ' + item.lvl + '% (' + item.hpnow + '/' + item.hptotal + ')';
if (item.hpdiff < 0) {
item.trend = '<span class="label label-danger"><span class="glyphicon glyphicon-arrow-down"></span> ' + item.hpdiff + '</span>';
}
else if (item.hpdiff == 0) {
item.trend = '<span class="label label-info"><span class="glyphicon glyphicon-arrow-right"></span> ' + item.hpdiff + '</span>';
}
else {
item.trend = '<span class="label label-success"><span class="glyphicon glyphicon-arrow-up"></span> ' + item.hpdiff + '</span>';
}
}
else {
item.trend = '';
item.service = '';
}
});
} else if (table == 'general_syslog') {
$.each(data, function (i, item) {
if (item === null) { return true; }
item.message = escapeHtml(item.message);
var danger_class = ["emerg", "alert", "crit", "err"];
var warning_class = ["warning", "warn"];
var info_class = ["notice", "info", "debug"];
if (jQuery.inArray(item.priority, danger_class) !== -1) {
item.priority = '<span class="label label-danger">' + item.priority + '</span>';
} else if (jQuery.inArray(item.priority, warning_class) !== -1) {
item.priority = '<span class="label label-warning">' + item.priority + '</span>';
} else if (jQuery.inArray(item.priority, info_class) !== -1) {
item.priority = '<span class="label label-info">' + item.priority + '</span>';
}
});
} else if (table == 'apilog') {
$.each(data, function (i, item) {
if (item === null) { return true; }
if (item.method == 'GET') {
item.method = '<span class="label label-success">' + item.method + '</span>';
} else if (item.method == 'POST') {
item.method = '<span class="label label-warning">' + item.method + '</span>';
}
});
}
return data
};
$('.add_log_lines').on('click', function (e) {
e.preventDefault();
var log_table= $(this).data("table")
var new_nrows = ($(this).data("nrows") - 1)
var post_process = $(this).data("post-process")
var log_url = $(this).data("log-url")
if (log_table === undefined || new_nrows === undefined || post_process === undefined || log_url === undefined) {
console.log("no data-table or data-nrows or log_url or data-post-process attr found");
return;
}
if (ft = FooTable.get($('#' + log_table))) {
var heading = ft.$el.parents('.tab-pane').find('.panel-heading')
var ft_paging = ft.use(FooTable.Paging)
var load_rows = ft_paging.totalRows + '-' + (ft_paging.totalRows + new_nrows)
$.get('/api/v1/get/logs/' + log_url + '/' + load_rows).then(function(data){
if (data.length === undefined) { mailcow_alert_box(lang.no_new_rows, "info"); return; }
var rows = process_table_data(data, post_process);
var rows_now = (ft_paging.totalRows + data.length);
$(heading).children('.log-lines').text(rows_now)
mailcow_alert_box(data.length + lang.additional_rows, "success");
ft.rows.load(rows, true);
});
}
})
// Initial table drawings
draw_postfix_logs();
draw_autodiscover_logs();
draw_dovecot_logs();
draw_sogo_logs();
draw_watchdog_logs();
draw_acme_logs();
draw_api_logs();
draw_fail2ban_logs();
draw_rspamd_history();
});

10
data/web/js/formcache.min.js vendored 100644
View File

@ -0,0 +1,10 @@
/*!
* Form Cache v@VERSION
* https://github.com/fengyuanchen/formcache
*
* Copyright 2014 Fengyuan Chen
* Released under the MIT license
*
* Date: @DATE
*/
!function(t){"function"==typeof define&&define.amd?define("formcache",["jquery"],t):t(jQuery)}(function(t){"use strict";var e=t(window),i=window.sessionStorage,s=window.localStorage,n="undefined",o=".formcache",a=/[\.\*\+\^\$\:\!\[\]#>~]+/g,c="change"+o,h="beforeunload"+o,r=function(t){return"checkbox"===t.type||"radio"===t.type},f=function(t){return parseInt(t,10)},u=function(e,i){this.form=e,this.$form=t(e),this.defaults=t.extend({},u.DEFAULTS,t.isPlainObject(i)?i:{}),this.init()};u.prototype={constructor:u,init:function(){var e=this.defaults;e.maxAge=Math.abs(e.maxAge||e.maxage),e.autoStore=Boolean(e.autoStore||e.autostore),this.initKey(),this.initStorage(),this.caches=this.storage.caches,this.index=0,this.activeIndex=0,this.storing=null,t.isArray(e.controls)||(e.controls=[]),this.$controls=this.$form.find(e.controls.join()).not(":file"),this.addListeners(),this.outputCache()},initKey:function(){var e=this.$form,i=this.defaults.key||e.data("key");i||(t("form").each(function(e){t(this).data("key",e)}),i=e.data("key")),this.key=location.pathname+"#formcache-"+i},initStorage:function(){var e,n=this.defaults,o=this.key,a=new Date,c={date:a,maxAge:n.maxAge,caches:[]};i&&(e=i.getItem(o)),!e&&s&&(e=s.getItem(o)),e="string"==typeof e?JSON.parse(e):null,t.isPlainObject(e)?"number"==typeof e.maxAge&&(a-new Date(e.date))/1e3>e.maxAge&&(e=c):e=c,this.storage=e},addListeners:function(){this.defaults.autoStore&&(this.$controls.on(c,t.proxy(this.change,this)),e.on(h,t.proxy(this.beforeunload,this)))},removeListeners:function(){this.defaults.autoStore&&(this.$controls.off(c,this.change),e.off(h,this.beforeunload))},change:function(e){var i,s,n=e.target,o=t(n),c=o.attr("name"),h=[];c&&(i=c.replace(a,""),this.$controls.filter('[name*="'+i+'"]').each(function(){r(n)?h.push(this.checked):(s=t(this).val(),s&&h.push(s))}),h.length&&(this.update(c,h),clearTimeout(this.storing),this.storing=setTimeout(t.proxy(this.store,this),1e3)))},beforeunload:function(){this.update(),this.store()},update:function(t,e){var i=this.activeIndex||this.index,s=this.getCache(i);"string"==typeof t?s[t]=e:s=this.serialize(),this.setCache(i,s)},serialize:function(){var e={};return this.$controls.each(function(){var i,s,n=t(this),o=n.attr("name");o&&(i=e[o],i=t.isArray(i)?i:[],r(this)?i.push(this.checked):(s=n.val(),s&&i.push(s)),i.length&&(e[o]=i))}),e},getCache:function(t){return this.caches[f(t)||this.index]||{}},getCaches:function(){return this.caches},setCache:function(e,i){typeof i===n&&(i=e,e=NaN),t.isPlainObject(i)&&(e=f(e)||this.index,this.caches[e]=i,this.store())},setCaches:function(e){t.isArray(e)&&(this.caches=e,this.store())},removeCache:function(t){this.caches.splice(f(t)||this.index,1),this.store()},removeCaches:function(){this.caches=[],this.store()},outputCache:function(e){var i=this.getCache(e);t.isPlainObject(i)&&(this.activeIndex=f(e)||this.index,i=t.extend(!0,{},i),this.$controls.each(function(){var e,s,n=t(this),o=n.attr("name");o&&(e=i[o],t.isArray(e)&&e.length&&(s=e.shift(),r(this)?this.checked=s:n.val(s)))}))},store:function(){var t=this.storage,e=this.key,n=this.defaults;t.date=new Date,t.maxAge=n.maxAge,t=JSON.stringify(t),n.session&&i&&i.setItem(e,t),n.local&&s&&s.setItem(e,t)},clear:function(){var t=this.key,e=this.defaults;e.session&&i&&i.removeItem(t),e.local&&s&&s.removeItem(t)},destroy:function(){this.removeListeners(),this.$form.removeData("formcache")}},u.DEFAULTS={key:"",local:!0,session:0,autoStore:!0,maxAge:void 0,controls:["select","textarea","input"]},u.setDefaults=function(e){t.extend(u.DEFAULTS,e)},u.other=t.fn.formcache,t.fn.formcache=function(e){var i,s=[].slice.call(arguments,1);return this.each(function(){var n,o=t(this),a=o.data("formcache");a||o.data("formcache",a=new u(this,e)),"string"==typeof e&&t.isFunction(n=a[e])&&(i=n.apply(a,s))}),typeof i!==n?i:this},t.fn.formcache.Constructor=u,t.fn.formcache.setDefaults=u.setDefaults,t.fn.formcache.noConflict=function(){return t.fn.formcache=u.other,this},t(function(){t('form[data-toggle="formcache"]').formcache()})});

View File

@ -319,9 +319,9 @@ jQuery(function($){
"columns": [ "columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"}, {"name":"chkbox","title":"","style":{"maxWidth":"60px","width":"60px"},"filterable": false,"sortable": false,"type":"html"},
{"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}}, {"sorted": true,"name":"id","title":"ID","style":{"maxWidth":"60px","width":"60px","text-align":"center"}},
{"name":"type","title":"Type"}, {"name":"type","title":lang.bcc_type},
{"name":"local_dest","title":"Local destination"}, {"name":"local_dest","title":lang.bcc_local_dest},
{"name":"bcc_dest","title":"BCC destination/s"}, {"name":"bcc_dest","title":lang.bcc_destinations},
{"name":"domain","title":lang.domain,"breakpoints":"xs sm"}, {"name":"domain","title":lang.domain,"breakpoints":"xs sm"},
{"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active}, {"name":"active","filterable": false,"style":{"maxWidth":"80px","width":"80px"},"title":lang.active},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"} {"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right","maxWidth":"180px","width":"180px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}

View File

@ -0,0 +1,82 @@
// Base64 functions
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(r){var t,e,o,a,h,n,c,d="",C=0;for(r=Base64._utf8_encode(r);C<r.length;)a=(t=r.charCodeAt(C++))>>2,h=(3&t)<<4|(e=r.charCodeAt(C++))>>4,n=(15&e)<<2|(o=r.charCodeAt(C++))>>6,c=63&o,isNaN(e)?n=c=64:isNaN(o)&&(c=64),d=d+this._keyStr.charAt(a)+this._keyStr.charAt(h)+this._keyStr.charAt(n)+this._keyStr.charAt(c);return d},decode:function(r){var t,e,o,a,h,n,c="",d=0;for(r=r.replace(/[^A-Za-z0-9\+\/\=]/g,"");d<r.length;)t=this._keyStr.indexOf(r.charAt(d++))<<2|(a=this._keyStr.indexOf(r.charAt(d++)))>>4,e=(15&a)<<4|(h=this._keyStr.indexOf(r.charAt(d++)))>>2,o=(3&h)<<6|(n=this._keyStr.indexOf(r.charAt(d++))),c+=String.fromCharCode(t),64!=h&&(c+=String.fromCharCode(e)),64!=n&&(c+=String.fromCharCode(o));return c=Base64._utf8_decode(c)},_utf8_encode:function(r){r=r.replace(/\r\n/g,"\n");for(var t="",e=0;e<r.length;e++){var o=r.charCodeAt(e);o<128?t+=String.fromCharCode(o):o>127&&o<2048?(t+=String.fromCharCode(o>>6|192),t+=String.fromCharCode(63&o|128)):(t+=String.fromCharCode(o>>12|224),t+=String.fromCharCode(o>>6&63|128),t+=String.fromCharCode(63&o|128))}return t},_utf8_decode:function(r){for(var t="",e=0,o=c1=c2=0;e<r.length;)(o=r.charCodeAt(e))<128?(t+=String.fromCharCode(o),e++):o>191&&o<224?(c2=r.charCodeAt(e+1),t+=String.fromCharCode((31&o)<<6|63&c2),e+=2):(c2=r.charCodeAt(e+1),c3=r.charCodeAt(e+2),t+=String.fromCharCode((15&o)<<12|(63&c2)<<6|63&c3),e+=3);return t}};
jQuery(function($){
// http://stackoverflow.com/questions/24816/escaping-html-strings-with-jquery
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]}
function draw_quarantaine_table() {
ft_quarantainetable = FooTable.init('#quarantainetable', {
"columns": [
{"name":"chkbox","title":"","style":{"maxWidth":"40px","width":"40px"},"filterable": false,"sortable": false,"type":"html"},
{"name":"id","type":"ID","filterable": false,"sorted": true,"direction":"DESC","title":"ID","style":{"width":"50px"}},
{"name":"qid","type":"text","title":lang.qid,"style":{"width":"125px"}},
{"name":"sender","title":lang.sender,"breakpoints":"xs sm"},
{"name":"rcpt","title":lang.rcpt, "type": "text"},
{"name":"created","formatter":function unix_time_format(tm) { var date = new Date(tm ? tm * 1000 : 0); return date.toLocaleString();},"title":lang.received,"style":{"width":"170px"}},
{"name":"action","filterable": false,"sortable": false,"style":{"text-align":"right"},"style":{"width":"205px"},"type":"html","title":lang.action,"breakpoints":"xs sm"}
],
"rows": $.ajax({
dataType: 'json',
url: '/api/v1/get/quarantaine/all',
jsonp: false,
error: function () {
console.log('Cannot draw quarantaine table');
},
success: function (data) {
$.each(data, function (i, item) {
item.action = '<div class="btn-group">' +
'<a href="#" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-info show_qid_info"><span class="glyphicon glyphicon-modal-window"></span> ' + lang.show_item + '</a>' +
'<a href="#" id="delete_selected" data-id="del-single-qitem" data-api-url="delete/qitem" data-item="' + encodeURI(item.id) + '" class="btn btn-xs btn-danger"><span class="glyphicon glyphicon-trash"></span> ' + lang.remove + '</a>' +
'</div>';
item.chkbox = '<input type="checkbox" data-id="qitems" name="multi_select" value="' + item.id + '" />';
});
}
}),
"empty": lang.empty,
"paging": {"enabled": true,"limit": 5,"size": pagination_size},
"sorting": {"enabled": true},
"on": {
"ready.ft.table": function(ev, ft){
$('.show_qid_info').on('click', function (e) {
e.preventDefault();
var qitem = $(this).data('item');
$('#qidDetailModal').modal('show');
$( "#qid_error" ).hide();
$.ajax({
url: '/inc/ajax/qitem_details.php',
data: { id: qitem },
dataType: 'json',
success: function(data){
if (typeof data.error !== 'undefined') {
$( "#qid_error" ).text(data.error);
$( "#qid_error" ).show();
}
$('#qid_detail_subj').text(escapeHtml(data.subject));
$('#qid_detail_text').text(escapeHtml(data.text_plain));
if (typeof data.attachments !== 'undefined') {
$( "#qid_detail_atts" ).text('');
$.each(data.attachments, function( index, value ) {
$( "#qid_detail_atts" ).append(
'<p><a href="/inc/ajax/qitem_details.php?id=' + qitem + '&att=' + index + '" target="_blank">' + value[0] + '</a> (' + value[1] + ')' +
' - <small><a href="' + value[3] + '" target="_blank">' + lang.check_hash + '</a></small></p>'
);
});
}
else {
$( "#qid_detail_atts" ).text('-');
}
}
});
})
}
},
"filtering": {"enabled": true,"position": "left","connectors": false,"placeholder": lang.filter_table},
});
}
// Initial table drawings
draw_quarantaine_table();
});

View File

@ -15,6 +15,41 @@ delete/alias => POST data:
header('Content-Type: application/json'); header('Content-Type: application/json');
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
error_reporting(0); error_reporting(0);
function api_log($postarray) {
global $redis;
$data_var = array();
foreach ($postarray as $data => &$value) {
if ($data == 'csrf_token') {
continue;
}
if ($value = json_decode($value, true)) {
unset($value["csrf_token"]);
$value = json_encode($value);
}
$data_var[] = $data . "='" . $value . "'";
}
try {
$log_line = array(
'time' => time(),
'uri' => $_SERVER['REQUEST_URI'],
'method' => $_SERVER['REQUEST_METHOD'],
'remote' => $_SERVER['REMOTE_ADDR'],
'data' => implode(', ', $data_var)
);
$redis->lPush('API_LOG', json_encode($log_line));
}
catch (RedisException $e) {
$_SESSION['return'] = array(
'type' => 'danger',
'msg' => 'Redis: '.$e
);
return false;
}
}
api_log($_POST);
if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_username'])) { if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_username'])) {
if (isset($_GET['query'])) { if (isset($_GET['query'])) {
@ -258,39 +293,6 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
)); ));
} }
break; break;
case "bcc":
if (isset($_POST['attr'])) {
$attr = (array)json_decode($_POST['attr'], true);
if (bcc('add', $attr) === false) {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot add item'
));
}
}
else {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'success',
'msg' => 'Task completed'
));
}
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot find attributes in post data'
));
}
break;
case "domain-policy": case "domain-policy":
if (isset($_POST['attr'])) { if (isset($_POST['attr'])) {
$attr = (array)json_decode($_POST['attr'], true); $attr = (array)json_decode($_POST['attr'], true);
@ -555,6 +557,39 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
)); ));
} }
break; break;
case "bcc":
if (isset($_POST['attr'])) {
$attr = (array)json_decode($_POST['attr'], true);
if (bcc('add', $attr) === false) {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot add item'
));
}
}
else {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'success',
'msg' => 'Task completed'
));
}
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot find attributes in post data'
));
}
break;
} }
break; break;
case "get": case "get":
@ -752,6 +787,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
break; break;
} }
break; break;
case "relayhost": case "relayhost":
switch ($object) { switch ($object) {
case "all": case "all":
@ -870,6 +906,54 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
echo '{}'; echo '{}';
} }
break; break;
case "watchdog":
// 0 is first record, so empty is fine
if (isset($extra)) {
$extra = preg_replace('/[^\d\-]/i', '', $extra);
$logs = get_logs('watchdog-mailcow', $extra);
}
else {
$logs = get_logs('watchdog-mailcow');
}
if (isset($logs) && !empty($logs)) {
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
else {
echo '{}';
}
break;
case "acme":
// 0 is first record, so empty is fine
if (isset($extra)) {
$extra = preg_replace('/[^\d\-]/i', '', $extra);
$logs = get_logs('acme-mailcow', $extra);
}
else {
$logs = get_logs('acme-mailcow');
}
if (isset($logs) && !empty($logs)) {
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
else {
echo '{}';
}
break;
case "api":
// 0 is first record, so empty is fine
if (isset($extra)) {
$extra = preg_replace('/[^\d\-]/i', '', $extra);
$logs = get_logs('api-mailcow', $extra);
}
else {
$logs = get_logs('api-mailcow');
}
if (isset($logs) && !empty($logs)) {
echo json_encode($logs, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
else {
echo '{}';
}
break;
case "rspamd-history": case "rspamd-history":
// 0 is first record, so empty is fine // 0 is first record, so empty is fine
if (isset($extra)) { if (isset($extra)) {
@ -1088,7 +1172,6 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
} }
break; break;
default: default:
$data = bcc('details', $object); $data = bcc('details', $object);
if (!empty($data)) { if (!empty($data)) {
@ -1230,6 +1313,29 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
break; break;
} }
break; break;
case "quarantaine":
// "all" will not print details
switch ($object) {
case "all":
$data = quarantaine('get');
if (!isset($data) || empty($data)) {
echo '{}';
}
else {
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
break;
default:
$data = quarantaine('details', $object);
if (!isset($data) || empty($data)) {
echo '{}';
}
else {
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
break;
}
break;
case "alias-domain": case "alias-domain":
switch ($object) { switch ($object) {
case "all": case "all":
@ -1342,13 +1448,10 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "u2f-registration": case "u2f-registration":
header('Content-Type: application/javascript'); header('Content-Type: application/javascript');
if (($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") && $_SESSION["mailcow_cc_username"] == $object) { if (($_SESSION["mailcow_cc_role"] == "admin" || $_SESSION["mailcow_cc_role"] == "domainadmin") && $_SESSION["mailcow_cc_username"] == $object) {
list($req, $sigs) = $u2f->getRegisterData(get_u2f_registrations($object)); $data = $u2f->getRegisterData(get_u2f_registrations($object));
list($req, $sigs) = $data;
$_SESSION['regReq'] = json_encode($req); $_SESSION['regReq'] = json_encode($req);
$_SESSION['regSigs'] = json_encode($sigs); echo 'var req = ' . json_encode($req) . '; var sigs = ' . json_encode($sigs) . ';';
echo 'var req = ' . json_encode($req) . ';';
echo 'var registeredKeys = ' . json_encode($sigs) . ';';
echo 'var appId = req.appId;';
echo 'var registerRequests = [{version: req.version, challenge: req.challenge}];';
} }
else { else {
return; return;
@ -1357,19 +1460,9 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
case "u2f-authentication": case "u2f-authentication":
header('Content-Type: application/javascript'); header('Content-Type: application/javascript');
if (isset($_SESSION['pending_mailcow_cc_username']) && $_SESSION['pending_mailcow_cc_username'] == $object) { if (isset($_SESSION['pending_mailcow_cc_username']) && $_SESSION['pending_mailcow_cc_username'] == $object) {
$auth_data = $u2f->getAuthenticateData(get_u2f_registrations($object)); $reqs = json_encode($u2f->getAuthenticateData(get_u2f_registrations($object)));
$challenge = $auth_data[0]->challenge; $_SESSION['authReq'] = $reqs;
$appId = $auth_data[0]->appId; echo 'var req = ' . $reqs . ';';
foreach ($auth_data as $each) {
$key = array(); // Empty array
$key['version'] = $each->version;
$key['keyHandle'] = $each->keyHandle;
$registeredKey[] = $key;
}
$_SESSION['authReq'] = json_encode($auth_data);
echo 'var appId = "' . $appId . '";';
echo 'var challenge = ' . json_encode($challenge) . ';';
echo 'var registeredKeys = ' . json_encode($registeredKey) . ';';
} }
else { else {
return; return;
@ -1546,6 +1639,47 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
)); ));
} }
break; break;
case "qitem":
if (isset($_POST['items'])) {
$items = (array)json_decode($_POST['items'], true);
if (is_array($items)) {
if (quarantaine('delete', array('id' => $items)) === false) {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Deletion of items/s failed'
));
}
}
else {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'success',
'msg' => 'Task completed'
));
}
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot find id array in post data'
));
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Cannot find items in post data'
));
}
break;
case "bcc": case "bcc":
if (isset($_POST['items'])) { if (isset($_POST['items'])) {
$items = (array)json_decode($_POST['items'], true); $items = (array)json_decode($_POST['items'], true);
@ -2042,6 +2176,50 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
break; break;
case "edit": case "edit":
switch ($category) { switch ($category) {
case "bcc":
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('id' => $items), $attr);
if (is_array($postarray['id'])) {
if (bcc('edit', $postarray) === false) {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Edit failed'
));
}
exit();
}
else {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'success',
'msg' => 'Task completed'
));
}
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Incomplete post data'
));
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Incomplete post data'
));
}
break;
case "alias": case "alias":
if (isset($_POST['items']) && isset($_POST['attr'])) { if (isset($_POST['items']) && isset($_POST['attr'])) {
$items = (array)json_decode($_POST['items'], true); $items = (array)json_decode($_POST['items'], true);
@ -2260,6 +2438,85 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
)); ));
} }
break; break;
case "qitem":
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('id' => $items), $attr);
if (is_array($postarray['id'])) {
if (quarantaine('edit', $postarray) === false) {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Edit failed'
));
}
exit();
}
else {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'success',
'msg' => 'Task completed'
));
}
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Incomplete post data'
));
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Incomplete post data'
));
}
break;
case "quarantaine":
// Edit settings, does not need IDs
if (isset($_POST['attr'])) {
$postarray = json_decode($_POST['attr'], true);
if (quarantaine('edit', $postarray) === false) {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Edit failed'
));
}
exit();
}
else {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'success',
'msg' => 'Task completed'
));
}
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Incomplete post data'
));
}
break;
case "time_limited_alias": case "time_limited_alias":
if (isset($_POST['items']) && isset($_POST['attr'])) { if (isset($_POST['items']) && isset($_POST['attr'])) {
$items = (array)json_decode($_POST['items'], true); $items = (array)json_decode($_POST['items'], true);
@ -2437,50 +2694,6 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
)); ));
} }
break; break;
case "bcc":
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('id' => $items), $attr);
if (is_array($postarray['id'])) {
if (bcc('edit', $postarray) === false) {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Edit failed'
));
}
exit();
}
else {
if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']);
}
else {
echo json_encode(array(
'type' => 'success',
'msg' => 'Task completed'
));
}
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Incomplete post data'
));
}
}
else {
echo json_encode(array(
'type' => 'error',
'msg' => 'Incomplete post data'
));
}
break;
case "resource": case "resource":
if (isset($_POST['items']) && isset($_POST['attr'])) { if (isset($_POST['items']) && isset($_POST['attr'])) {
$items = (array)json_decode($_POST['items'], true); $items = (array)json_decode($_POST['items'], true);
@ -2531,7 +2744,7 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
$attr = (array)json_decode($_POST['attr'], true); $attr = (array)json_decode($_POST['attr'], true);
$postarray = array_merge(array('domain' => $items), $attr); $postarray = array_merge(array('domain' => $items), $attr);
if (is_array($postarray['domain'])) { if (is_array($postarray['domain'])) {
if (mailbox('edit', 'domain', $postarray) === false) { if (mailbox('edit', 'domain', $postarray)) {
if (isset($_SESSION['return'])) { if (isset($_SESSION['return'])) {
echo json_encode($_SESSION['return']); echo json_encode($_SESSION['return']);
} }
@ -2824,6 +3037,41 @@ if (isset($_SESSION['mailcow_cc_role']) || isset($_SESSION['pending_mailcow_cc_u
)); ));
} }
break; break;
case "ui_texts":
// No items
if (isset($_POST['attr'])) {
$attr = (array)json_decode($_POST['attr'], true);
if (customize('edit', 'ui_texts', $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;
case "self": case "self":
// No items, logged-in user, users and domain admins // No items, logged-in user, users and domain admins
if ($_SESSION['mailcow_cc_role'] == "domainadmin") { if ($_SESSION['mailcow_cc_role'] == "domainadmin") {

View File

@ -5,9 +5,9 @@
$lang['footer']['loading'] = 'Einen Moment bitte...'; $lang['footer']['loading'] = 'Einen Moment bitte...';
$lang['header']['restart_sogo'] = 'SOGo neustarten'; $lang['header']['restart_sogo'] = 'SOGo neustarten';
$lang['footer']['restart_sogo'] = 'SOGo neustarten'; $lang['footer']['restart_container'] = 'Container neustarten';
$lang['footer']['restart_now'] = 'Jetzt neustarten'; $lang['footer']['restart_now'] = 'Jetzt neustarten';
$lang['footer']['restart_sogo_info'] = 'Einige Änderungen an Domains benötigen einen Neustart SOGos. Hier können Sie SOGo neustarten.<br><br><b>Wichtig:</b> Ein korrekter Neustart SOGos kann eine Weile in Anspruch nehmen, bitte warten Sie, bis der Prozess vollständig beendet wurde.'; $lang['footer']['restart_container_info'] = '<b>Wichtig:</b> Ein korrekter Neustart eines Containers kann eine Weile in Anspruch nehmen, bitte warten Sie, bis der Prozess vollständig beendet wurde.';
$lang['footer']['confirm_delete'] = 'Löschen bestätigen'; $lang['footer']['confirm_delete'] = 'Löschen bestätigen';
$lang['footer']['delete_these_items'] = 'Sind Sie sicher, dass die Änderungen an Elementen mit folgender ID durchgeführt werden sollen?'; $lang['footer']['delete_these_items'] = 'Sind Sie sicher, dass die Änderungen an Elementen mit folgender ID durchgeführt werden sollen?';
@ -73,7 +73,7 @@ $lang['danger']['resource_invalid'] = 'Ressourcenname ist ungültig';
$lang['danger']['description_invalid'] = 'Ressourcenbeschreibung ist ungültig'; $lang['danger']['description_invalid'] = 'Ressourcenbeschreibung ist ungültig';
$lang['danger']['mailbox_invalid_suggest'] = 'Mailboxname ist ungültig, meinten Sie vielleicht %s?'; $lang['danger']['mailbox_invalid_suggest'] = 'Mailboxname ist ungültig, meinten Sie vielleicht %s?';
$lang['danger']['is_alias'] = '%s lautet bereits eine Alias-Adresse'; $lang['danger']['is_alias'] = '%s lautet bereits eine Alias-Adresse';
$lang['danger']['is_alias_or_mailbox'] = "Eine Mailbox oder ein Alias mit der Adresse %s ist bereits vorhanden"; $lang['danger']['is_alias_or_mailbox'] = "Eine Mailbox, ein Alias oder eine sich aus einer Alias-Domain ergebende Adresse mit dem Namen %s ist bereits vorhanden";
$lang['danger']['is_spam_alias'] = '%s lautet bereits eine Spam-Alias-Adresse'; $lang['danger']['is_spam_alias'] = '%s lautet bereits eine Spam-Alias-Adresse';
$lang['danger']['quota_not_0_not_numeric'] = 'Speicherplatz muss numerisch und >= 0 sein'; $lang['danger']['quota_not_0_not_numeric'] = 'Speicherplatz muss numerisch und >= 0 sein';
$lang['danger']['domain_not_found'] = 'Domain %s nicht gefunden'; $lang['danger']['domain_not_found'] = 'Domain %s nicht gefunden';
@ -121,6 +121,8 @@ $lang['user']['did_you_know'] = '<b>Wussten Sie schon?</b> Sie können Ihre E-Ma
$lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse'; $lang['user']['spam_aliases'] = 'Temporäre E-Mail Aliasse';
$lang['user']['alias'] = 'Alias'; $lang['user']['alias'] = 'Alias';
$lang['user']['aliases'] = 'Aliasse'; $lang['user']['aliases'] = 'Aliasse';
$lang['user']['shared_aliases'] = 'Geteilte Alias-Adressen';
$lang['user']['direct_aliases'] = 'Direkte Alias-Adressen';
$lang['user']['domain_aliases'] = 'Domain-Alias Adressen'; $lang['user']['domain_aliases'] = 'Domain-Alias Adressen';
$lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)'; $lang['user']['is_catch_all'] = 'Ist Catch-All Adresse für Domain(s)';
$lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als Benutzer'; $lang['user']['aliases_also_send_as'] = 'Darf außerdem versenden als Benutzer';
@ -248,7 +250,7 @@ $lang['mailbox']['target_domain'] = 'Ziel-Domain';
$lang['mailbox']['target_address'] = 'Ziel-Adresse'; $lang['mailbox']['target_address'] = 'Ziel-Adresse';
$lang['mailbox']['username'] = 'Benutzername'; $lang['mailbox']['username'] = 'Benutzername';
$lang['mailbox']['fname'] = 'Name'; $lang['mailbox']['fname'] = 'Name';
$lang['mailbox']['filter_table'] = 'Tabelle filtern'; $lang['mailbox']['filter_table'] = 'Filtern';
$lang['mailbox']['yes'] = '&#10004;'; $lang['mailbox']['yes'] = '&#10004;';
$lang['mailbox']['no'] = '&#10008;'; $lang['mailbox']['no'] = '&#10008;';
$lang['mailbox']['quota'] = 'Speicherplatz'; $lang['mailbox']['quota'] = 'Speicherplatz';
@ -533,6 +535,10 @@ $lang['admin']['host'] = 'Host';
$lang['admin']['source'] = 'Quelle'; $lang['admin']['source'] = 'Quelle';
$lang['admin']['add_forwarding_host'] = 'Weiterleitungs-Host hinzufügen'; $lang['admin']['add_forwarding_host'] = 'Weiterleitungs-Host hinzufügen';
$lang['admin']['add_relayhost'] = 'Relayhost hinzufügen'; $lang['admin']['add_relayhost'] = 'Relayhost hinzufügen';
$lang['admin']['api_allow_from'] = "IP-Adressen für Zugriff";
$lang['admin']['api_key'] = "API-Key";
$lang['admin']['activate_api'] = "API aktivieren";
$lang['admin']['regen_api_key'] = "API-Key regenerieren";
$lang['delete']['remove_forwardinghost_warning'] = '<b>Warnung:</b> Sie entfernen den Weiterleitungs-Host <b>%s</b>!'; $lang['delete']['remove_forwardinghost_warning'] = '<b>Warnung:</b> Sie entfernen den Weiterleitungs-Host <b>%s</b>!';
$lang['success']['forwarding_host_removed'] = "Weiterleitungs-Host %s wurde entfernt"; $lang['success']['forwarding_host_removed'] = "Weiterleitungs-Host %s wurde entfernt";
$lang['success']['forwarding_host_added'] = "Weiterleitungs-Host %s wurde hinzugefügt"; $lang['success']['forwarding_host_added'] = "Weiterleitungs-Host %s wurde hinzugefügt";
@ -560,3 +566,80 @@ $lang['admin']['merged_vars_hint'] = 'Ausgegraute Zeilen wurden aus der Datei <c
$lang['mailbox']['waiting'] = "Wartend"; $lang['mailbox']['waiting'] = "Wartend";
$lang['mailbox']['status'] = "Status"; $lang['mailbox']['status'] = "Status";
$lang['mailbox']['running'] = "In Ausführung"; $lang['mailbox']['running'] = "In Ausführung";
$lang['admin']['ui_texts'] = "UI Label und Texte";
$lang['admin']['help_text'] = "Hilfstext unter Login-Maske (HTML zulässig)";
$lang['admin']['main_name'] = '"mailcow UI" Name';
$lang['admin']['apps_name'] = '"mailcow Apps" Name';
$lang['admin']['customize'] = "UI Anpassung";
$lang['admin']['change_logo'] = "Logo ändern";
$lang['admin']['logo_info'] = "Die hochgeladene Grafik wird für die Navigationsleiste auf eine Höhe von 40px skaliert. Für die Darstellung auf der Login-Maske beträgt die skalierte Breite maximal 250px. Eine frei skalierbare Grafik (etwa SVG) wird empfohlen.";
$lang['admin']['upload'] = "Hochladen";
$lang['admin']['app_links'] = "App Links";
$lang['admin']['app_name'] = "App Name";
$lang['admin']['link'] = "Link";
$lang['admin']['remove_row'] = "Entfernen";
$lang['admin']['add_row'] = "Reihe hinzufügen";
$lang['admin']['reset_default'] = "Zurücksetzen auf Standard";
$lang['admin']['merged_vars_hint'] = 'Ausgegraute Reihen wurden aus der Datei <code>vars.inc.(local.)php</code> gelesen und können hier nicht verändert werden.';
$lang['edit']['tls_policy'] = "TLS Policy ändern";
$lang['edit']['spam_score'] = "Einen benutzerdefiniterten Spam-Score festlegen";
$lang['edit']['spam_policy'] = "Hinzufügen und Entfernen von Einträgen in White- und Blacklists";
$lang['edit']['delimiter_action'] = "Delimiter Aktion verändern";
$lang['edit']['syncjobs'] = "Sync job hinzufügen oder anpassen";
$lang['edit']['eas_reset'] = "ActiveSync Geräte-Cache zurücksetzen";
$lang['edit']['spam_alias'] = "Anpassen temporärer Alias-Adressen";
$lang['danger']['img_tmp_missing'] = "Grafik konnte nicht validiert werden: Erstellung temporärer Datei fehlgeschlagen";
$lang['danger']['img_invalid'] = "Grafik konnte nicht validiert werden";
$lang['danger']['invalid_mime_type'] = "Grafik konnte nicht validiert werden: Ungültiger MIME-Type";
$lang['success']['upload_success'] = "Datei wurde erfolgreich hochgeladen";
$lang['success']['app_links'] = "Änderungen an App Links wurden gespeichert";
$lang['success']['ui_texts'] = "Änderungen an UI-Texten";
$lang['success']['reset_main_logo'] = "Standardgrafik wurde wiederhergestellt";
$lang['success']['items_released'] = "Ausgewählte Objekte wurden an Mailbox versendet";
$lang['danger']['imagick_exception'] = "Fataler Bildverarbeitungsfehler";
$lang['quarantaine']['quarantaine'] = "Quarantäne";
$lang['quarantaine']['qinfo'] = "Das Quarantänesystem speichert abgelehnte Nachrichten in der Datenbank. Die Nachricht wird <em>nicht</em> angekommen. Der Absender erhält keinen Eindruck einer zugestellten E-Mail.<br />
E-Mails mit einer maximalen Größe von 10 MiB werden gespeichert.";
$lang['quarantaine']['release'] = "Freigeben";
$lang['quarantaine']['empty'] = 'Keine Einträge';
$lang['quarantaine']['toggle_all'] = 'Alle auswählen';
$lang['quarantaine']['quick_actions'] = 'Aktionen';
$lang['quarantaine']['remove'] = 'Entfernen';
$lang['quarantaine']['received'] = "Empfangen";
$lang['quarantaine']['action'] = "Aktion";
$lang['quarantaine']['rcpt'] = "Empfänger";
$lang['quarantaine']['qid'] = "Rspamd QID";
$lang['quarantaine']['sender'] = "Sender";
$lang['quarantaine']['show_item'] = "Details";
$lang['quarantaine']['check_hash'] = "Checksumme auf VirusTotal suchen";
$lang['quarantaine']['qitem'] = "Quarantäneeintrag";
$lang['quarantaine']['subj'] = "Betreff";
$lang['quarantaine']['text_plain_content'] = "Inhalt (text/plain)";
$lang['quarantaine']['atts'] = "Anhänge";
$lang['header']['quarantaine'] = "Quarantäne";
$lang['header']['debug'] = "Debugging";
$lang['quarantaine']['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['quarantaine']['release_subject'] = "Potentiell schädliche Nachricht aus Quarantäne: %s";
$lang['mailbox']['bcc_map_type'] = "BCC Typ";
$lang['mailbox']['bcc_type'] = "BCC Typ";
$lang['mailbox']['bcc_sender_map'] = "Senderabhängig";
$lang['mailbox']['bcc_rcpt_map'] = "Empfängerabhängig";
$lang['mailbox']['bcc_local_dest'] = "Lokales Ziel";
$lang['mailbox']['bcc_destinations'] = "BCC Ziel(e)";
$lang['mailbox']['bcc'] = "BCC";
$lang['mailbox']['bcc_maps'] = "BCC-Maps";
$lang['mailbox']['bcc_to_sender'] = "Map senderabhängig verwenden";
$lang['mailbox']['bcc_to_rcpt'] = "Map empfängerabhängig verwenden";
$lang['mailbox']['add_bcc_entry'] = "BCC-Eintrag hinzufügen";
$lang['mailbox']['bcc_info'] = "Eine empfängerabhängige Map wird verwendet, wenn die BCC-Map Eintragung auf den Eingang einer E-Mail auf das lokale Ziel reagieren soll. Senderabhängige Maps verfahren nach dem gleichen Prinzip.<br/>
Das lokale Ziel wird bei Fehlzustellungen an ein BCC-Ziel nicht informiert.";

View File

@ -5,9 +5,9 @@
$lang['footer']['loading'] = "Please wait..."; $lang['footer']['loading'] = "Please wait...";
$lang['header']['restart_sogo'] = 'Restart SOGo'; $lang['header']['restart_sogo'] = 'Restart SOGo';
$lang['footer']['restart_sogo'] = 'Restart SOGo'; $lang['footer']['restart_container'] = 'Restart container';
$lang['footer']['restart_now'] = 'Restart now'; $lang['footer']['restart_now'] = 'Restart now';
$lang['footer']['restart_sogo_info'] = 'Some tasks, e.g. adding a domain, require you to restart SOGo to catch changes made in the mailcow UI.<br><br><b>Important:</b> A graceful restart may take a while to complete, please wait for it to finish.'; $lang['footer']['restart_container_info'] = '<b>Important:</b> A graceful restart may take a while to complete, please wait for it to finish.';
$lang['footer']['confirm_delete'] = 'Confirm deletion'; $lang['footer']['confirm_delete'] = 'Confirm deletion';
$lang['footer']['delete_these_items'] = 'Please confirm your changes to the following object id:'; $lang['footer']['delete_these_items'] = 'Please confirm your changes to the following object id:';
@ -73,7 +73,7 @@ $lang['danger']['description_invalid'] = 'Resource description is invalid';
$lang['danger']['resource_invalid'] = "Resource name is invalid"; $lang['danger']['resource_invalid'] = "Resource name is invalid";
$lang['danger']['mailbox_invalid_suggest'] = 'Mailbox name is invalid, did you mean to type "%s"?'; $lang['danger']['mailbox_invalid_suggest'] = 'Mailbox name is invalid, did you mean to type "%s"?';
$lang['danger']['is_alias'] = "%s is already known as an alias address"; $lang['danger']['is_alias'] = "%s is already known as an alias address";
$lang['danger']['is_alias_or_mailbox'] = "%s is already known as an alias or a mailbox"; $lang['danger']['is_alias_or_mailbox'] = "%s is already known as an alias, a mailbox or an alias address expanded from an alias domain.";
$lang['danger']['is_spam_alias'] = "%s is already known as a spam alias address"; $lang['danger']['is_spam_alias'] = "%s is already known as a spam alias address";
$lang['danger']['quota_not_0_not_numeric'] = "Quota must be numeric and >= 0"; $lang['danger']['quota_not_0_not_numeric'] = "Quota must be numeric and >= 0";
$lang['danger']['domain_not_found'] = 'Domain %s not found'; $lang['danger']['domain_not_found'] = 'Domain %s not found';
@ -121,6 +121,8 @@ $lang['user']['did_you_know'] = '<b>Did you know?</b> You can use tags in your e
$lang['user']['spam_aliases'] = 'Temporary email aliases'; $lang['user']['spam_aliases'] = 'Temporary email aliases';
$lang['user']['alias'] = 'Alias'; $lang['user']['alias'] = 'Alias';
$lang['user']['aliases'] = 'Aliases'; $lang['user']['aliases'] = 'Aliases';
$lang['user']['shared_aliases'] = 'Shared alias addresses';
$lang['user']['direct_aliases'] = 'Direct alias addresses';
$lang['user']['domain_aliases'] = 'Domain alias addresses'; $lang['user']['domain_aliases'] = 'Domain alias addresses';
$lang['user']['is_catch_all'] = 'Catch-all for domain/s'; $lang['user']['is_catch_all'] = 'Catch-all for domain/s';
$lang['user']['aliases_also_send_as'] = 'Also allowed to send as user'; $lang['user']['aliases_also_send_as'] = 'Also allowed to send as user';
@ -550,7 +552,15 @@ $lang['diagnostics']['dns_records_type'] = 'Type';
$lang['diagnostics']['dns_records_data'] = 'Correct Data'; $lang['diagnostics']['dns_records_data'] = 'Correct Data';
$lang['diagnostics']['dns_records_status'] = 'Current State'; $lang['diagnostics']['dns_records_status'] = 'Current State';
$lang['admin']['relay_from'] = '"From:" address'; $lang['admin']['relay_from'] = '"From:" address';
$lang['admin']['relay_run'] = "Run test"; $lang['admin']['api_allow_from'] = "Allow API access from these IPs";
$lang['admin']['api_key'] = "API key";
$lang['admin']['activate_api'] = "Activate API";
$lang['admin']['regen_api_key'] = "Regenerate API key";
$lang['admin']['ui_texts'] = "UI labels and texts";
$lang['admin']['help_text'] = "Override help text below login mask (HTML allowed)";
$lang['admin']['main_name'] = '"mailcow UI" name';
$lang['admin']['apps_name'] = '"mailcow Apps" name';
$lang['admin']['customize'] = "Customize"; $lang['admin']['customize'] = "Customize";
$lang['admin']['change_logo'] = "Change logo"; $lang['admin']['change_logo'] = "Change logo";
@ -574,3 +584,55 @@ $lang['edit']['delimiter_action'] = "Change delimiter action";
$lang['edit']['syncjobs'] = "Add or change sync jobs"; $lang['edit']['syncjobs'] = "Add or change sync jobs";
$lang['edit']['eas_reset'] = "Reset EAS devices"; $lang['edit']['eas_reset'] = "Reset EAS devices";
$lang['edit']['spam_alias'] = "Create or change time limited alias addresses"; $lang['edit']['spam_alias'] = "Create or change time limited alias addresses";
$lang['danger']['img_tmp_missing'] = "Cannot validate image file: Temporary file not found";
$lang['danger']['img_invalid'] = "Cannot validate image file";
$lang['danger']['invalid_mime_type'] = "Invalid mime type";
$lang['success']['upload_success'] = "File uploaded successfully";
$lang['success']['app_links'] = "Saved changes to app links";
$lang['success']['ui_texts'] = "Saved changes to UI texts";
$lang['success']['reset_main_logo'] = "Reset to default logo";
$lang['success']['items_released'] = "Selected items were released";
$lang['danger']['imagick_exception'] = "Error: Imagick exception while reading image";
$lang['quarantaine']['quarantaine'] = "Quarantaine";
$lang['quarantaine']['qinfo'] = "The quarantaine system will save rejected mail to the database, while the sender will <em>not</em> be given the impression of a delivered mail.<br />
Only mails up to 10 MiB will be saved in the quarantaine.";
$lang['quarantaine']['release'] = "Release";
$lang['quarantaine']['empty'] = 'No results';
$lang['quarantaine']['toggle_all'] = 'Toggle all';
$lang['quarantaine']['quick_actions'] = 'Actions';
$lang['quarantaine']['remove'] = 'Remove';
$lang['quarantaine']['received'] = "Received";
$lang['quarantaine']['action'] = "Action";
$lang['quarantaine']['rcpt'] = "Recipient";
$lang['quarantaine']['qid'] = "Rspamd QID";
$lang['quarantaine']['sender'] = "Sender";
$lang['quarantaine']['show_item'] = "Show item";
$lang['quarantaine']['check_hash'] = "Search file hash @ VT";
$lang['quarantaine']['qitem'] = "Quarantaine item";
$lang['quarantaine']['subj'] = "Subject";
$lang['quarantaine']['text_plain_content'] = "Content (text/plain)";
$lang['quarantaine']['atts'] = "Attachments";
$lang['header']['quarantaine'] = "Quarantaine";
$lang['header']['debug'] = "Debug";
$lang['quarantaine']['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['quarantaine']['release_subject'] = "Potentially damaging quarantaine item %s";
$lang['mailbox']['bcc_map_type'] = "BCC type";
$lang['mailbox']['bcc_type'] = "BCC type";
$lang['mailbox']['bcc_sender_map'] = "Sender map";
$lang['mailbox']['bcc_rcpt_map'] = "Recipient map";
$lang['mailbox']['bcc_local_dest'] = "Local destination";
$lang['mailbox']['bcc_destinations'] = "BCC destination/s";
$lang['mailbox']['bcc'] = "BCC";
$lang['mailbox']['bcc_maps'] = "BCC maps";
$lang['mailbox']['bcc_to_sender'] = "Switch to sender map type";
$lang['mailbox']['bcc_to_rcpt'] = "Switch to recipient map type";
$lang['mailbox']['add_bcc_entry'] = "Add BCC map";
$lang['mailbox']['bcc_info'] = "A recipient map type entry is used, when the local destination acts as recipient of a mail. Sender maps conform to the same principle.<br/>
The local destination will not be informed about a failed delivery.";

View File

@ -21,7 +21,7 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
</li> </li>
<li role="presentation"><a href="#tab-syncjobs" aria-controls="tab-syncjobs" role="tab" data-toggle="tab"><?=$lang['mailbox']['sync_jobs'];?></a></li> <li role="presentation"><a href="#tab-syncjobs" aria-controls="tab-syncjobs" role="tab" data-toggle="tab"><?=$lang['mailbox']['sync_jobs'];?></a></li>
<li role="presentation"><a href="#tab-filters" aria-controls="tab-filters" role="tab" data-toggle="tab"><?=$lang['mailbox']['filters'];?></a></li> <li role="presentation"><a href="#tab-filters" aria-controls="tab-filters" role="tab" data-toggle="tab"><?=$lang['mailbox']['filters'];?></a></li>
<li role="presentation"><a href="#tab-bcc" aria-controls="tab-filters" role="tab" data-toggle="tab">BCC maps</a></li> <li role="presentation"><a href="#tab-bcc" aria-controls="tab-filters" role="tab" data-toggle="tab"><?=$lang['mailbox']['bcc_maps'];?></a></li>
</ul> </ul>
<div class="row"> <div class="row">
@ -211,10 +211,9 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<div role="tabpanel" class="tab-pane" id="tab-bcc"> <div role="tabpanel" class="tab-pane" id="tab-bcc">
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">BCC maps</h3> <h3 class="panel-title"><?=$lang['mailbox']['bcc_maps'];?></h3>
</div> </div>
<p style="margin:10px" class="help-block">A recipient map type entry is used, when the local destination acts as recipient of a mail. Sender maps conform to the same principle. <p style="margin:10px" class="help-block"><?=$lang['mailbox']['bcc_info'];?></p>
The local destination will not be informed about a failed delivery.</p>
<div class="table-responsive"> <div class="table-responsive">
<table class="table table-striped" id="bcc_table"></table> <table class="table table-striped" id="bcc_table"></table>
</div> </div>
@ -226,12 +225,12 @@ $_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
<li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li> <li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"active":"1"}' href="#"><?=$lang['mailbox']['activate'];?></a></li>
<li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li> <li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"active":"0"}' href="#"><?=$lang['mailbox']['deactivate'];?></a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>
<li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"type":"sender"}' href="#">Switch to sender map type</a></li> <li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"type":"sender"}' href="#"><?=$lang['mailbox']['bcc_to_sender'];?></a></li>
<li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"type":"rcpt"}' href="#">Switch to recipient map type</a></li> <li><a id="edit_selected" data-id="bcc" data-api-url='edit/bcc' data-api-attr='{"type":"rcpt"}' href="#"><?=$lang['mailbox']['bcc_to_rcpt'];?></a></li>
<li role="separator" class="divider"></li> <li role="separator" class="divider"></li>
<li><a id="delete_selected" data-id="bcc" data-api-url='delete/bcc' href="#"><?=$lang['mailbox']['remove'];?></a></li> <li><a id="delete_selected" data-id="bcc" data-api-url='delete/bcc' href="#"><?=$lang['mailbox']['remove'];?></a></li>
</ul> </ul>
<a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addBCCModalAdmin"><span class="glyphicon glyphicon-plus"></span> Add BCC map</a> <a class="btn btn-sm btn-success" href="#" data-toggle="modal" data-target="#addBCCModalAdmin"><span class="glyphicon glyphicon-plus"></span> <?=$lang['mailbox']['add_bcc_entry'];?></a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -13,7 +13,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<h3 class="modal-title"><?=$lang['admin']['add_domain_admin'];?></h3> <h3 class="modal-title"><?=$lang['admin']['add_domain_admin'];?></h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal" data-id="domain_admin" role="form" method="post"> <form class="form-horizontal" data-cached-form="true" data-id="domain_admin" role="form" method="post">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="username"><?=$lang['admin']['username'];?>:</label> <label class="control-label col-sm-2" for="username"><?=$lang['admin']['username'];?>:</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -71,7 +71,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<h3 class="modal-title"><span class="glyphicon glyphicon-stats"></span> Relayhost</h3> <h3 class="modal-title"><span class="glyphicon glyphicon-stats"></span> Relayhost</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal" id="test_relayhost_form" role="form" method="post"> <form class="form-horizontal" data-cached-form="true" id="test_relayhost_form" role="form" method="post">
<input type="hidden" class="form-control" name="relayhost_id" id="relayhost_id"> <input type="hidden" class="form-control" name="relayhost_id" id="relayhost_id">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="mail_from"><?=$lang['admin']['relay_from'];?></label> <label class="control-label col-sm-2" for="mail_from"><?=$lang['admin']['relay_from'];?></label>

View File

@ -0,0 +1,6 @@
<?php
if (!isset($_SESSION['mailcow_cc_role'])) {
header('Location: /');
exit();
}
?>

View File

@ -49,7 +49,7 @@ if (isset($_SESSION['mailcow_cc_role']) && ($_SESSION['mailcow_cc_role'] == "adm
<input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required> <input type="password" class="form-control" name="confirm_password" id="confirm_password" placeholder="<?=$lang['user']['password_now'];?>" autocomplete="off" required>
</div> </div>
<hr> <hr>
<p><?=$lang['tfa']['waiting_usb_register'];?></p> <p id="u2f_status_reg"></p>
<div class="alert alert-danger" style="display:none" id="u2f_return_code"></div> <div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
<input type="hidden" name="token" id="u2f_register_data"/> <input type="hidden" name="token" id="u2f_register_data"/>
<input type="hidden" name="tfa_method" value="u2f"> <input type="hidden" name="tfa_method" value="u2f">
@ -146,7 +146,7 @@ if (isset($_SESSION['pending_tfa_method'])):
case "u2f": case "u2f":
?> ?>
<form role="form" method="post" id="u2f_auth_form"> <form role="form" method="post" id="u2f_auth_form">
<p><?=$lang['tfa']['waiting_usb_auth'];?></p> <p id="u2f_status_auth"></p>
<div class="alert alert-danger" style="display:none" id="u2f_return_code"></div> <div class="alert alert-danger" style="display:none" id="u2f_return_code"></div>
<input type="hidden" name="token" id="u2f_auth_data"/> <input type="hidden" name="token" id="u2f_auth_data"/>
<input type="hidden" name="tfa_method" value="u2f"> <input type="hidden" name="tfa_method" value="u2f">
@ -183,19 +183,19 @@ if (isset($_SESSION['pending_tfa_method'])):
endif; endif;
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin'): if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'admin'):
?> ?>
<div id="RestartSOGo" class="modal fade" role="dialog"> <div id="RestartContainer" class="modal fade" role="dialog">
<div class="modal-dialog"> <div class="modal-dialog">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button> <button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title"><?= $lang['footer']['restart_sogo']; ?></h4> <h4 class="modal-title"><?= $lang['footer']['restart_container']; ?> (<code id="containerName"></code>)</h4>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p><?= $lang['footer']['restart_sogo_info']; ?></p> <p><?= $lang['footer']['restart_container_info']; ?></p>
<hr> <hr>
<button class="btn btn-md btn-primary" id="triggerRestartSogo"><?= $lang['footer']['restart_now']; ?></button> <button class="btn btn-md btn-primary" id="triggerRestartContainer"><?= $lang['footer']['restart_now']; ?></button>
<br><br> <br><br>
<div id="statusTriggerRestartSogo"></div> <div id="statusTriggerRestartContainer"></div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -13,7 +13,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<h3 class="modal-title"><?=$lang['mailbox']['add_mailbox'];?></h3> <h3 class="modal-title"><?=$lang['mailbox']['add_mailbox'];?></h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal" data-id="add_mailbox" role="form"> <form class="form-horizontal" data-cached-form="true" data-id="add_mailbox" role="form">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="local_part"><?=$lang['add']['mailbox_username'];?></label> <label class="control-label col-sm-2" for="local_part"><?=$lang['add']['mailbox_username'];?></label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -86,7 +86,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<h3 class="modal-title"><?=$lang['mailbox']['add_domain'];?></h3> <h3 class="modal-title"><?=$lang['mailbox']['add_domain'];?></h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal" data-id="add_domain" role="form"> <form class="form-horizontal" data-cached-form="true" data-id="add_domain" role="form">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label> <label class="control-label col-sm-2" for="domain"><?=$lang['add']['domain'];?>:</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -96,7 +96,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label> <label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
<div class="col-sm-10"> <div class="col-sm-10">
<input type="text" class="form-control" name="description" id="description"> <input type="text" class="form-control" name="description" id="description" required>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -161,7 +161,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<h3 class="modal-title"><?=$lang['mailbox']['add_resource'];?></h3> <h3 class="modal-title"><?=$lang['mailbox']['add_resource'];?></h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal" role="form" data-id="add_resource"> <form class="form-horizontal" data-cached-form="true" role="form" data-id="add_resource">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label> <label class="control-label col-sm-2" for="description"><?=$lang['add']['description'];?></label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -223,7 +223,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<h3 class="modal-title"><?=$lang['mailbox']['add_alias'];?></h3> <h3 class="modal-title"><?=$lang['mailbox']['add_alias'];?></h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal" role="form" data-id="add_alias"> <form class="form-horizontal" data-cached-form="true" role="form" data-id="add_alias">
<input type="hidden" value="0" name="active"> <input type="hidden" value="0" name="active">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="address"><?=$lang['add']['alias_address'];?></label> <label class="control-label col-sm-2" for="address"><?=$lang['add']['alias_address'];?></label>
@ -268,7 +268,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<h3 class="modal-title"><?=$lang['mailbox']['add_domain_alias'];?></h3> <h3 class="modal-title"><?=$lang['mailbox']['add_domain_alias'];?></h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal" role="form" data-id="add_alias_domain"> <form class="form-horizontal" data-cached-form="true" role="form" data-id="add_alias_domain">
<input type="hidden" value="0" name="active"> <input type="hidden" value="0" name="active">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="alias_domain"><?=$lang['add']['alias_domain'];?></label> <label class="control-label col-sm-2" for="alias_domain"><?=$lang['add']['alias_domain'];?></label>
@ -316,7 +316,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p class="help-block"><?=$lang['add']['syncjob_hint'];?></p> <p class="help-block"><?=$lang['add']['syncjob_hint'];?></p>
<form class="form-horizontal" role="form" data-id="add_syncjob"> <form class="form-horizontal" data-cached-form="true" role="form" data-id="add_syncjob">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?>:</label> <label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?>:</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -417,13 +417,6 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
</div> </div>
</div> </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="delete2"> <?=$lang['add']['delete2'];?></label>
</div>
</div>
</div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-offset-2 col-sm-10"> <div class="col-sm-offset-2 col-sm-10">
<div class="checkbox"> <div class="checkbox">
@ -450,7 +443,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<h3 class="modal-title">Filter</h3> <h3 class="modal-title">Filter</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal" role="form" data-id="add_filter"> <form class="form-horizontal" data-cached-form="true" role="form" data-id="add_filter">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?>:</label> <label class="control-label col-sm-2" for="username"><?=$lang['add']['username'];?>:</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -515,12 +508,12 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button> <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
<h3 class="modal-title">BCC map</h3> <h3 class="modal-title"><?=$lang['mailbox']['bcc_maps'];?></h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal" role="form" data-id="add_bcc"> <form class="form-horizontal" data-cached-form="true" role="form" data-id="add_bcc">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="local_dest">Local destination:</label> <label class="control-label col-sm-2" for="local_dest"><?=$lang['mailbox']['bcc_local_dest'];?>:</label>
<div class="col-sm-10"> <div class="col-sm-10">
<select id="addSelectLocalDest" name="local_dest" id="local_dest" required> <select id="addSelectLocalDest" name="local_dest" id="local_dest" required>
<?php <?php
@ -549,16 +542,16 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="type">BCC map type:</label> <label class="control-label col-sm-2" for="type"><?=$lang['mailbox']['bcc_map_type'];?>:</label>
<div class="col-sm-10"> <div class="col-sm-10">
<select id="addFBCCType" name="type" id="type" required> <select id="addFBCCType" name="type" id="type" required>
<option value="sender">Sender map</option> <option value="sender"><?=$lang['mailbox']['bcc_sender_map'];?></option>
<option value="rcpt">Recipient map</option> <option value="rcpt"><?=$lang['mailbox']['bcc_rcpt_map'];?></option>
</select> </select>
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="bcc_dest">BCC destination/s:</label> <label class="control-label col-sm-2" for="bcc_dest"><?=$lang['mailbox']['bcc_destinations'];?>:</label>
<div class="col-sm-10"> <div class="col-sm-10">
<textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control" rows="20" id="bcc_dest" name="bcc_dest" required></textarea> <textarea autocorrect="off" spellcheck="false" autocapitalize="none" class="form-control" rows="20" id="bcc_dest" name="bcc_dest" required></textarea>
</div> </div>

View File

@ -0,0 +1,32 @@
<?php
if (!isset($_SESSION['mailcow_cc_role'])) {
header('Location: /');
exit();
}
?>
<div class="modal fade" id="qidDetailModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span></button>
<h3 class="modal-title"><span class="glyphicon glyphicon-info"></span> <?=$lang['quarantaine']['qitem'];?></h3>
</div>
<div class="modal-body">
<div id="qid_error" style="display:none" class="alert alert-danger"></div>
<div class="form-group">
<label for="qid_detail_subj"><h4><?=$lang['quarantaine']['subj'];?>:</h4></label>
<p id="qid_detail_subj"></p>
</div>
<div class="form-group">
<label for="qid_detail_text"><h4><?=$lang['quarantaine']['text_plain_content'];?>:</h4></label>
<pre id="qid_detail_text"></pre>
</div>
<div class="form-group">
<label for="qid_detail_atts"><h4><?=$lang['quarantaine']['atts'];?>:</h4></label>
<div id="qid_detail_atts">-</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -14,7 +14,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
</div> </div>
<div class="modal-body"> <div class="modal-body">
<p><?=$lang['add']['syncjob_hint'];?></p> <p><?=$lang['add']['syncjob_hint'];?></p>
<form class="form-horizontal" role="form" data-id="add_syncjob"> <form class="form-horizontal" data-cached-form="true" role="form" data-id="add_syncjob">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-2" for="host1"><?=$lang['add']['hostname'];?></label> <label class="control-label col-sm-2" for="host1"><?=$lang['add']['hostname'];?></label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -130,7 +130,7 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-body"> <div class="modal-body">
<form class="form-horizontal" data-id="pwchange" role="form" method="post" autocomplete="off"> <form class="form-horizontal" data-cached-form="true" data-id="pwchange" role="form" method="post" autocomplete="off">
<div class="form-group"> <div class="form-group">
<label class="control-label col-sm-3" for="user_new_pass"><?=$lang['user']['new_password'];?></label> <label class="control-label col-sm-3" for="user_new_pass"><?=$lang['user']['new_password'];?></label>
<div class="col-sm-5"> <div class="col-sm-5">

View File

@ -0,0 +1,56 @@
<?php
require_once "inc/prerequisites.inc.php";
if (isset($_SESSION['mailcow_cc_role'])) {
require_once "inc/header.inc.php";
$_SESSION['return_to'] = $_SERVER['REQUEST_URI'];
?>
<div class="container">
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><?=$lang['quarantaine']['quarantaine'];?></h3>
</div>
<p style="margin:10px" class="help-block"><?=$lang['quarantaine']['qinfo'];?></p>
<div class="table-responsive">
<table id="quarantainetable" class="table table-striped"></table>
</div>
<div class="mass-actions-quarantaine">
<div class="btn-group">
<a class="btn btn-sm btn-default" id="toggle_multi_select_all" data-id="qitems" href="#"><span class="glyphicon glyphicon-check" aria-hidden="true"></span> <?=$lang['quarantaine']['toggle_all'];?></a>
<a class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" href="#"><?=$lang['quarantaine']['quick_actions'];?> <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a id="edit_selected" data-id="qitems" data-api-url='edit/qitem' data-api-attr='{"action":"release"}' href="#"><?=$lang['quarantaine']['release'];?></a></li>
<li role="separator" class="divider"></li>
<li><a id="delete_selected" data-id="qitems" data-api-url='delete/qitem' href="#"><?=$lang['quarantaine']['remove'];?></a></li>
</ul>
</div>
</div>
</div>
</div> <!-- /col-md-12 -->
</div> <!-- /row -->
</div> <!-- /container -->
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/modals/quarantaine.php';
?>
<script type='text/javascript'>
<?php
$lang_mailbox = json_encode($lang['quarantaine']);
echo "var lang = ". $lang_mailbox . ";\n";
echo "var csrf_token = '". $_SESSION['CSRF']['TOKEN'] . "';\n";
$role = ($_SESSION['mailcow_cc_role'] == "admin") ? 'admin' : 'domainadmin';
echo "var role = '". $role . "';\n";
echo "var pagination_size = '". $PAGINATION_SIZE . "';\n";
?>
</script>
<script src="js/footable.min.js"></script>
<script src="js/quarantaine.js"></script>
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php';
} else {
header('Location: /');
exit();
}
?>

View File

@ -23,7 +23,7 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
</div> </div>
<hr> <hr>
<div class="row"> <div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?></div> <div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['tfa'];?></div>
<div class="col-sm-9 col-xs-7"> <div class="col-sm-9 col-xs-7">
<p id="tfa_pretty"><?=$tfa_data['pretty'];?></p> <p id="tfa_pretty"><?=$tfa_data['pretty'];?></p>
<div id="tfa_additional"> <div id="tfa_additional">
@ -40,8 +40,8 @@ if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == 'doma
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?></div> <div class="col-sm-3 col-xs-5 text-right"><?=$lang['tfa']['set_tfa'];?></div>
<div class="col-md-9 col-xs-7"> <div class="col-sm-9 col-xs-7">
<select id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>"> <select id="selectTFA" class="selectpicker" title="<?=$lang['tfa']['select'];?>">
<option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option> <option value="yubi_otp"><?=$lang['tfa']['yubi_otp'];?></option>
<option value="u2f"><?=$lang['tfa']['u2f'];?></option> <option value="u2f"><?=$lang['tfa']['u2f'];?></option>
@ -105,11 +105,18 @@ elseif (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == '
$user_get_alias_details = user_get_alias_details($username); $user_get_alias_details = user_get_alias_details($username);
?> ?>
<div class="row"> <div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['aliases'];?>:</div> <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['direct_aliases'];?>:</div>
<div class="col-md-9 col-xs-7"> <div class="col-md-9 col-xs-7">
<p><?=$user_get_alias_details['aliases'];?></p> <p><?=$user_get_alias_details['direct_aliases'];?></p>
</div> </div>
</div> </div>
<div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['shared_aliases'];?>:</div>
<div class="col-md-9 col-xs-7">
<p><?=$user_get_alias_details['shared_aliases'];?></p>
</div>
</div>
<hr>
<div class="row"> <div class="row">
<div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['domain_aliases'];?>:</div> <div class="col-md-3 col-xs-5 text-right"><?=$lang['user']['domain_aliases'];?>:</div>
<div class="col-md-9 col-xs-7"> <div class="col-md-9 col-xs-7">