renormalized line endings with git config core.autocrlf true && git add --renormalize .

This commit is contained in:
Thorbjörn Jörger 2023-03-30 10:13:31 +02:00
parent ae46a877d3
commit b5f49afdba
No known key found for this signature in database
GPG Key ID: 82701519391A1DCD
59 changed files with 19210 additions and 19210 deletions

View File

@ -1,20 +1,20 @@
[supervisord]
nodaemon=true
user=root
pidfile=/var/run/supervisord.pid
[program:syslog-ng]
command=/usr/sbin/syslog-ng --foreground --no-caps
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autostart=true
[program:dovecot]
command=/usr/sbin/dovecot -F
autorestart=true
[eventlistener:processes]
command=/usr/local/sbin/stop-supervisor.sh
events=PROCESS_STATE_STOPPED, PROCESS_STATE_EXITED, PROCESS_STATE_FATAL
[supervisord]
nodaemon=true
user=root
pidfile=/var/run/supervisord.pid
[program:syslog-ng]
command=/usr/sbin/syslog-ng --foreground --no-caps
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
autostart=true
[program:dovecot]
command=/usr/sbin/dovecot -F
autorestart=true
[eventlistener:processes]
command=/usr/local/sbin/stop-supervisor.sh
events=PROCESS_STATE_STOPPED, PROCESS_STATE_EXITED, PROCESS_STATE_FATAL

File diff suppressed because it is too large Load Diff

View File

@ -1,293 +1,293 @@
namespace inbox {
inbox = yes
location =
separator = /
mailbox "Trash" {
auto = subscribe
special_use = \Trash
}
mailbox "Deleted Messages" {
special_use = \Trash
}
mailbox "Deleted Items" {
special_use = \Trash
}
mailbox "Rubbish" {
special_use = \Trash
}
mailbox "Gelöschte Objekte" {
special_use = \Trash
}
mailbox "Gelöschte Elemente" {
special_use = \Trash
}
mailbox "Papierkorb" {
special_use = \Trash
}
mailbox "Itens Excluidos" {
special_use = \Trash
}
mailbox "Itens Excluídos" {
special_use = \Trash
}
mailbox "Lixeira" {
special_use = \Trash
}
mailbox "Prullenbak" {
special_use = \Trash
}
mailbox "Odstránené položky" {
special_use = \Trash
}
mailbox "Koš" {
special_use = \Trash
}
mailbox "Verwijderde items" {
special_use = \Trash
}
mailbox "Удаленные" {
special_use = \Trash
}
mailbox "Удаленные элементы" {
special_use = \Trash
}
mailbox "Корзина" {
special_use = \Trash
}
mailbox "Видалені" {
special_use = \Trash
}
mailbox "Видалені елементи" {
special_use = \Trash
}
mailbox "Кошик" {
special_use = \Trash
}
mailbox "废件箱" {
special_use = \Trash
}
mailbox "已删除消息" {
special_use = \Trash
}
mailbox "已删除邮件" {
special_use = \Trash
}
mailbox "Archive" {
auto = subscribe
special_use = \Archive
}
mailbox "Archiv" {
special_use = \Archive
}
mailbox "Archives" {
special_use = \Archive
}
mailbox "Arquivo" {
special_use = \Archive
}
mailbox "Arquivos" {
special_use = \Archive
}
mailbox "Archief" {
special_use = \Archive
}
mailbox "Archív" {
special_use = \Archive
}
mailbox "Archivovať" {
special_use = \Archive
}
mailbox "归档" {
special_use = \Archive
}
mailbox "Архив" {
special_use = \Archive
}
mailbox "Архів" {
special_use = \Archive
}
mailbox "Sent" {
auto = subscribe
special_use = \Sent
}
mailbox "Sent Messages" {
special_use = \Sent
}
mailbox "Sent Items" {
special_use = \Sent
}
mailbox "已发送" {
special_use = \Sent
}
mailbox "已发送消息" {
special_use = \Sent
}
mailbox "已发送邮件" {
special_use = \Sent
}
mailbox "Отправленные" {
special_use = \Sent
}
mailbox "Отправленные элементы" {
special_use = \Sent
}
mailbox "Надіслані" {
special_use = \Sent
}
mailbox "Надіслані елементи" {
special_use = \Sent
}
mailbox "Gesendet" {
special_use = \Sent
}
mailbox "Gesendete Objekte" {
special_use = \Sent
}
mailbox "Gesendete Elemente" {
special_use = \Sent
}
mailbox "Itens Enviados" {
special_use = \Sent
}
mailbox "Enviados" {
special_use = \Sent
}
mailbox "Verzonden items" {
special_use = \Sent
}
mailbox "Verzonden" {
special_use = \Sent
}
mailbox "Odoslaná pošta" {
special_use = \Sent
}
mailbox "Odoslané" {
special_use = \Sent
}
mailbox "Drafts" {
auto = subscribe
special_use = \Drafts
}
mailbox "Entwürfe" {
special_use = \Drafts
}
mailbox "Rascunhos" {
special_use = \Drafts
}
mailbox "Concepten" {
special_use = \Drafts
}
mailbox "Koncepty" {
special_use = \Drafts
}
mailbox "草稿" {
special_use = \Drafts
}
mailbox "草稿箱" {
special_use = \Drafts
}
mailbox "Черновики" {
special_use = \Drafts
}
mailbox "Чернетки" {
special_use = \Drafts
}
mailbox "Junk" {
auto = subscribe
special_use = \Junk
}
mailbox "Junk-E-Mail" {
special_use = \Junk
}
mailbox "Junk E-Mail" {
special_use = \Junk
}
mailbox "Spam" {
special_use = \Junk
}
mailbox "Lixo Eletrônico" {
special_use = \Junk
}
mailbox "Nevyžiadaná pošta" {
special_use = \Junk
}
mailbox "Infikované položky" {
special_use = \Junk
}
mailbox "Ongewenste e-mail" {
special_use = \Junk
}
mailbox "垃圾" {
special_use = \Junk
}
mailbox "垃圾箱" {
special_use = \Junk
}
mailbox "Нежелательная почта" {
special_use = \Junk
}
mailbox "Спам" {
special_use = \Junk
}
mailbox "Небажана пошта" {
special_use = \Junk
}
mailbox "Koncepty" {
special_use = \Drafts
}
mailbox "Nevyžádaná pošta" {
special_use = \Junk
}
mailbox "Odstraněná pošta" {
special_use = \Trash
}
mailbox "Odeslaná pošta" {
special_use = \Sent
}
mailbox "Skräp" {
special_use = \Trash
}
mailbox "Borttagna Meddelanden" {
special_use = \Trash
}
mailbox "Arkiv" {
special_use = \Archive
}
mailbox "Arkeverat" {
special_use = \Archive
}
mailbox "Skickat" {
special_use = \Sent
}
mailbox "Skickade Meddelanden" {
special_use = \Sent
}
mailbox "Utkast" {
special_use = \Drafts
}
mailbox "Skraldespand" {
special_use = \Trash
}
mailbox "Slettet mails" {
special_use = \Trash
}
mailbox "Arkiv" {
special_use = \Archive
}
mailbox "Arkiveret mails" {
special_use = \Archive
}
mailbox "Sendt" {
special_use = \Sent
}
mailbox "Sendte mails" {
special_use = \Sent
}
mailbox "Udkast" {
special_use = \Drafts
}
mailbox "Kladde" {
special_use = \Drafts
}
prefix =
namespace inbox {
inbox = yes
location =
separator = /
mailbox "Trash" {
auto = subscribe
special_use = \Trash
}
mailbox "Deleted Messages" {
special_use = \Trash
}
mailbox "Deleted Items" {
special_use = \Trash
}
mailbox "Rubbish" {
special_use = \Trash
}
mailbox "Gelöschte Objekte" {
special_use = \Trash
}
mailbox "Gelöschte Elemente" {
special_use = \Trash
}
mailbox "Papierkorb" {
special_use = \Trash
}
mailbox "Itens Excluidos" {
special_use = \Trash
}
mailbox "Itens Excluídos" {
special_use = \Trash
}
mailbox "Lixeira" {
special_use = \Trash
}
mailbox "Prullenbak" {
special_use = \Trash
}
mailbox "Odstránené položky" {
special_use = \Trash
}
mailbox "Koš" {
special_use = \Trash
}
mailbox "Verwijderde items" {
special_use = \Trash
}
mailbox "Удаленные" {
special_use = \Trash
}
mailbox "Удаленные элементы" {
special_use = \Trash
}
mailbox "Корзина" {
special_use = \Trash
}
mailbox "Видалені" {
special_use = \Trash
}
mailbox "Видалені елементи" {
special_use = \Trash
}
mailbox "Кошик" {
special_use = \Trash
}
mailbox "废件箱" {
special_use = \Trash
}
mailbox "已删除消息" {
special_use = \Trash
}
mailbox "已删除邮件" {
special_use = \Trash
}
mailbox "Archive" {
auto = subscribe
special_use = \Archive
}
mailbox "Archiv" {
special_use = \Archive
}
mailbox "Archives" {
special_use = \Archive
}
mailbox "Arquivo" {
special_use = \Archive
}
mailbox "Arquivos" {
special_use = \Archive
}
mailbox "Archief" {
special_use = \Archive
}
mailbox "Archív" {
special_use = \Archive
}
mailbox "Archivovať" {
special_use = \Archive
}
mailbox "归档" {
special_use = \Archive
}
mailbox "Архив" {
special_use = \Archive
}
mailbox "Архів" {
special_use = \Archive
}
mailbox "Sent" {
auto = subscribe
special_use = \Sent
}
mailbox "Sent Messages" {
special_use = \Sent
}
mailbox "Sent Items" {
special_use = \Sent
}
mailbox "已发送" {
special_use = \Sent
}
mailbox "已发送消息" {
special_use = \Sent
}
mailbox "已发送邮件" {
special_use = \Sent
}
mailbox "Отправленные" {
special_use = \Sent
}
mailbox "Отправленные элементы" {
special_use = \Sent
}
mailbox "Надіслані" {
special_use = \Sent
}
mailbox "Надіслані елементи" {
special_use = \Sent
}
mailbox "Gesendet" {
special_use = \Sent
}
mailbox "Gesendete Objekte" {
special_use = \Sent
}
mailbox "Gesendete Elemente" {
special_use = \Sent
}
mailbox "Itens Enviados" {
special_use = \Sent
}
mailbox "Enviados" {
special_use = \Sent
}
mailbox "Verzonden items" {
special_use = \Sent
}
mailbox "Verzonden" {
special_use = \Sent
}
mailbox "Odoslaná pošta" {
special_use = \Sent
}
mailbox "Odoslané" {
special_use = \Sent
}
mailbox "Drafts" {
auto = subscribe
special_use = \Drafts
}
mailbox "Entwürfe" {
special_use = \Drafts
}
mailbox "Rascunhos" {
special_use = \Drafts
}
mailbox "Concepten" {
special_use = \Drafts
}
mailbox "Koncepty" {
special_use = \Drafts
}
mailbox "草稿" {
special_use = \Drafts
}
mailbox "草稿箱" {
special_use = \Drafts
}
mailbox "Черновики" {
special_use = \Drafts
}
mailbox "Чернетки" {
special_use = \Drafts
}
mailbox "Junk" {
auto = subscribe
special_use = \Junk
}
mailbox "Junk-E-Mail" {
special_use = \Junk
}
mailbox "Junk E-Mail" {
special_use = \Junk
}
mailbox "Spam" {
special_use = \Junk
}
mailbox "Lixo Eletrônico" {
special_use = \Junk
}
mailbox "Nevyžiadaná pošta" {
special_use = \Junk
}
mailbox "Infikované položky" {
special_use = \Junk
}
mailbox "Ongewenste e-mail" {
special_use = \Junk
}
mailbox "垃圾" {
special_use = \Junk
}
mailbox "垃圾箱" {
special_use = \Junk
}
mailbox "Нежелательная почта" {
special_use = \Junk
}
mailbox "Спам" {
special_use = \Junk
}
mailbox "Небажана пошта" {
special_use = \Junk
}
mailbox "Koncepty" {
special_use = \Drafts
}
mailbox "Nevyžádaná pošta" {
special_use = \Junk
}
mailbox "Odstraněná pošta" {
special_use = \Trash
}
mailbox "Odeslaná pošta" {
special_use = \Sent
}
mailbox "Skräp" {
special_use = \Trash
}
mailbox "Borttagna Meddelanden" {
special_use = \Trash
}
mailbox "Arkiv" {
special_use = \Archive
}
mailbox "Arkeverat" {
special_use = \Archive
}
mailbox "Skickat" {
special_use = \Sent
}
mailbox "Skickade Meddelanden" {
special_use = \Sent
}
mailbox "Utkast" {
special_use = \Drafts
}
mailbox "Skraldespand" {
special_use = \Trash
}
mailbox "Slettet mails" {
special_use = \Trash
}
mailbox "Arkiv" {
special_use = \Archive
}
mailbox "Arkiveret mails" {
special_use = \Archive
}
mailbox "Sendt" {
special_use = \Sent
}
mailbox "Sendte mails" {
special_use = \Sent
}
mailbox "Udkast" {
special_use = \Drafts
}
mailbox "Kladde" {
special_use = \Drafts
}
prefix =
}

View File

@ -1,174 +1,174 @@
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
error_log("ALIASEXP: " . $e . PHP_EOL);
http_response_code(501);
exit;
}
// Init Redis
$redis = new Redis();
$redis->connect('redis-mailcow', 6379);
function parse_email($email) {
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
}
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
// Read headers
$headers = getallheaders();
// Get rcpt
$rcpt = $headers['Rcpt'];
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
// Parse email address
$parsed_rcpt = parse_email($rcpt);
// Create array of final mailboxes
$rcpt_final_mailboxes = array();
// Skip if not a mailcow handled domain
try {
if (!$redis->hGet('DOMAIN_MAP', $parsed_rcpt['domain'])) {
exit;
}
}
catch (RedisException $e) {
error_log("ALIASEXP: " . $e . PHP_EOL);
http_response_code(504);
exit;
}
// Always assume rcpt is not a final mailbox but an alias for a mailbox or further aliases
//
// rcpt
// |
// mailbox <-- goto ---> alias1, alias2, mailbox2
// | |
// mailbox3 |
// |
// alias3 ---> mailbox4
//
try {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => $rcpt
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => '@' . $parsed_rcpt['domain']
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
}
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :rcpt AND `active` = '1'");
$stmt->execute(array(':rcpt' => $parsed_rcpt['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
$gotos = $parsed_rcpt['local'] . '@' . $goto_branch;
}
}
$gotos_array = explode(',', $gotos);
$loop_c = 0;
while (count($gotos_array) != 0 && $loop_c <= 20) {
// Loop through all found gotos
foreach ($gotos_array as $index => &$goto) {
error_log("ALIAS EXPANDER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND (`active`= '1' OR `active`= '2');");
$stmt->execute(array(':goto' => $goto));
$username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
if (!empty($username)) {
error_log("ALIAS EXPANDER: http pipe: mailbox found: " . $username . PHP_EOL);
// Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
if (!in_array($username, $rcpt_final_mailboxes)) {
$rcpt_final_mailboxes[] = $username;
}
}
else {
$parsed_goto = parse_email($goto);
if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
error_log("ALIAS EXPANDER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
}
else {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
$stmt->execute(array(':goto' => $goto));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if ($goto_branch) {
error_log("ALIAS EXPANDER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = explode(',', $goto_branch);
} else {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` AND '1'");
$stmt->execute(array(':domain' => $parsed_goto['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
error_log("ALIAS EXPANDER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = array($parsed_goto['local'] . '@' . $goto_branch);
}
}
}
}
// goto item was processed, unset
unset($gotos_array[$index]);
}
// Merge goto branch array derived from previous loop (if any), filter duplicates and unset goto branch array
if (!empty($goto_branch_array)) {
$gotos_array = array_unique(array_merge($gotos_array, $goto_branch_array));
unset($goto_branch_array);
}
// Reindex array
$gotos_array = array_values($gotos_array);
// Force exit if loop cannot be solved
// Postfix does not allow for alias loops, so this should never happen.
$loop_c++;
error_log("ALIAS EXPANDER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
}
}
catch (PDOException $e) {
error_log("ALIAS EXPANDER: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}
// Does also return the mailbox name if question == answer (query == mailbox)
if (count($rcpt_final_mailboxes) == 1) {
error_log("ALIASEXP: direct alias " . $rcpt . " expanded to " . $rcpt_final_mailboxes[0] . PHP_EOL);
echo trim($rcpt_final_mailboxes[0]);
}
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
error_log("ALIASEXP: " . $e . PHP_EOL);
http_response_code(501);
exit;
}
// Init Redis
$redis = new Redis();
$redis->connect('redis-mailcow', 6379);
function parse_email($email) {
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
}
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
// Read headers
$headers = getallheaders();
// Get rcpt
$rcpt = $headers['Rcpt'];
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
// Parse email address
$parsed_rcpt = parse_email($rcpt);
// Create array of final mailboxes
$rcpt_final_mailboxes = array();
// Skip if not a mailcow handled domain
try {
if (!$redis->hGet('DOMAIN_MAP', $parsed_rcpt['domain'])) {
exit;
}
}
catch (RedisException $e) {
error_log("ALIASEXP: " . $e . PHP_EOL);
http_response_code(504);
exit;
}
// Always assume rcpt is not a final mailbox but an alias for a mailbox or further aliases
//
// rcpt
// |
// mailbox <-- goto ---> alias1, alias2, mailbox2
// | |
// mailbox3 |
// |
// alias3 ---> mailbox4
//
try {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => $rcpt
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => '@' . $parsed_rcpt['domain']
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
}
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :rcpt AND `active` = '1'");
$stmt->execute(array(':rcpt' => $parsed_rcpt['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
$gotos = $parsed_rcpt['local'] . '@' . $goto_branch;
}
}
$gotos_array = explode(',', $gotos);
$loop_c = 0;
while (count($gotos_array) != 0 && $loop_c <= 20) {
// Loop through all found gotos
foreach ($gotos_array as $index => &$goto) {
error_log("ALIAS EXPANDER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND (`active`= '1' OR `active`= '2');");
$stmt->execute(array(':goto' => $goto));
$username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
if (!empty($username)) {
error_log("ALIAS EXPANDER: http pipe: mailbox found: " . $username . PHP_EOL);
// Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
if (!in_array($username, $rcpt_final_mailboxes)) {
$rcpt_final_mailboxes[] = $username;
}
}
else {
$parsed_goto = parse_email($goto);
if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
error_log("ALIAS EXPANDER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
}
else {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
$stmt->execute(array(':goto' => $goto));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if ($goto_branch) {
error_log("ALIAS EXPANDER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = explode(',', $goto_branch);
} else {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` AND '1'");
$stmt->execute(array(':domain' => $parsed_goto['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
error_log("ALIAS EXPANDER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = array($parsed_goto['local'] . '@' . $goto_branch);
}
}
}
}
// goto item was processed, unset
unset($gotos_array[$index]);
}
// Merge goto branch array derived from previous loop (if any), filter duplicates and unset goto branch array
if (!empty($goto_branch_array)) {
$gotos_array = array_unique(array_merge($gotos_array, $goto_branch_array));
unset($goto_branch_array);
}
// Reindex array
$gotos_array = array_values($gotos_array);
// Force exit if loop cannot be solved
// Postfix does not allow for alias loops, so this should never happen.
$loop_c++;
error_log("ALIAS EXPANDER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
}
}
catch (PDOException $e) {
error_log("ALIAS EXPANDER: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}
// Does also return the mailbox name if question == answer (query == mailbox)
if (count($rcpt_final_mailboxes) == 1) {
error_log("ALIASEXP: direct alias " . $rcpt . " expanded to " . $rcpt_final_mailboxes[0] . PHP_EOL);
echo trim($rcpt_final_mailboxes[0]);
}

View File

@ -1,88 +1,88 @@
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
error_log("BCC MAP SQL ERROR: " . $e . PHP_EOL);
http_response_code(501);
exit;
}
function parse_email($email) {
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
}
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
// Read headers
$headers = getallheaders();
// Get rcpt
$rcpt = $headers['Rcpt'];
// Get from
$from = $headers['From'];
// Remove tags
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
$from = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $from);
try {
if (!empty($rcpt)) {
$stmt = $pdo->prepare("SELECT `bcc_dest` FROM `bcc_maps` WHERE `type` = 'rcpt' AND `local_dest` = :local_dest AND `active` = '1'");
$stmt->execute(array(
':local_dest' => $rcpt
));
$bcc_dest = $stmt->fetch(PDO::FETCH_ASSOC)['bcc_dest'];
if (!empty($bcc_dest) && filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
error_log("BCC MAP: returning ". $bcc_dest . " for " . $rcpt . PHP_EOL);
http_response_code(201);
echo trim($bcc_dest);
exit;
}
}
if (!empty($from)) {
$stmt = $pdo->prepare("SELECT `bcc_dest` FROM `bcc_maps` WHERE `type` = 'sender' AND `local_dest` = :local_dest AND `active` = '1'");
$stmt->execute(array(
':local_dest' => $from
));
$bcc_dest = $stmt->fetch(PDO::FETCH_ASSOC)['bcc_dest'];
if (!empty($bcc_dest) && filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
error_log("BCC MAP: returning ". $bcc_dest . " for " . $from . PHP_EOL);
http_response_code(201);
echo trim($bcc_dest);
exit;
}
}
}
catch (PDOException $e) {
error_log("BCC MAP SQL ERROR: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
error_log("BCC MAP SQL ERROR: " . $e . PHP_EOL);
http_response_code(501);
exit;
}
function parse_email($email) {
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
}
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
// Read headers
$headers = getallheaders();
// Get rcpt
$rcpt = $headers['Rcpt'];
// Get from
$from = $headers['From'];
// Remove tags
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
$from = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $from);
try {
if (!empty($rcpt)) {
$stmt = $pdo->prepare("SELECT `bcc_dest` FROM `bcc_maps` WHERE `type` = 'rcpt' AND `local_dest` = :local_dest AND `active` = '1'");
$stmt->execute(array(
':local_dest' => $rcpt
));
$bcc_dest = $stmt->fetch(PDO::FETCH_ASSOC)['bcc_dest'];
if (!empty($bcc_dest) && filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
error_log("BCC MAP: returning ". $bcc_dest . " for " . $rcpt . PHP_EOL);
http_response_code(201);
echo trim($bcc_dest);
exit;
}
}
if (!empty($from)) {
$stmt = $pdo->prepare("SELECT `bcc_dest` FROM `bcc_maps` WHERE `type` = 'sender' AND `local_dest` = :local_dest AND `active` = '1'");
$stmt->execute(array(
':local_dest' => $from
));
$bcc_dest = $stmt->fetch(PDO::FETCH_ASSOC)['bcc_dest'];
if (!empty($bcc_dest) && filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
error_log("BCC MAP: returning ". $bcc_dest . " for " . $from . PHP_EOL);
http_response_code(201);
echo trim($bcc_dest);
exit;
}
}
}
catch (PDOException $e) {
error_log("BCC MAP SQL ERROR: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}

View File

@ -1,471 +1,471 @@
<?php
/*
The match section performs AND operation on different matches: for example, if you have from and rcpt in the same rule,
then the rule matches only when from AND rcpt match. For similar matches, the OR rule applies: if you have multiple rcpt matches,
then any of these will trigger the rule. If a rule is triggered then no more rules are matched.
*/
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Getting headers sent by the client.
ini_set('error_reporting', 0);
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
$stmt = $pdo->query("SELECT '1' FROM `filterconf`");
}
catch (PDOException $e) {
echo 'settings { }';
exit;
}
// Check if db changed and return header
$stmt = $pdo->prepare("SELECT GREATEST(COALESCE(MAX(UNIX_TIMESTAMP(UPDATE_TIME)), 1), COALESCE(MAX(UNIX_TIMESTAMP(CREATE_TIME)), 1)) AS `db_update_time` FROM `information_schema`.`tables`
WHERE (`TABLE_NAME` = 'filterconf' OR `TABLE_NAME` = 'settingsmap' OR `TABLE_NAME` = 'sogo_quick_contact' OR `TABLE_NAME` = 'alias')
AND TABLE_SCHEMA = :dbname;");
$stmt->execute(array(
':dbname' => $database_name
));
$db_update_time = $stmt->fetch(PDO::FETCH_ASSOC)['db_update_time'];
if (empty($db_update_time)) {
$db_update_time = 1572048000;
}
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && (strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $db_update_time)) {
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $db_update_time).' GMT', true, 304);
exit;
} else {
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $db_update_time).' GMT', true, 200);
}
function parse_email($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr($email, $a));
}
function normalize_email($email) {
$email = strtolower(str_replace('/', '\/', $email));
$gm = "@gmail.com";
if (substr_compare($email, $gm, -strlen($gm)) == 0) {
$email = explode('@', $email);
$email[0] = str_replace('.', '', $email[0]);
$email = implode('@', $email);
}
$gm_alt = "@googlemail.com";
if (substr_compare($email, $gm_alt, -strlen($gm_alt)) == 0) {
$email = explode('@', $email);
$email[0] = str_replace('.', '', $email[0]);
$email[1] = str_replace('@', '', $gm);
$email = implode('@', $email);
}
if (str_contains($email, "+")) {
$email = explode('@', $email);
$user = explode('+', $email[0]);
$email[0] = $user[0];
$email = implode('@', $email);
}
return $email;
}
function wl_by_sogo() {
global $pdo;
$rcpt = array();
$stmt = $pdo->query("SELECT DISTINCT(`sogo_folder_info`.`c_path2`) AS `user`, GROUP_CONCAT(`sogo_quick_contact`.`c_mail`) AS `contacts` FROM `sogo_folder_info`
INNER JOIN `sogo_quick_contact` ON `sogo_quick_contact`.`c_folder_id` = `sogo_folder_info`.`c_folder_id`
GROUP BY `c_path2`");
$sogo_contacts = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($sogo_contacts)) {
foreach (explode(',', $row['contacts']) as $contact) {
if (!filter_var($contact, FILTER_VALIDATE_EMAIL)) {
continue;
}
// Explicit from, no mime_from, no regex - envelope must match
// mailcow white and blacklists also cover mime_from
$rcpt[$row['user']][] = normalize_email($contact);
}
}
return $rcpt;
}
function ucl_rcpts($object, $type) {
global $pdo;
$rcpt = array();
if ($type == 'mailbox') {
// Standard aliases
$stmt = $pdo->prepare("SELECT `address` FROM `alias`
WHERE `goto` = :object_goto
AND `address` NOT LIKE '@%'");
$stmt->execute(array(
':object_goto' => $object
));
$standard_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($standard_aliases)) {
$local = parse_email($row['address'])['local'];
$domain = parse_email($row['address'])['domain'];
if (!empty($local) && !empty($domain)) {
$rcpt[] = '/^' . str_replace('/', '\/', $local) . '[+].*' . str_replace('/', '\/', $domain) . '$/i';
}
$rcpt[] = str_replace('/', '\/', $row['address']);
}
// Aliases by alias domains
$stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `alias` FROM `mailbox`
LEFT OUTER JOIN `alias_domain` ON `mailbox`.`domain` = `alias_domain`.`target_domain`
WHERE `mailbox`.`username` = :object");
$stmt->execute(array(
':object' => $object
));
$by_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
array_filter($by_domain_aliases);
while ($row = array_shift($by_domain_aliases)) {
if (!empty($row['alias'])) {
$local = parse_email($row['alias'])['local'];
$domain = parse_email($row['alias'])['domain'];
if (!empty($local) && !empty($domain)) {
$rcpt[] = '/^' . str_replace('/', '\/', $local) . '[+].*' . str_replace('/', '\/', $domain) . '$/i';
}
$rcpt[] = str_replace('/', '\/', $row['alias']);
}
}
}
elseif ($type == 'domain') {
// Domain self
$rcpt[] = '/.*@' . $object . '/i';
$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
WHERE `target_domain` = :object");
$stmt->execute(array(':object' => $object));
$alias_domains = $stmt->fetchAll(PDO::FETCH_ASSOC);
array_filter($alias_domains);
while ($row = array_shift($alias_domains)) {
$rcpt[] = '/.*@' . $row['alias_domain'] . '/i';
}
}
return $rcpt;
}
?>
settings {
watchdog {
priority = 10;
rcpt_mime = "/null@localhost/i";
from_mime = "/watchdog@localhost/i";
apply "default" {
symbols_disabled = ["HISTORY_SAVE", "ARC", "ARC_SIGNED", "DKIM", "DKIM_SIGNED", "CLAM_VIRUS"];
want_spam = yes;
actions {
reject = 9999.0;
greylist = 9998.0;
"add header" = 9997.0;
}
}
}
<?php
/*
// Start custom scores for users
*/
$stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'highspamlevel' OR `option` = 'lowspamlevel'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
?>
score_<?=$username_sane;?> {
priority = 4;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
$stmt = $pdo->prepare("SELECT `option`, `value` FROM `filterconf`
WHERE (`option` = 'highspamlevel' OR `option` = 'lowspamlevel')
AND `object`= :object");
$stmt->execute(array(':object' => $row['object']));
$spamscore = $stmt->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP);
?>
apply "default" {
actions {
reject = <?=$spamscore['highspamlevel'][0];?>;
greylist = <?=$spamscore['lowspamlevel'][0] - 1;?>;
"add header" = <?=$spamscore['lowspamlevel'][0];?>;
}
}
}
<?php
}
/*
// Start SOGo contacts whitelist
// Priority 4, lower than a domain whitelist (5) and lower than a mailbox whitelist (6)
*/
foreach (wl_by_sogo() as $user => $contacts) {
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $user);
?>
whitelist_sogo_<?=$username_sane;?> {
<?php
foreach ($contacts as $contact) {
?>
from = <?=json_encode($contact, JSON_UNESCAPED_SLASHES);?>;
<?php
}
?>
priority = 4;
<?php
foreach (ucl_rcpts($user, 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
?>
apply "default" {
SOGO_CONTACT = -99.0;
}
symbols [
"SOGO_CONTACT"
]
}
<?php
}
/*
// Start whitelist
*/
$stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'whitelist_from'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
?>
whitelist_<?=$username_sane;?> {
<?php
$list_items = array();
$stmt = $pdo->prepare("SELECT `value` FROM `filterconf`
WHERE `object`= :object
AND `option` = 'whitelist_from'");
$stmt->execute(array(':object' => $row['object']));
$list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($list_items as $item) {
?>
from = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
<?php
}
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
?>
priority = 5;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
else {
?>
priority = 6;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
?>
apply "default" {
MAILCOW_WHITE = -999.0;
}
symbols [
"MAILCOW_WHITE"
]
}
whitelist_mime_<?=$username_sane;?> {
<?php
foreach ($list_items as $item) {
?>
from_mime = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
<?php
}
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
?>
priority = 5;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
else {
?>
priority = 6;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
?>
apply "default" {
MAILCOW_WHITE = -999.0;
}
symbols [
"MAILCOW_WHITE"
]
}
<?php
}
/*
// Start blacklist
*/
$stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'blacklist_from'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
?>
blacklist_<?=$username_sane;?> {
<?php
$list_items = array();
$stmt = $pdo->prepare("SELECT `value` FROM `filterconf`
WHERE `object`= :object
AND `option` = 'blacklist_from'");
$stmt->execute(array(':object' => $row['object']));
$list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($list_items as $item) {
?>
from = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
<?php
}
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
?>
priority = 5;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
else {
?>
priority = 6;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
?>
apply "default" {
MAILCOW_BLACK = 999.0;
}
symbols [
"MAILCOW_BLACK"
]
}
blacklist_header_<?=$username_sane;?> {
<?php
foreach ($list_items as $item) {
?>
from_mime = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
<?php
}
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
?>
priority = 5;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
else {
?>
priority = 6;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
?>
apply "default" {
MAILCOW_BLACK = 999.0;
}
symbols [
"MAILCOW_BLACK"
]
}
<?php
}
/*
// Start traps
*/
?>
ham_trap {
<?php
foreach (ucl_rcpts('ham@localhost', 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
?>
priority = 9;
apply "default" {
symbols_enabled = ["HISTORY_SAVE"];
}
symbols [
"HAM_TRAP"
]
}
spam_trap {
<?php
foreach (ucl_rcpts('spam@localhost', 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
?>
priority = 9;
apply "default" {
symbols_enabled = ["HISTORY_SAVE"];
}
symbols [
"SPAM_TRAP"
]
}
<?php
// Start additional content
$stmt = $pdo->query("SELECT `id`, `content` FROM `settingsmap` WHERE `active` = '1'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['id']);
?>
additional_settings_<?=intval($row['id']);?> {
<?php
$content = preg_split('/\r\n|\r|\n/', $row['content']);
foreach ($content as $line) {
echo ' ' . $line . PHP_EOL;
}
?>
}
<?php
}
?>
}
<?php
/*
The match section performs AND operation on different matches: for example, if you have from and rcpt in the same rule,
then the rule matches only when from AND rcpt match. For similar matches, the OR rule applies: if you have multiple rcpt matches,
then any of these will trigger the rule. If a rule is triggered then no more rules are matched.
*/
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Getting headers sent by the client.
ini_set('error_reporting', 0);
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
$stmt = $pdo->query("SELECT '1' FROM `filterconf`");
}
catch (PDOException $e) {
echo 'settings { }';
exit;
}
// Check if db changed and return header
$stmt = $pdo->prepare("SELECT GREATEST(COALESCE(MAX(UNIX_TIMESTAMP(UPDATE_TIME)), 1), COALESCE(MAX(UNIX_TIMESTAMP(CREATE_TIME)), 1)) AS `db_update_time` FROM `information_schema`.`tables`
WHERE (`TABLE_NAME` = 'filterconf' OR `TABLE_NAME` = 'settingsmap' OR `TABLE_NAME` = 'sogo_quick_contact' OR `TABLE_NAME` = 'alias')
AND TABLE_SCHEMA = :dbname;");
$stmt->execute(array(
':dbname' => $database_name
));
$db_update_time = $stmt->fetch(PDO::FETCH_ASSOC)['db_update_time'];
if (empty($db_update_time)) {
$db_update_time = 1572048000;
}
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && (strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $db_update_time)) {
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $db_update_time).' GMT', true, 304);
exit;
} else {
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $db_update_time).' GMT', true, 200);
}
function parse_email($email) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr($email, $a));
}
function normalize_email($email) {
$email = strtolower(str_replace('/', '\/', $email));
$gm = "@gmail.com";
if (substr_compare($email, $gm, -strlen($gm)) == 0) {
$email = explode('@', $email);
$email[0] = str_replace('.', '', $email[0]);
$email = implode('@', $email);
}
$gm_alt = "@googlemail.com";
if (substr_compare($email, $gm_alt, -strlen($gm_alt)) == 0) {
$email = explode('@', $email);
$email[0] = str_replace('.', '', $email[0]);
$email[1] = str_replace('@', '', $gm);
$email = implode('@', $email);
}
if (str_contains($email, "+")) {
$email = explode('@', $email);
$user = explode('+', $email[0]);
$email[0] = $user[0];
$email = implode('@', $email);
}
return $email;
}
function wl_by_sogo() {
global $pdo;
$rcpt = array();
$stmt = $pdo->query("SELECT DISTINCT(`sogo_folder_info`.`c_path2`) AS `user`, GROUP_CONCAT(`sogo_quick_contact`.`c_mail`) AS `contacts` FROM `sogo_folder_info`
INNER JOIN `sogo_quick_contact` ON `sogo_quick_contact`.`c_folder_id` = `sogo_folder_info`.`c_folder_id`
GROUP BY `c_path2`");
$sogo_contacts = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($sogo_contacts)) {
foreach (explode(',', $row['contacts']) as $contact) {
if (!filter_var($contact, FILTER_VALIDATE_EMAIL)) {
continue;
}
// Explicit from, no mime_from, no regex - envelope must match
// mailcow white and blacklists also cover mime_from
$rcpt[$row['user']][] = normalize_email($contact);
}
}
return $rcpt;
}
function ucl_rcpts($object, $type) {
global $pdo;
$rcpt = array();
if ($type == 'mailbox') {
// Standard aliases
$stmt = $pdo->prepare("SELECT `address` FROM `alias`
WHERE `goto` = :object_goto
AND `address` NOT LIKE '@%'");
$stmt->execute(array(
':object_goto' => $object
));
$standard_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($standard_aliases)) {
$local = parse_email($row['address'])['local'];
$domain = parse_email($row['address'])['domain'];
if (!empty($local) && !empty($domain)) {
$rcpt[] = '/^' . str_replace('/', '\/', $local) . '[+].*' . str_replace('/', '\/', $domain) . '$/i';
}
$rcpt[] = str_replace('/', '\/', $row['address']);
}
// Aliases by alias domains
$stmt = $pdo->prepare("SELECT CONCAT(`local_part`, '@', `alias_domain`.`alias_domain`) AS `alias` FROM `mailbox`
LEFT OUTER JOIN `alias_domain` ON `mailbox`.`domain` = `alias_domain`.`target_domain`
WHERE `mailbox`.`username` = :object");
$stmt->execute(array(
':object' => $object
));
$by_domain_aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
array_filter($by_domain_aliases);
while ($row = array_shift($by_domain_aliases)) {
if (!empty($row['alias'])) {
$local = parse_email($row['alias'])['local'];
$domain = parse_email($row['alias'])['domain'];
if (!empty($local) && !empty($domain)) {
$rcpt[] = '/^' . str_replace('/', '\/', $local) . '[+].*' . str_replace('/', '\/', $domain) . '$/i';
}
$rcpt[] = str_replace('/', '\/', $row['alias']);
}
}
}
elseif ($type == 'domain') {
// Domain self
$rcpt[] = '/.*@' . $object . '/i';
$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain`
WHERE `target_domain` = :object");
$stmt->execute(array(':object' => $object));
$alias_domains = $stmt->fetchAll(PDO::FETCH_ASSOC);
array_filter($alias_domains);
while ($row = array_shift($alias_domains)) {
$rcpt[] = '/.*@' . $row['alias_domain'] . '/i';
}
}
return $rcpt;
}
?>
settings {
watchdog {
priority = 10;
rcpt_mime = "/null@localhost/i";
from_mime = "/watchdog@localhost/i";
apply "default" {
symbols_disabled = ["HISTORY_SAVE", "ARC", "ARC_SIGNED", "DKIM", "DKIM_SIGNED", "CLAM_VIRUS"];
want_spam = yes;
actions {
reject = 9999.0;
greylist = 9998.0;
"add header" = 9997.0;
}
}
}
<?php
/*
// Start custom scores for users
*/
$stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'highspamlevel' OR `option` = 'lowspamlevel'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
?>
score_<?=$username_sane;?> {
priority = 4;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
$stmt = $pdo->prepare("SELECT `option`, `value` FROM `filterconf`
WHERE (`option` = 'highspamlevel' OR `option` = 'lowspamlevel')
AND `object`= :object");
$stmt->execute(array(':object' => $row['object']));
$spamscore = $stmt->fetchAll(PDO::FETCH_COLUMN|PDO::FETCH_GROUP);
?>
apply "default" {
actions {
reject = <?=$spamscore['highspamlevel'][0];?>;
greylist = <?=$spamscore['lowspamlevel'][0] - 1;?>;
"add header" = <?=$spamscore['lowspamlevel'][0];?>;
}
}
}
<?php
}
/*
// Start SOGo contacts whitelist
// Priority 4, lower than a domain whitelist (5) and lower than a mailbox whitelist (6)
*/
foreach (wl_by_sogo() as $user => $contacts) {
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $user);
?>
whitelist_sogo_<?=$username_sane;?> {
<?php
foreach ($contacts as $contact) {
?>
from = <?=json_encode($contact, JSON_UNESCAPED_SLASHES);?>;
<?php
}
?>
priority = 4;
<?php
foreach (ucl_rcpts($user, 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
?>
apply "default" {
SOGO_CONTACT = -99.0;
}
symbols [
"SOGO_CONTACT"
]
}
<?php
}
/*
// Start whitelist
*/
$stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'whitelist_from'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
?>
whitelist_<?=$username_sane;?> {
<?php
$list_items = array();
$stmt = $pdo->prepare("SELECT `value` FROM `filterconf`
WHERE `object`= :object
AND `option` = 'whitelist_from'");
$stmt->execute(array(':object' => $row['object']));
$list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($list_items as $item) {
?>
from = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
<?php
}
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
?>
priority = 5;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
else {
?>
priority = 6;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
?>
apply "default" {
MAILCOW_WHITE = -999.0;
}
symbols [
"MAILCOW_WHITE"
]
}
whitelist_mime_<?=$username_sane;?> {
<?php
foreach ($list_items as $item) {
?>
from_mime = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
<?php
}
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
?>
priority = 5;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
else {
?>
priority = 6;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
?>
apply "default" {
MAILCOW_WHITE = -999.0;
}
symbols [
"MAILCOW_WHITE"
]
}
<?php
}
/*
// Start blacklist
*/
$stmt = $pdo->query("SELECT DISTINCT `object` FROM `filterconf` WHERE `option` = 'blacklist_from'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['object']);
?>
blacklist_<?=$username_sane;?> {
<?php
$list_items = array();
$stmt = $pdo->prepare("SELECT `value` FROM `filterconf`
WHERE `object`= :object
AND `option` = 'blacklist_from'");
$stmt->execute(array(':object' => $row['object']));
$list_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($list_items as $item) {
?>
from = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
<?php
}
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
?>
priority = 5;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
else {
?>
priority = 6;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
?>
apply "default" {
MAILCOW_BLACK = 999.0;
}
symbols [
"MAILCOW_BLACK"
]
}
blacklist_header_<?=$username_sane;?> {
<?php
foreach ($list_items as $item) {
?>
from_mime = "/<?='^' . str_replace('\*', '.*', preg_quote($item['value'], '/')) . '$' ;?>/i";
<?php
}
if (!filter_var(trim($row['object']), FILTER_VALIDATE_EMAIL)) {
?>
priority = 5;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
else {
?>
priority = 6;
<?php
foreach (ucl_rcpts($row['object'], strpos($row['object'], '@') === FALSE ? 'domain' : 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
}
?>
apply "default" {
MAILCOW_BLACK = 999.0;
}
symbols [
"MAILCOW_BLACK"
]
}
<?php
}
/*
// Start traps
*/
?>
ham_trap {
<?php
foreach (ucl_rcpts('ham@localhost', 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
?>
priority = 9;
apply "default" {
symbols_enabled = ["HISTORY_SAVE"];
}
symbols [
"HAM_TRAP"
]
}
spam_trap {
<?php
foreach (ucl_rcpts('spam@localhost', 'mailbox') as $rcpt) {
?>
rcpt = <?=json_encode($rcpt, JSON_UNESCAPED_SLASHES);?>;
<?php
}
?>
priority = 9;
apply "default" {
symbols_enabled = ["HISTORY_SAVE"];
}
symbols [
"SPAM_TRAP"
]
}
<?php
// Start additional content
$stmt = $pdo->query("SELECT `id`, `content` FROM `settingsmap` WHERE `active` = '1'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$username_sane = preg_replace("/[^a-zA-Z0-9]+/", "", $row['id']);
?>
additional_settings_<?=intval($row['id']);?> {
<?php
$content = preg_split('/\r\n|\r|\n/', $row['content']);
foreach ($content as $line) {
echo ' ' . $line . PHP_EOL;
}
?>
}
<?php
}
?>
}

View File

@ -1,260 +1,260 @@
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
error_log("QUARANTINE: " . $e . PHP_EOL);
http_response_code(501);
exit;
}
// Init Redis
$redis = new Redis();
$redis->connect('redis-mailcow', 6379);
// Functions
function parse_email($email) {
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
}
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
$raw_data_content = file_get_contents('php://input');
$raw_data = mb_convert_encoding($raw_data_content, 'HTML-ENTITIES', "UTF-8");
$headers = getallheaders();
$qid = $headers['X-Rspamd-Qid'];
$fuzzy = $headers['X-Rspamd-Fuzzy'];
$subject = $headers['X-Rspamd-Subject'];
$score = $headers['X-Rspamd-Score'];
$rcpts = $headers['X-Rspamd-Rcpt'];
$user = $headers['X-Rspamd-User'];
$ip = $headers['X-Rspamd-Ip'];
$action = $headers['X-Rspamd-Action'];
$sender = $headers['X-Rspamd-From'];
$symbols = $headers['X-Rspamd-Symbols'];
$raw_size = (int)$_SERVER['CONTENT_LENGTH'];
if (empty($sender)) {
error_log("QUARANTINE: Unknown sender, assuming empty-env-from@localhost" . PHP_EOL);
$sender = 'empty-env-from@localhost';
}
if ($fuzzy == 'unknown') {
$fuzzy = '[]';
}
try {
$max_size = (int)$redis->Get('Q_MAX_SIZE');
if (($max_size * 1048576) < $raw_size) {
error_log(sprintf("QUARANTINE: Message too large: %d b exceeds %d b", $raw_size, ($max_size * 1048576)) . PHP_EOL);
http_response_code(505);
exit;
}
if ($exclude_domains = $redis->Get('Q_EXCLUDE_DOMAINS')) {
$exclude_domains = json_decode($exclude_domains, true);
}
$retention_size = (int)$redis->Get('Q_RETENTION_SIZE');
}
catch (RedisException $e) {
error_log("QUARANTINE: " . $e . PHP_EOL);
http_response_code(504);
exit;
}
$rcpt_final_mailboxes = array();
// Loop through all rcpts
foreach (json_decode($rcpts, true) as $rcpt) {
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
// Break rcpt into local part and domain part
$parsed_rcpt = parse_email($rcpt);
// Skip if not a mailcow handled domain
try {
if (!$redis->hGet('DOMAIN_MAP', $parsed_rcpt['domain'])) {
continue;
}
}
catch (RedisException $e) {
error_log("QUARANTINE: " . $e . PHP_EOL);
http_response_code(504);
exit;
}
// Skip if domain is excluded
if (in_array($parsed_rcpt['domain'], $exclude_domains)) {
error_log(sprintf("QUARANTINE: Skipped domain %s", $parsed_rcpt['domain']) . PHP_EOL);
continue;
}
// Always assume rcpt is not a final mailbox but an alias for a mailbox or further aliases
//
// rcpt
// |
// mailbox <-- goto ---> alias1, alias2, mailbox2
// | |
// mailbox3 |
// |
// alias3 ---> mailbox4
//
try {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => $rcpt
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => '@' . $parsed_rcpt['domain']
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
}
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :rcpt AND `active` = '1'");
$stmt->execute(array(':rcpt' => $parsed_rcpt['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
$gotos = $parsed_rcpt['local'] . '@' . $goto_branch;
}
}
$gotos_array = explode(',', $gotos);
$loop_c = 0;
while (count($gotos_array) != 0 && $loop_c <= 20) {
// Loop through all found gotos
foreach ($gotos_array as $index => &$goto) {
error_log("RCPT RESOVLER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND (`active`= '1' OR `active`= '2');");
$stmt->execute(array(':goto' => $goto));
$username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
if (!empty($username)) {
error_log("RCPT RESOVLER: http pipe: mailbox found: " . $username . PHP_EOL);
// Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
if (!in_array($username, $rcpt_final_mailboxes)) {
$rcpt_final_mailboxes[] = $username;
}
}
else {
$parsed_goto = parse_email($goto);
if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
error_log("RCPT RESOVLER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
}
else {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
$stmt->execute(array(':goto' => $goto));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = explode(',', $goto_branch);
} else {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` AND '1'");
$stmt->execute(array(':domain' => $parsed_goto['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = array($parsed_goto['local'] . '@' . $goto_branch);
}
}
}
}
// goto item was processed, unset
unset($gotos_array[$index]);
}
// Merge goto branch array derived from previous loop (if any), filter duplicates and unset goto branch array
if (!empty($goto_branch_array)) {
$gotos_array = array_unique(array_merge($gotos_array, $goto_branch_array));
unset($goto_branch_array);
}
// Reindex array
$gotos_array = array_values($gotos_array);
// Force exit if loop cannot be solved
// Postfix does not allow for alias loops, so this should never happen.
$loop_c++;
error_log("RCPT RESOVLER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
}
}
catch (PDOException $e) {
error_log("RCPT RESOVLER: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}
}
foreach ($rcpt_final_mailboxes as $rcpt_final) {
error_log("QUARANTINE: quarantine pipe: processing quarantine message for rcpt " . $rcpt_final . PHP_EOL);
try {
$stmt = $pdo->prepare("INSERT INTO `quarantine` (`qid`, `subject`, `score`, `sender`, `rcpt`, `symbols`, `user`, `ip`, `msg`, `action`, `fuzzy_hashes`)
VALUES (:qid, :subject, :score, :sender, :rcpt, :symbols, :user, :ip, :msg, :action, :fuzzy_hashes)");
$stmt->execute(array(
':qid' => $qid,
':subject' => $subject,
':score' => $score,
':sender' => $sender,
':rcpt' => $rcpt_final,
':symbols' => $symbols,
':user' => $user,
':ip' => $ip,
':msg' => $raw_data,
':action' => $action,
':fuzzy_hashes' => $fuzzy
));
$stmt = $pdo->prepare('DELETE FROM `quarantine` WHERE `rcpt` = :rcpt AND `id` NOT IN (
SELECT `id`
FROM (
SELECT `id`
FROM `quarantine`
WHERE `rcpt` = :rcpt2
ORDER BY id DESC
LIMIT :retention_size
) x
);');
$stmt->execute(array(
':rcpt' => $rcpt_final,
':rcpt2' => $rcpt_final,
':retention_size' => $retention_size
));
}
catch (PDOException $e) {
error_log("QUARANTINE: " . $e->getMessage() . PHP_EOL);
http_response_code(503);
exit;
}
}
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
error_log("QUARANTINE: " . $e . PHP_EOL);
http_response_code(501);
exit;
}
// Init Redis
$redis = new Redis();
$redis->connect('redis-mailcow', 6379);
// Functions
function parse_email($email) {
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
}
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
$raw_data_content = file_get_contents('php://input');
$raw_data = mb_convert_encoding($raw_data_content, 'HTML-ENTITIES', "UTF-8");
$headers = getallheaders();
$qid = $headers['X-Rspamd-Qid'];
$fuzzy = $headers['X-Rspamd-Fuzzy'];
$subject = $headers['X-Rspamd-Subject'];
$score = $headers['X-Rspamd-Score'];
$rcpts = $headers['X-Rspamd-Rcpt'];
$user = $headers['X-Rspamd-User'];
$ip = $headers['X-Rspamd-Ip'];
$action = $headers['X-Rspamd-Action'];
$sender = $headers['X-Rspamd-From'];
$symbols = $headers['X-Rspamd-Symbols'];
$raw_size = (int)$_SERVER['CONTENT_LENGTH'];
if (empty($sender)) {
error_log("QUARANTINE: Unknown sender, assuming empty-env-from@localhost" . PHP_EOL);
$sender = 'empty-env-from@localhost';
}
if ($fuzzy == 'unknown') {
$fuzzy = '[]';
}
try {
$max_size = (int)$redis->Get('Q_MAX_SIZE');
if (($max_size * 1048576) < $raw_size) {
error_log(sprintf("QUARANTINE: Message too large: %d b exceeds %d b", $raw_size, ($max_size * 1048576)) . PHP_EOL);
http_response_code(505);
exit;
}
if ($exclude_domains = $redis->Get('Q_EXCLUDE_DOMAINS')) {
$exclude_domains = json_decode($exclude_domains, true);
}
$retention_size = (int)$redis->Get('Q_RETENTION_SIZE');
}
catch (RedisException $e) {
error_log("QUARANTINE: " . $e . PHP_EOL);
http_response_code(504);
exit;
}
$rcpt_final_mailboxes = array();
// Loop through all rcpts
foreach (json_decode($rcpts, true) as $rcpt) {
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
// Break rcpt into local part and domain part
$parsed_rcpt = parse_email($rcpt);
// Skip if not a mailcow handled domain
try {
if (!$redis->hGet('DOMAIN_MAP', $parsed_rcpt['domain'])) {
continue;
}
}
catch (RedisException $e) {
error_log("QUARANTINE: " . $e . PHP_EOL);
http_response_code(504);
exit;
}
// Skip if domain is excluded
if (in_array($parsed_rcpt['domain'], $exclude_domains)) {
error_log(sprintf("QUARANTINE: Skipped domain %s", $parsed_rcpt['domain']) . PHP_EOL);
continue;
}
// Always assume rcpt is not a final mailbox but an alias for a mailbox or further aliases
//
// rcpt
// |
// mailbox <-- goto ---> alias1, alias2, mailbox2
// | |
// mailbox3 |
// |
// alias3 ---> mailbox4
//
try {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => $rcpt
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => '@' . $parsed_rcpt['domain']
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
}
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :rcpt AND `active` = '1'");
$stmt->execute(array(':rcpt' => $parsed_rcpt['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
$gotos = $parsed_rcpt['local'] . '@' . $goto_branch;
}
}
$gotos_array = explode(',', $gotos);
$loop_c = 0;
while (count($gotos_array) != 0 && $loop_c <= 20) {
// Loop through all found gotos
foreach ($gotos_array as $index => &$goto) {
error_log("RCPT RESOVLER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND (`active`= '1' OR `active`= '2');");
$stmt->execute(array(':goto' => $goto));
$username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
if (!empty($username)) {
error_log("RCPT RESOVLER: http pipe: mailbox found: " . $username . PHP_EOL);
// Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
if (!in_array($username, $rcpt_final_mailboxes)) {
$rcpt_final_mailboxes[] = $username;
}
}
else {
$parsed_goto = parse_email($goto);
if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
error_log("RCPT RESOVLER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
}
else {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
$stmt->execute(array(':goto' => $goto));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = explode(',', $goto_branch);
} else {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` AND '1'");
$stmt->execute(array(':domain' => $parsed_goto['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = array($parsed_goto['local'] . '@' . $goto_branch);
}
}
}
}
// goto item was processed, unset
unset($gotos_array[$index]);
}
// Merge goto branch array derived from previous loop (if any), filter duplicates and unset goto branch array
if (!empty($goto_branch_array)) {
$gotos_array = array_unique(array_merge($gotos_array, $goto_branch_array));
unset($goto_branch_array);
}
// Reindex array
$gotos_array = array_values($gotos_array);
// Force exit if loop cannot be solved
// Postfix does not allow for alias loops, so this should never happen.
$loop_c++;
error_log("RCPT RESOVLER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
}
}
catch (PDOException $e) {
error_log("RCPT RESOVLER: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}
}
foreach ($rcpt_final_mailboxes as $rcpt_final) {
error_log("QUARANTINE: quarantine pipe: processing quarantine message for rcpt " . $rcpt_final . PHP_EOL);
try {
$stmt = $pdo->prepare("INSERT INTO `quarantine` (`qid`, `subject`, `score`, `sender`, `rcpt`, `symbols`, `user`, `ip`, `msg`, `action`, `fuzzy_hashes`)
VALUES (:qid, :subject, :score, :sender, :rcpt, :symbols, :user, :ip, :msg, :action, :fuzzy_hashes)");
$stmt->execute(array(
':qid' => $qid,
':subject' => $subject,
':score' => $score,
':sender' => $sender,
':rcpt' => $rcpt_final,
':symbols' => $symbols,
':user' => $user,
':ip' => $ip,
':msg' => $raw_data,
':action' => $action,
':fuzzy_hashes' => $fuzzy
));
$stmt = $pdo->prepare('DELETE FROM `quarantine` WHERE `rcpt` = :rcpt AND `id` NOT IN (
SELECT `id`
FROM (
SELECT `id`
FROM `quarantine`
WHERE `rcpt` = :rcpt2
ORDER BY id DESC
LIMIT :retention_size
) x
);');
$stmt->execute(array(
':rcpt' => $rcpt_final,
':rcpt2' => $rcpt_final,
':retention_size' => $retention_size
));
}
catch (PDOException $e) {
error_log("QUARANTINE: " . $e->getMessage() . PHP_EOL);
http_response_code(503);
exit;
}
}

View File

@ -1,48 +1,48 @@
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init Redis
$redis = new Redis();
try {
if (!empty(getenv('REDIS_SLAVEOF_IP'))) {
$redis->connect(getenv('REDIS_SLAVEOF_IP'), getenv('REDIS_SLAVEOF_PORT'));
}
else {
$redis->connect('redis-mailcow', 6379);
}
}
catch (Exception $e) {
exit;
}
$raw_data_content = file_get_contents('php://input');
$raw_data_decoded = json_decode($raw_data_content, true);
$data['time'] = time();
$data['rcpt'] = implode(', ', $raw_data_decoded['rcpt']);
$data['from'] = $raw_data_decoded['from'];
$data['user'] = $raw_data_decoded['user'];
$symbol_rl_key = array_search('RATELIMITED', array_column($raw_data_decoded['symbols'], 'name'));
$data['rl_info'] = implode($raw_data_decoded['symbols'][$symbol_rl_key]['options']);
preg_match('/(.+)\((.+)\)/i', $data['rl_info'], $rl_matches);
if (!empty($rl_matches[1]) && !empty($rl_matches[2])) {
$data['rl_name'] = $rl_matches[1];
$data['rl_hash'] = $rl_matches[2];
}
else {
$data['rl_name'] = 'err';
$data['rl_hash'] = 'err';
}
$data['qid'] = $raw_data_decoded['qid'];
$data['ip'] = $raw_data_decoded['ip'];
$data['message_id'] = $raw_data_decoded['message_id'];
$data['header_subject'] = implode(' ', $raw_data_decoded['header_subject']);
$data['header_from'] = implode(', ', $raw_data_decoded['header_from']);
$redis->lpush('RL_LOG', json_encode($data));
exit;
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init Redis
$redis = new Redis();
try {
if (!empty(getenv('REDIS_SLAVEOF_IP'))) {
$redis->connect(getenv('REDIS_SLAVEOF_IP'), getenv('REDIS_SLAVEOF_PORT'));
}
else {
$redis->connect('redis-mailcow', 6379);
}
}
catch (Exception $e) {
exit;
}
$raw_data_content = file_get_contents('php://input');
$raw_data_decoded = json_decode($raw_data_content, true);
$data['time'] = time();
$data['rcpt'] = implode(', ', $raw_data_decoded['rcpt']);
$data['from'] = $raw_data_decoded['from'];
$data['user'] = $raw_data_decoded['user'];
$symbol_rl_key = array_search('RATELIMITED', array_column($raw_data_decoded['symbols'], 'name'));
$data['rl_info'] = implode($raw_data_decoded['symbols'][$symbol_rl_key]['options']);
preg_match('/(.+)\((.+)\)/i', $data['rl_info'], $rl_matches);
if (!empty($rl_matches[1]) && !empty($rl_matches[2])) {
$data['rl_name'] = $rl_matches[1];
$data['rl_hash'] = $rl_matches[2];
}
else {
$data['rl_name'] = 'err';
$data['rl_hash'] = 'err';
}
$data['qid'] = $raw_data_decoded['qid'];
$data['ip'] = $raw_data_decoded['ip'];
$data['message_id'] = $raw_data_decoded['message_id'];
$data['header_subject'] = implode(' ', $raw_data_decoded['header_subject']);
$data['header_from'] = implode(', ', $raw_data_decoded['header_from']);
$redis->lpush('RL_LOG', json_encode($data));
exit;

View File

@ -1,275 +1,275 @@
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
error_log("NOTIFY: " . $e . PHP_EOL);
http_response_code(501);
exit;
}
// Init Redis
$redis = new Redis();
$redis->connect('redis-mailcow', 6379);
// Functions
function parse_email($email) {
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
}
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
$headers = getallheaders();
$json_body = json_decode(file_get_contents('php://input'));
$qid = $headers['X-Rspamd-Qid'];
$rcpts = $headers['X-Rspamd-Rcpt'];
$sender = $headers['X-Rspamd-From'];
$ip = $headers['X-Rspamd-Ip'];
$subject = $headers['X-Rspamd-Subject'];
$messageid= $json_body->message_id;
$priority = 0;
$symbols_array = json_decode($headers['X-Rspamd-Symbols'], true);
if (is_array($symbols_array)) {
foreach ($symbols_array as $symbol) {
if ($symbol['name'] == 'HAS_X_PRIO_ONE') {
$priority = 1;
break;
}
}
}
$sender_address = $json_body->header_from[0];
$sender_name = '-';
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $sender_address, $matches)) {
$sender_address = $matches['address'];
$sender_name = trim($matches['name'], '"\' ');
}
$to_address = $json_body->header_to[0];
$to_name = '-';
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $to_address, $matches)) {
$to_address = $matches['address'];
$to_name = trim($matches['name'], '"\' ');
}
$rcpt_final_mailboxes = array();
// Loop through all rcpts
foreach (json_decode($rcpts, true) as $rcpt) {
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
// Break rcpt into local part and domain part
$parsed_rcpt = parse_email($rcpt);
// Skip if not a mailcow handled domain
try {
if (!$redis->hGet('DOMAIN_MAP', $parsed_rcpt['domain'])) {
continue;
}
}
catch (RedisException $e) {
error_log("NOTIFY: " . $e . PHP_EOL);
http_response_code(504);
exit;
}
// Always assume rcpt is not a final mailbox but an alias for a mailbox or further aliases
//
// rcpt
// |
// mailbox <-- goto ---> alias1, alias2, mailbox2
// | |
// mailbox3 |
// |
// alias3 ---> mailbox4
//
try {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => $rcpt
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => '@' . $parsed_rcpt['domain']
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
}
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :rcpt AND `active` = '1'");
$stmt->execute(array(':rcpt' => $parsed_rcpt['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
$gotos = $parsed_rcpt['local'] . '@' . $goto_branch;
}
}
$gotos_array = explode(',', $gotos);
$loop_c = 0;
while (count($gotos_array) != 0 && $loop_c <= 20) {
// Loop through all found gotos
foreach ($gotos_array as $index => &$goto) {
error_log("RCPT RESOVLER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND (`active`= '1' OR `active`= '2');");
$stmt->execute(array(':goto' => $goto));
$username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
if (!empty($username)) {
error_log("RCPT RESOVLER: http pipe: mailbox found: " . $username . PHP_EOL);
// Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
if (!in_array($username, $rcpt_final_mailboxes)) {
$rcpt_final_mailboxes[] = $username;
}
}
else {
$parsed_goto = parse_email($goto);
if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
error_log("RCPT RESOVLER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
}
else {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
$stmt->execute(array(':goto' => $goto));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = explode(',', $goto_branch);
} else {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` AND '1'");
$stmt->execute(array(':domain' => $parsed_goto['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = array($parsed_goto['local'] . '@' . $goto_branch);
}
}
}
}
// goto item was processed, unset
unset($gotos_array[$index]);
}
// Merge goto branch array derived from previous loop (if any), filter duplicates and unset goto branch array
if (!empty($goto_branch_array)) {
$gotos_array = array_unique(array_merge($gotos_array, $goto_branch_array));
unset($goto_branch_array);
}
// Reindex array
$gotos_array = array_values($gotos_array);
// Force exit if loop cannot be solved
// Postfix does not allow for alias loops, so this should never happen.
$loop_c++;
error_log("RCPT RESOVLER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
}
}
catch (PDOException $e) {
error_log("RCPT RESOVLER: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}
}
foreach ($rcpt_final_mailboxes as $rcpt_final) {
error_log("NOTIFY: pushover pipe: processing pushover message for rcpt " . $rcpt_final . PHP_EOL);
$stmt = $pdo->prepare("SELECT * FROM `pushover`
WHERE `username` = :username AND `active` = '1'");
$stmt->execute(array(
':username' => $rcpt_final
));
$api_data = $stmt->fetch(PDO::FETCH_ASSOC);
if (isset($api_data['key']) && isset($api_data['token'])) {
$title = (!empty($api_data['title'])) ? $api_data['title'] : 'Mail';
$text = (!empty($api_data['text'])) ? $api_data['text'] : 'You\'ve got mail 📧';
$attributes = json_decode($api_data['attributes'], true);
$senders = explode(',', $api_data['senders']);
$senders = array_filter($senders);
$senders_regex = $api_data['senders_regex'];
$sender_validated = false;
if (empty($senders) && empty($senders_regex)) {
$sender_validated = true;
}
else {
if (!empty($senders)) {
if (in_array($sender, $senders)) {
$sender_validated = true;
}
}
if (!empty($senders_regex) && $sender_validated !== true) {
if (preg_match($senders_regex, $sender)) {
$sender_validated = true;
}
}
}
if ($sender_validated === false) {
error_log("NOTIFY: pushover pipe: skipping unwanted sender " . $sender);
continue;
}
if ($attributes['only_x_prio'] == "1" && $priority == 0) {
error_log("NOTIFY: pushover pipe: mail has no X-Priority: 1 header, skipping");
continue;
}
$post_fields = array(
"token" => $api_data['token'],
"user" => $api_data['key'],
"title" => sprintf("%s", str_replace(
array('{SUBJECT}', '{SENDER}', '{SENDER_NAME}', '{SENDER_ADDRESS}', '{TO_NAME}', '{TO_ADDRESS}', '{MSG_ID}'),
array($subject, $sender, $sender_name, $sender_address, $to_name, $to_address, $messageid), $title)
),
"priority" => $priority,
"message" => sprintf("%s", str_replace(
array('{SUBJECT}', '{SENDER}', '{SENDER_NAME}', '{SENDER_ADDRESS}', '{TO_NAME}', '{TO_ADDRESS}', '{MSG_ID}', '\n'),
array($subject, $sender, $sender_name, $sender_address, $to_name, $to_address, $messageid, PHP_EOL), $text)
),
"sound" => $attributes['sound'] ?? "pushover"
);
if ($attributes['evaluate_x_prio'] == "1" && $priority == 1) {
$post_fields['expire'] = 600;
$post_fields['retry'] = 120;
$post_fields['priority'] = 2;
}
curl_setopt_array($ch = curl_init(), array(
CURLOPT_URL => "https://api.pushover.net/1/messages.json",
CURLOPT_POSTFIELDS => $post_fields,
CURLOPT_SAFE_UPLOAD => true,
CURLOPT_RETURNTRANSFER => true,
));
$result = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
error_log("NOTIFY: result: " . $httpcode . PHP_EOL);
}
}
<?php
// File size is limited by Nginx site to 10M
// To speed things up, we do not include prerequisites
header('Content-Type: text/plain');
require_once "vars.inc.php";
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init database
//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
$opt = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $database_user, $database_pass, $opt);
}
catch (PDOException $e) {
error_log("NOTIFY: " . $e . PHP_EOL);
http_response_code(501);
exit;
}
// Init Redis
$redis = new Redis();
$redis->connect('redis-mailcow', 6379);
// Functions
function parse_email($email) {
if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
$a = strrpos($email, '@');
return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
}
if (!function_exists('getallheaders')) {
function getallheaders() {
if (!is_array($_SERVER)) {
return array();
}
$headers = array();
foreach ($_SERVER as $name => $value) {
if (substr($name, 0, 5) == 'HTTP_') {
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
}
}
return $headers;
}
}
$headers = getallheaders();
$json_body = json_decode(file_get_contents('php://input'));
$qid = $headers['X-Rspamd-Qid'];
$rcpts = $headers['X-Rspamd-Rcpt'];
$sender = $headers['X-Rspamd-From'];
$ip = $headers['X-Rspamd-Ip'];
$subject = $headers['X-Rspamd-Subject'];
$messageid= $json_body->message_id;
$priority = 0;
$symbols_array = json_decode($headers['X-Rspamd-Symbols'], true);
if (is_array($symbols_array)) {
foreach ($symbols_array as $symbol) {
if ($symbol['name'] == 'HAS_X_PRIO_ONE') {
$priority = 1;
break;
}
}
}
$sender_address = $json_body->header_from[0];
$sender_name = '-';
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $sender_address, $matches)) {
$sender_address = $matches['address'];
$sender_name = trim($matches['name'], '"\' ');
}
$to_address = $json_body->header_to[0];
$to_name = '-';
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $to_address, $matches)) {
$to_address = $matches['address'];
$to_name = trim($matches['name'], '"\' ');
}
$rcpt_final_mailboxes = array();
// Loop through all rcpts
foreach (json_decode($rcpts, true) as $rcpt) {
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
// Break rcpt into local part and domain part
$parsed_rcpt = parse_email($rcpt);
// Skip if not a mailcow handled domain
try {
if (!$redis->hGet('DOMAIN_MAP', $parsed_rcpt['domain'])) {
continue;
}
}
catch (RedisException $e) {
error_log("NOTIFY: " . $e . PHP_EOL);
http_response_code(504);
exit;
}
// Always assume rcpt is not a final mailbox but an alias for a mailbox or further aliases
//
// rcpt
// |
// mailbox <-- goto ---> alias1, alias2, mailbox2
// | |
// mailbox3 |
// |
// alias3 ---> mailbox4
//
try {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => $rcpt
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :rcpt AND `active` = '1'");
$stmt->execute(array(
':rcpt' => '@' . $parsed_rcpt['domain']
));
$gotos = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
}
if (empty($gotos)) {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :rcpt AND `active` = '1'");
$stmt->execute(array(':rcpt' => $parsed_rcpt['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
$gotos = $parsed_rcpt['local'] . '@' . $goto_branch;
}
}
$gotos_array = explode(',', $gotos);
$loop_c = 0;
while (count($gotos_array) != 0 && $loop_c <= 20) {
// Loop through all found gotos
foreach ($gotos_array as $index => &$goto) {
error_log("RCPT RESOVLER: http pipe: query " . $goto . " as username from mailbox" . PHP_EOL);
$stmt = $pdo->prepare("SELECT `username` FROM `mailbox` WHERE `username` = :goto AND (`active`= '1' OR `active`= '2');");
$stmt->execute(array(':goto' => $goto));
$username = $stmt->fetch(PDO::FETCH_ASSOC)['username'];
if (!empty($username)) {
error_log("RCPT RESOVLER: http pipe: mailbox found: " . $username . PHP_EOL);
// Current goto is a mailbox, save to rcpt_final_mailboxes if not a duplicate
if (!in_array($username, $rcpt_final_mailboxes)) {
$rcpt_final_mailboxes[] = $username;
}
}
else {
$parsed_goto = parse_email($goto);
if (!$redis->hGet('DOMAIN_MAP', $parsed_goto['domain'])) {
error_log("RCPT RESOVLER:" . $goto . " is not a mailcow handled mailbox or alias address" . PHP_EOL);
}
else {
$stmt = $pdo->prepare("SELECT `goto` FROM `alias` WHERE `address` = :goto AND `active` = '1'");
$stmt->execute(array(':goto' => $goto));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['goto'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto address " . $goto . " is an alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = explode(',', $goto_branch);
} else {
$stmt = $pdo->prepare("SELECT `target_domain` FROM `alias_domain` WHERE `alias_domain` = :domain AND `active` AND '1'");
$stmt->execute(array(':domain' => $parsed_goto['domain']));
$goto_branch = $stmt->fetch(PDO::FETCH_ASSOC)['target_domain'];
if ($goto_branch) {
error_log("RCPT RESOVLER: http pipe: goto domain " . $parsed_goto['domain'] . " is a domain alias branch for " . $goto_branch . PHP_EOL);
$goto_branch_array = array($parsed_goto['local'] . '@' . $goto_branch);
}
}
}
}
// goto item was processed, unset
unset($gotos_array[$index]);
}
// Merge goto branch array derived from previous loop (if any), filter duplicates and unset goto branch array
if (!empty($goto_branch_array)) {
$gotos_array = array_unique(array_merge($gotos_array, $goto_branch_array));
unset($goto_branch_array);
}
// Reindex array
$gotos_array = array_values($gotos_array);
// Force exit if loop cannot be solved
// Postfix does not allow for alias loops, so this should never happen.
$loop_c++;
error_log("RCPT RESOVLER: http pipe: goto array count on loop #". $loop_c . " is " . count($gotos_array) . PHP_EOL);
}
}
catch (PDOException $e) {
error_log("RCPT RESOVLER: " . $e->getMessage() . PHP_EOL);
http_response_code(502);
exit;
}
}
foreach ($rcpt_final_mailboxes as $rcpt_final) {
error_log("NOTIFY: pushover pipe: processing pushover message for rcpt " . $rcpt_final . PHP_EOL);
$stmt = $pdo->prepare("SELECT * FROM `pushover`
WHERE `username` = :username AND `active` = '1'");
$stmt->execute(array(
':username' => $rcpt_final
));
$api_data = $stmt->fetch(PDO::FETCH_ASSOC);
if (isset($api_data['key']) && isset($api_data['token'])) {
$title = (!empty($api_data['title'])) ? $api_data['title'] : 'Mail';
$text = (!empty($api_data['text'])) ? $api_data['text'] : 'You\'ve got mail 📧';
$attributes = json_decode($api_data['attributes'], true);
$senders = explode(',', $api_data['senders']);
$senders = array_filter($senders);
$senders_regex = $api_data['senders_regex'];
$sender_validated = false;
if (empty($senders) && empty($senders_regex)) {
$sender_validated = true;
}
else {
if (!empty($senders)) {
if (in_array($sender, $senders)) {
$sender_validated = true;
}
}
if (!empty($senders_regex) && $sender_validated !== true) {
if (preg_match($senders_regex, $sender)) {
$sender_validated = true;
}
}
}
if ($sender_validated === false) {
error_log("NOTIFY: pushover pipe: skipping unwanted sender " . $sender);
continue;
}
if ($attributes['only_x_prio'] == "1" && $priority == 0) {
error_log("NOTIFY: pushover pipe: mail has no X-Priority: 1 header, skipping");
continue;
}
$post_fields = array(
"token" => $api_data['token'],
"user" => $api_data['key'],
"title" => sprintf("%s", str_replace(
array('{SUBJECT}', '{SENDER}', '{SENDER_NAME}', '{SENDER_ADDRESS}', '{TO_NAME}', '{TO_ADDRESS}', '{MSG_ID}'),
array($subject, $sender, $sender_name, $sender_address, $to_name, $to_address, $messageid), $title)
),
"priority" => $priority,
"message" => sprintf("%s", str_replace(
array('{SUBJECT}', '{SENDER}', '{SENDER_NAME}', '{SENDER_ADDRESS}', '{TO_NAME}', '{TO_ADDRESS}', '{MSG_ID}', '\n'),
array($subject, $sender, $sender_name, $sender_address, $to_name, $to_address, $messageid, PHP_EOL), $text)
),
"sound" => $attributes['sound'] ?? "pushover"
);
if ($attributes['evaluate_x_prio'] == "1" && $priority == 1) {
$post_fields['expire'] = 600;
$post_fields['retry'] = 120;
$post_fields['priority'] = 2;
}
curl_setopt_array($ch = curl_init(), array(
CURLOPT_URL => "https://api.pushover.net/1/messages.json",
CURLOPT_POSTFIELDS => $post_fields,
CURLOPT_SAFE_UPLOAD => true,
CURLOPT_RETURNTRANSFER => true,
));
$result = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
error_log("NOTIFY: result: " . $httpcode . PHP_EOL);
}
}

View File

@ -1,28 +1,28 @@
<!--
<example>
<key>canAuthenticate</key>
<string>YES</string>
<key>id</key>
<string>${line}_ldap</string>
<key>isAddressBook</key>
<string>NO</string>
<key>IDFieldName</key>
<string>mail</string>
<key>UIDFieldName</key>
<string>uid</string>
<key>bindFields</key>
<array>
<string>mail</string>
</array>
<key>type</key>
<string>ldap</string>
<key>bindDN</key>
<string>cn=admin,dc=example,dc=local</string>
<key>bindPassword</key>
<string>password</string>
<key>baseDN</key>
<string>ou=People,dc=example,dc=local</string>
<key>hostname</key>
<string>ldap://1.2.3.4:389</string>
</example>
-->
<!--
<example>
<key>canAuthenticate</key>
<string>YES</string>
<key>id</key>
<string>${line}_ldap</string>
<key>isAddressBook</key>
<string>NO</string>
<key>IDFieldName</key>
<string>mail</string>
<key>UIDFieldName</key>
<string>uid</string>
<key>bindFields</key>
<array>
<string>mail</string>
</array>
<key>type</key>
<string>ldap</string>
<key>bindDN</key>
<string>cn=admin,dc=example,dc=local</string>
<key>bindPassword</key>
<string>password</string>
<key>baseDN</key>
<string>ou=People,dc=example,dc=local</string>
<key>hostname</key>
<string>ldap://1.2.3.4:389</string>
</example>
-->

View File

@ -1,10 +1,10 @@
/*!
* Bootstrap-select v1.14.0-beta2 (https://developer.snapappointments.com/bootstrap-select)
*
* Copyright 2012-2021 SnapAppointments, LLC
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
*/
/*!
* Bootstrap-select v1.14.0-beta2 (https://developer.snapappointments.com/bootstrap-select)
*
* Copyright 2012-2021 SnapAppointments, LLC
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
*/
@-webkit-keyframes bs-notify-fadeOut {
0% {
opacity: 0.9;

View File

@ -1,372 +1,372 @@
@font-face {
font-family: 'Noto Sans';
font-style: normal;
font-weight: 400;
src: local(''),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-regular.woff2') format('woff2'),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-regular.woff') format('woff');
}
@font-face {
font-family: 'Noto Sans';
font-style: normal;
font-weight: 700;
src: local(''),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-700.woff2') format('woff2'),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-700.woff') format('woff');
}
@font-face {
font-family: 'Noto Sans';
font-style: italic;
font-weight: 400;
src: local(''),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-italic.woff2') format('woff2'),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-italic.woff') format('woff');
}
@font-face {
font-family: 'Noto Sans';
font-style: italic;
font-weight: 700;
src: local(''),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff2') format('woff2'),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff') format('woff');
}
#maxmsgsize { min-width: 80px; }
#slider1 .slider-selection {
background: #FFD700;
}
#slider1 .slider-track-high {
background: #FF4500;
}
#slider1 .slider-track-low {
background: #66CD00;
}
.striped:nth-child(odd) {
background-color: #fff;
}
.striped:nth-child(even) {
background-color: #fafafa;
border:1px solid white;
}
.btn {
text-transform: none;
}
.btn * {
pointer-events: none;
}
.textarea-code {
font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
background:transparent !important;
}
.navbar-nav {
margin: 0;
}
.navbar-nav .nav-item {
flex-direction: column;
display: flex;
padding: 0 10px !important;
}
.navbar-nav .nav-link {
height: 44px;
display: flex;
align-items: center;
padding: 0 10px !important;
}
.navbar-fixed-bottom .navbar-collapse,
.navbar-fixed-top .navbar-collapse {
max-height: 1000px
}
.bi {
display: inline-block;
font-size: 12pt;
}
.btn .bi {
display: inline-block;
font-size: inherit;
}
.btn-group-xs > .btn, .btn-xs {
padding: .25rem .4rem;
font-size: .875rem;
line-height: 1rem;
border-radius: .2rem;
}
.icon-spin {
animation-name: spin;
animation-duration: 2000ms;
animation-iteration-count: infinite;
animation-timing-function: linear;
-webkit-animation: spin 2000ms infinite linear;
}
.dropdown-menu {
font-size: 0.9rem;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes blink {
50% {
color: transparent
}
}
.loader-dot {
animation: 1s blink infinite
}
.loader-dot:nth-child(2) {
animation-delay: 250ms
}
.loader-dot:nth-child(3) {
animation-delay: 500ms
}
pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;}
/* Fix modal moving content left */
body.modal-open {
overflow: inherit;
padding-right: inherit !important;
}
body {
font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 10.5pt;
line-height: 1.5;
}
html {
font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 10.5pt;
line-height: 1.5;
}
#mailcow-alert {
position: fixed;
bottom: 8px;
right: 25px;
min-width: 350px;
max-width: 550px;
z-index: 2000;
}
.input-group-sm .btn { margin-top: 0 !important }
legend {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
font-size: 1.2rem;
}
.navbar .navbar-brand {
padding-top: 5px;
}
.navbar .navbar-brand img {
height: 40px;
}
.mailcow-logo img {
max-width: 250px;
}
.lang-link-disabled a {
pointer-events: none;
}
.lang-link-disabled {
cursor: not-allowed;
}
.overlay {
background: #fff;
position: absolute;
z-index: 10000;
top: 0; right: 0; bottom: 0; left: 0;
opacity: 0.7;
}
.bootstrap-select.btn-group .no-results {
display: none;
}
.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
color: rgb(197, 197, 197) !important;
}
.haveibeenpwned {
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.full-width-select {
width: 100%!important;
}
.tooltip {
font-family: inherit;
font-size: 0.8rem;
}
.progress-bar {
font-size: 0.8rem;
line-height: 14px;
}
.footer {
margin-top: 27px;
margin-bottom: 20px;
color: #959595;
display: flex;
flex-direction: column;
}
.footer .version {
margin-left: auto;
margin-top: 20px;
}
.slave-info {
padding: 15px 0px 15px 15px;
font-weight: bold;
}
.alert-hr {
margin:3px 0px;
border-bottom:1px solid #f5f5f5!important;
opacity: 0.3;
}
.btn-input-missing,
.btn-input-missing:hover,
.btn-input-missing:active,
.btn-input-missing:focus,
.btn-input-missing:active:hover,
.btn-input-missing:active:focus {
color: #000 !important;
background-color: #ff2f24 !important;
border-color: #e21207 !important;
}
.navbar-nav > li {
font-size: 1rem !important;
}
.dropdown-menu > li > a {
font-size: 1rem !important;
}
.label {
font-size:inherit;
}
[class^="bi-"]::before, [class*=" bi-"]::before {
vertical-align: -0.2em !important;
}
legend > [class^="bi-"]::before, legend > [class*=" bi-"]::before {
vertical-align: 0em !important;
}
code {
font-size: inherit;
}
.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark {
margin-top: 0px;
}
.flag-icon {
margin-right: 5px;
}
.dropdown-header {
font-weight: 600;
}
.tag-box {
display: flex;
flex-wrap: wrap;
height: auto;
}
.tag-badge {
transition: 200ms linear;
margin-top: 5px;
margin-bottom: 5px;
margin-left: 2px;
margin-right: 2px;
}
.tag-badge.btn-badge {
cursor: pointer;
}
.tag-badge .bi {
font-size: 12px;
}
.tag-badge.btn-badge:hover {
filter: brightness(0.9);
}
.tag-input {
margin-left: 10px;
border: 0 !important;
flex: 1;
height: 24px;
min-width: 150px;
}
.tag-input:focus {
outline: none;
}
.tag-add {
padding: 0 5px 0 5px;
align-items: center;
display: inline-flex;
}
#dnstable {
overflow-x: auto!important;
}
.well {
border: 1px solid #dfdfdf;
background-color: #f9f9f9;
padding: 10px;
}
.btn-check-label {
color: #555;
}
.caret {
transform: rotate(0deg);
}
a[aria-expanded='true'] > .caret,
button[aria-expanded='true'] > .caret {
transform: rotate(-180deg);
}
.list-group-details {
background: #fff;
}
.list-group-header {
background: #f7f7f7;
}
.bg-primary, .alert-primary, .btn-primary {
background-color: #0F688D !important;
border-color: #0d526d !important;
}
.bg-info, .alert-info, .btn-info {
background-color: #148DBC !important;
border-color: #127ea8 !important;
}
.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
color: rgb(137 137 137)!important;
}
.progress {
background-color: #d5d5d5;
}
.btn-outline-secondary:hover {
background-color: #f0f0f0;
}
.btn.btn-outline-secondary {
border-color: #cfcfcf !important;
}
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
background-color: #f0f0f0 !important;
}
@font-face {
font-family: 'Noto Sans';
font-style: normal;
font-weight: 400;
src: local(''),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-regular.woff2') format('woff2'),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-regular.woff') format('woff');
}
@font-face {
font-family: 'Noto Sans';
font-style: normal;
font-weight: 700;
src: local(''),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-700.woff2') format('woff2'),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-700.woff') format('woff');
}
@font-face {
font-family: 'Noto Sans';
font-style: italic;
font-weight: 400;
src: local(''),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-italic.woff2') format('woff2'),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-italic.woff') format('woff');
}
@font-face {
font-family: 'Noto Sans';
font-style: italic;
font-weight: 700;
src: local(''),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff2') format('woff2'),
url('/fonts/noto-sans-v12-latin_greek_cyrillic-700italic.woff') format('woff');
}
#maxmsgsize { min-width: 80px; }
#slider1 .slider-selection {
background: #FFD700;
}
#slider1 .slider-track-high {
background: #FF4500;
}
#slider1 .slider-track-low {
background: #66CD00;
}
.striped:nth-child(odd) {
background-color: #fff;
}
.striped:nth-child(even) {
background-color: #fafafa;
border:1px solid white;
}
.btn {
text-transform: none;
}
.btn * {
pointer-events: none;
}
.textarea-code {
font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
background:transparent !important;
}
.navbar-nav {
margin: 0;
}
.navbar-nav .nav-item {
flex-direction: column;
display: flex;
padding: 0 10px !important;
}
.navbar-nav .nav-link {
height: 44px;
display: flex;
align-items: center;
padding: 0 10px !important;
}
.navbar-fixed-bottom .navbar-collapse,
.navbar-fixed-top .navbar-collapse {
max-height: 1000px
}
.bi {
display: inline-block;
font-size: 12pt;
}
.btn .bi {
display: inline-block;
font-size: inherit;
}
.btn-group-xs > .btn, .btn-xs {
padding: .25rem .4rem;
font-size: .875rem;
line-height: 1rem;
border-radius: .2rem;
}
.icon-spin {
animation-name: spin;
animation-duration: 2000ms;
animation-iteration-count: infinite;
animation-timing-function: linear;
-webkit-animation: spin 2000ms infinite linear;
}
.dropdown-menu {
font-size: 0.9rem;
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes blink {
50% {
color: transparent
}
}
.loader-dot {
animation: 1s blink infinite
}
.loader-dot:nth-child(2) {
animation-delay: 250ms
}
.loader-dot:nth-child(3) {
animation-delay: 500ms
}
pre{white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;}
/* Fix modal moving content left */
body.modal-open {
overflow: inherit;
padding-right: inherit !important;
}
body {
font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 10.5pt;
line-height: 1.5;
}
html {
font-family: "Noto Sans","Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 10.5pt;
line-height: 1.5;
}
#mailcow-alert {
position: fixed;
bottom: 8px;
right: 25px;
min-width: 350px;
max-width: 550px;
z-index: 2000;
}
.input-group-sm .btn { margin-top: 0 !important }
legend {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
font-size: 1.2rem;
}
.navbar .navbar-brand {
padding-top: 5px;
}
.navbar .navbar-brand img {
height: 40px;
}
.mailcow-logo img {
max-width: 250px;
}
.lang-link-disabled a {
pointer-events: none;
}
.lang-link-disabled {
cursor: not-allowed;
}
.overlay {
background: #fff;
position: absolute;
z-index: 10000;
top: 0; right: 0; bottom: 0; left: 0;
opacity: 0.7;
}
.bootstrap-select.btn-group .no-results {
display: none;
}
.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
color: rgb(197, 197, 197) !important;
}
.haveibeenpwned {
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.full-width-select {
width: 100%!important;
}
.tooltip {
font-family: inherit;
font-size: 0.8rem;
}
.progress-bar {
font-size: 0.8rem;
line-height: 14px;
}
.footer {
margin-top: 27px;
margin-bottom: 20px;
color: #959595;
display: flex;
flex-direction: column;
}
.footer .version {
margin-left: auto;
margin-top: 20px;
}
.slave-info {
padding: 15px 0px 15px 15px;
font-weight: bold;
}
.alert-hr {
margin:3px 0px;
border-bottom:1px solid #f5f5f5!important;
opacity: 0.3;
}
.btn-input-missing,
.btn-input-missing:hover,
.btn-input-missing:active,
.btn-input-missing:focus,
.btn-input-missing:active:hover,
.btn-input-missing:active:focus {
color: #000 !important;
background-color: #ff2f24 !important;
border-color: #e21207 !important;
}
.navbar-nav > li {
font-size: 1rem !important;
}
.dropdown-menu > li > a {
font-size: 1rem !important;
}
.label {
font-size:inherit;
}
[class^="bi-"]::before, [class*=" bi-"]::before {
vertical-align: -0.2em !important;
}
legend > [class^="bi-"]::before, legend > [class*=" bi-"]::before {
vertical-align: 0em !important;
}
code {
font-size: inherit;
}
.bootstrap-select.btn-group.show-tick .dropdown-menu li.selected a span.check-mark {
margin-top: 0px;
}
.flag-icon {
margin-right: 5px;
}
.dropdown-header {
font-weight: 600;
}
.tag-box {
display: flex;
flex-wrap: wrap;
height: auto;
}
.tag-badge {
transition: 200ms linear;
margin-top: 5px;
margin-bottom: 5px;
margin-left: 2px;
margin-right: 2px;
}
.tag-badge.btn-badge {
cursor: pointer;
}
.tag-badge .bi {
font-size: 12px;
}
.tag-badge.btn-badge:hover {
filter: brightness(0.9);
}
.tag-input {
margin-left: 10px;
border: 0 !important;
flex: 1;
height: 24px;
min-width: 150px;
}
.tag-input:focus {
outline: none;
}
.tag-add {
padding: 0 5px 0 5px;
align-items: center;
display: inline-flex;
}
#dnstable {
overflow-x: auto!important;
}
.well {
border: 1px solid #dfdfdf;
background-color: #f9f9f9;
padding: 10px;
}
.btn-check-label {
color: #555;
}
.caret {
transform: rotate(0deg);
}
a[aria-expanded='true'] > .caret,
button[aria-expanded='true'] > .caret {
transform: rotate(-180deg);
}
.list-group-details {
background: #fff;
}
.list-group-header {
background: #f7f7f7;
}
.bg-primary, .alert-primary, .btn-primary {
background-color: #0F688D !important;
border-color: #0d526d !important;
}
.bg-info, .alert-info, .btn-info {
background-color: #148DBC !important;
border-color: #127ea8 !important;
}
.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary {
color: rgb(137 137 137)!important;
}
.progress {
background-color: #d5d5d5;
}
.btn-outline-secondary:hover {
background-color: #f0f0f0;
}
.btn.btn-outline-secondary {
border-color: #cfcfcf !important;
}
.btn-check:checked+.btn-outline-secondary, .btn-check:active+.btn-outline-secondary, .btn-outline-secondary:active, .btn-outline-secondary.active, .btn-outline-secondary.dropdown-toggle.show {
background-color: #f0f0f0 !important;
}

View File

@ -1,86 +1,86 @@
.pagination a {
text-decoration: none !important;
}
.panel.panel-default {
overflow: visible !important;
}
.table-responsive {
overflow: visible !important;
}
.table-responsive {
overflow-x: scroll !important;
}
body {
overflow-y:scroll;
}
/* Fix modal moving content left */
body.modal-open {
overflow-y:scroll;
padding-right: inherit !important;
}
@media (min-width: 992px) {
.container {
width: 80%;
}
}
.mass-actions-admin {
user-select: none;
}
.inputMissingAttr {
border-color: #FF4136;
}
.rotate {
-moz-transition: all 0.3s linear;
-webkit-transition: all 0.3s linear;
transition: all 0.3s linear;
}
.rotate.animation {
-ms-transform:rotateX(180deg);
-moz-transform:rotateX(180deg);
-webkit-transform:rotateX(180deg);
transform:rotateX(180deg);
}
.anchor {
display: block;
height: 65px;
margin-top: -65px;
visibility: hidden;
}
.thumbnail img {
min-height:100px;
height:100px;
}
.nav-tabs>li>a {
z-index: 1;
}
.table-condensed .input-sm {
width: 100%!important;
}
.table-condensed > thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td {
padding: 3px;
}
table tbody tr {
cursor: pointer;
}
table tbody tr td input[type="checkbox"] {
cursor: pointer;
}
#quarantine_template {
margin:20px;
}
.regex-input {
font-family: Consolas,monaco,monospace;
font-size: 1rem;
}
.label-keys {
font-size:100%;
margin: 0px !important;
white-space: normal !important;
}
.key-action {
font-weight:bold;
color:white !important;
}
.dkim-label {
margin: 0 0 8px !important;
.pagination a {
text-decoration: none !important;
}
.panel.panel-default {
overflow: visible !important;
}
.table-responsive {
overflow: visible !important;
}
.table-responsive {
overflow-x: scroll !important;
}
body {
overflow-y:scroll;
}
/* Fix modal moving content left */
body.modal-open {
overflow-y:scroll;
padding-right: inherit !important;
}
@media (min-width: 992px) {
.container {
width: 80%;
}
}
.mass-actions-admin {
user-select: none;
}
.inputMissingAttr {
border-color: #FF4136;
}
.rotate {
-moz-transition: all 0.3s linear;
-webkit-transition: all 0.3s linear;
transition: all 0.3s linear;
}
.rotate.animation {
-ms-transform:rotateX(180deg);
-moz-transform:rotateX(180deg);
-webkit-transform:rotateX(180deg);
transform:rotateX(180deg);
}
.anchor {
display: block;
height: 65px;
margin-top: -65px;
visibility: hidden;
}
.thumbnail img {
min-height:100px;
height:100px;
}
.nav-tabs>li>a {
z-index: 1;
}
.table-condensed .input-sm {
width: 100%!important;
}
.table-condensed > thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td {
padding: 3px;
}
table tbody tr {
cursor: pointer;
}
table tbody tr td input[type="checkbox"] {
cursor: pointer;
}
#quarantine_template {
margin:20px;
}
.regex-input {
font-family: Consolas,monaco,monospace;
font-size: 1rem;
}
.label-keys {
font-size:100%;
margin: 0px !important;
white-space: normal !important;
}
.key-action {
font-weight:bold;
color:white !important;
}
.dkim-label {
margin: 0 0 8px !important;
}

View File

@ -1,36 +1,36 @@
.pagination a {
text-decoration: none !important;
}
.panel.panel-default {
overflow: visible !important;
}
.table-responsive {
overflow: visible !important;
}
@media screen and (max-width: 1280px) {
.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;
}
.inputMissingAttr {
border-color: #FF4136;
}
.table-lines {
vertical-align: inherit;
}
.pagination a {
text-decoration: none !important;
}
.panel.panel-default {
overflow: visible !important;
}
.table-responsive {
overflow: visible !important;
}
@media screen and (max-width: 1280px) {
.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;
}
.inputMissingAttr {
border-color: #FF4136;
}
.table-lines {
vertical-align: inherit;
}

View File

@ -1,42 +1,42 @@
.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;
}
.mass-actions-user {
user-select: none;
}
.inputMissingAttr {
border-color: #FF4136;
}
.rotate {
-moz-transition: all 0.3s linear;
-webkit-transition: all 0.3s linear;
transition: all 0.3s linear;
}
.rotate.animation {
-ms-transform:rotateX(180deg);
-moz-transform:rotateX(180deg);
-webkit-transform:rotateX(180deg);
transform:rotateX(180deg);
}
#sender_acl_disabled {
display:none;
margin-top:10px;
}
.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;
}
.mass-actions-user {
user-select: none;
}
.inputMissingAttr {
border-color: #FF4136;
}
.rotate {
-moz-transition: all 0.3s linear;
-webkit-transition: all 0.3s linear;
transition: all 0.3s linear;
}
.rotate.animation {
-ms-transform:rotateX(180deg);
-moz-transform:rotateX(180deg);
-webkit-transform:rotateX(180deg);
transform:rotateX(180deg);
}
#sender_acl_disabled {
display:none;
margin-top:10px;
}

View File

@ -1,8 +1,8 @@
@media (max-width: 500px) {
#top {
padding-top: 15px !important;
}
}
.ui-announcement-alert {
margin: 10px 0px 10px 0px;
}
@media (max-width: 500px) {
#top {
padding-top: 15px !important;
}
}
.ui-announcement-alert {
margin: 10px 0px 10px 0px;
}

View File

@ -1,69 +1,69 @@
.pagination a {
text-decoration: none !important;
}
.panel.panel-default {
overflow: visible !important;
}
.table-responsive {
overflow: inherit !important;
}
.table-responsive {
overflow-x: scroll !important;
}
.btn-group {
width: max-content;
}
.footer-add-item {
display:block;
text-align: center;
font-style: italic;
padding: 10px;
background: #F5F5F5;
}
@media (min-width: 992px) {
.container {
width: 100%;
}
}
@media (min-width: 1920px) {
.container {
width: 80%;
}
}
.mass-actions-mailbox {
user-select: none;
}
.inputMissingAttr {
border-color: #FF4136;
}
.dns-found {
max-width: 300px;
word-break: break-all;
}
.dns-recommended {
max-width: 150px;
word-break: break-all;
}
.table-lines {
vertical-align: inherit;
}
#logText {
font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
font-size:smaller;
}
table tbody tr {
cursor: pointer;
}
table tbody tr td input[type="checkbox"] {
cursor: pointer;
}
.label-last-login .bi {
font-size: 8pt !important;
}
.label-last-login {
line-height: 2.2;
color: #4a4a4a!important;
padding: .2em .4em .3em !important;
background-color: #ececec!important;
}
.pagination a {
text-decoration: none !important;
}
.panel.panel-default {
overflow: visible !important;
}
.table-responsive {
overflow: inherit !important;
}
.table-responsive {
overflow-x: scroll !important;
}
.btn-group {
width: max-content;
}
.footer-add-item {
display:block;
text-align: center;
font-style: italic;
padding: 10px;
background: #F5F5F5;
}
@media (min-width: 992px) {
.container {
width: 100%;
}
}
@media (min-width: 1920px) {
.container {
width: 80%;
}
}
.mass-actions-mailbox {
user-select: none;
}
.inputMissingAttr {
border-color: #FF4136;
}
.dns-found {
max-width: 300px;
word-break: break-all;
}
.dns-recommended {
max-width: 150px;
word-break: break-all;
}
.table-lines {
vertical-align: inherit;
}
#logText {
font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;
font-size:smaller;
}
table tbody tr {
cursor: pointer;
}
table tbody tr td input[type="checkbox"] {
cursor: pointer;
}
.label-last-login .bi {
font-size: 8pt !important;
}
.label-last-login {
line-height: 2.2;
color: #4a4a4a!important;
padding: .2em .4em .3em !important;
background-color: #ececec!important;
}

View File

@ -1,134 +1,134 @@
.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;
}
.mass-actions-user {
user-select: none;
}
.inputMissingAttr {
border-color: #FF4136;
}
#logText {
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
body {
overflow-y:scroll;
}
table tbody tr {
cursor: pointer;
}
table tbody tr td input[type="checkbox"] {
cursor: pointer;
}
.label-keys {
font-size:100%;
margin: 0px !important;
white-space: normal !important;
}
.key-action {
font-weight:bold;
color:white !important;
}
svg {
display: inline-block;
vertical-align: middle;
}
.c-1-color, .label-ham {
background: #28b62c;
background: -webkit-linear-gradient(to right, #28b62c, #fff233);
background: linear-gradient(to right, #28b62c, #fff233);
color: #000;
}
.c-2-color, .label-spam {
background: #fff233;
background: -webkit-linear-gradient(to right, #fff233, #ff4136);
background: linear-gradient(to right, #fff233, #ff4136);
color: #000;
}
.c-3-color, .label-reject{
background: #ff4136;
color: #fff;
}
#spam_score {
margin-bottom: 10px;
}
.noUi-handle {
border: 1px solid #e2e2e2;
border-radius: 0px;
background: #eee;
cursor: default;
box-shadow: none;
border-top-width: 0px;
border-right-width: 1px;
border-bottom-width: 4px;
border-left-width: 1px;
}
.noUi-handle:hover {
background-color: #eee;
border-color: #e2e2e2;
margin-top: 1px;
border-bottom-width: 3px;
}
.noUi-handle::after, .noUi-handle::before {
background: #c6c6c6;
width: 2px;
}
.noUi-target {
background: transparent;
border-radius: 0px;
border: 1px solid #D3D3D3;
box-shadow: none;
}
.noUi-connects {
border-radius: 0px;
}
.label-ham,
.label-spam,
.label-reject {
padding: .1em .5em .1em;
font-size: inherit;
font-weight: 400;
}
.clear-last-logins {
cursor: pointer;
font-size:90%;
font-style: italic;
color: #158cba;
user-select:none;
}
.ip-location-flag {
border-radius: 4px;
top: 3px;
}
.recent-login-success {
margin-top:2px;
margin-right:10px;
}
.label-protocol-access {
line-height: 2;
}
.help-block-mt-0 {
margin-top: 0px;
}
.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;
}
.mass-actions-user {
user-select: none;
}
.inputMissingAttr {
border-color: #FF4136;
}
#logText {
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
}
body {
overflow-y:scroll;
}
table tbody tr {
cursor: pointer;
}
table tbody tr td input[type="checkbox"] {
cursor: pointer;
}
.label-keys {
font-size:100%;
margin: 0px !important;
white-space: normal !important;
}
.key-action {
font-weight:bold;
color:white !important;
}
svg {
display: inline-block;
vertical-align: middle;
}
.c-1-color, .label-ham {
background: #28b62c;
background: -webkit-linear-gradient(to right, #28b62c, #fff233);
background: linear-gradient(to right, #28b62c, #fff233);
color: #000;
}
.c-2-color, .label-spam {
background: #fff233;
background: -webkit-linear-gradient(to right, #fff233, #ff4136);
background: linear-gradient(to right, #fff233, #ff4136);
color: #000;
}
.c-3-color, .label-reject{
background: #ff4136;
color: #fff;
}
#spam_score {
margin-bottom: 10px;
}
.noUi-handle {
border: 1px solid #e2e2e2;
border-radius: 0px;
background: #eee;
cursor: default;
box-shadow: none;
border-top-width: 0px;
border-right-width: 1px;
border-bottom-width: 4px;
border-left-width: 1px;
}
.noUi-handle:hover {
background-color: #eee;
border-color: #e2e2e2;
margin-top: 1px;
border-bottom-width: 3px;
}
.noUi-handle::after, .noUi-handle::before {
background: #c6c6c6;
width: 2px;
}
.noUi-target {
background: transparent;
border-radius: 0px;
border: 1px solid #D3D3D3;
box-shadow: none;
}
.noUi-connects {
border-radius: 0px;
}
.label-ham,
.label-spam,
.label-reject {
padding: .1em .5em .1em;
font-size: inherit;
font-weight: 400;
}
.clear-last-logins {
cursor: pointer;
font-size:90%;
font-style: italic;
color: #158cba;
user-select:none;
}
.ip-location-flag {
border-radius: 4px;
top: 3px;
}
.recent-login-success {
margin-top:2px;
margin-right:10px;
}
.label-protocol-access {
line-height: 2;
}
.help-block-mt-0 {
margin-top: 0px;
}

View File

@ -1,52 +1,52 @@
<?php
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('info', $_GET['service'])['State']['Running'] != 1 && $retry <= 3) {
$response = docker('post', $_GET['service'], '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('info', $_GET['service'])['State']['Running'] == 1 && $retry <= 3) {
$response = docker('post', $_GET['service'], '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('post', $_GET['service'], '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('logs', $_GET['service'], $lines)));
}
}
?>
<?php
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('info', $_GET['service'])['State']['Running'] != 1 && $retry <= 3) {
$response = docker('post', $_GET['service'], '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('info', $_GET['service'])['State']['Running'] == 1 && $retry <= 3) {
$response = docker('post', $_GET['service'], '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('post', $_GET['service'], '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('logs', $_GET['service'], $lines)));
}
}
?>

View File

@ -1,6 +1,6 @@
<?php
session_start();
unset($_SESSION['pending_mailcow_cc_username']);
unset($_SESSION['pending_mailcow_cc_role']);
unset($_SESSION['pending_tfa_methods']);
?>
<?php
session_start();
unset($_SESSION['pending_mailcow_cc_username']);
unset($_SESSION['pending_mailcow_cc_role']);
unset($_SESSION['pending_tfa_methods']);
?>

View File

@ -1,207 +1,207 @@
<?php
header("Content-Type: application/json");
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
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);
}
function addAddresses(&$list, $mail, $headerName) {
$addresses = $mail->getAddresses($headerName);
foreach ($addresses as $address) {
if (filter_var($address['address'], FILTER_VALIDATE_EMAIL)) {
$list[] = array('address' => $address['address'], 'type' => $headerName);
}
}
}
if (!empty($_GET['hash']) && ctype_alnum($_GET['hash'])) {
$mailc = quarantine('hash_details', $_GET['hash']);
if ($mailc === false) {
echo json_encode(array('error' => 'Message invalid'));
exit;
}
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();
$html2text = new Html2Text\Html2Text();
// Load msg to parser
$mail_parser->setText($mailc['msg']);
// Get mail recipients
{
$recipientsList = array();
addAddresses($recipientsList, $mail_parser, 'to');
addAddresses($recipientsList, $mail_parser, 'cc');
addAddresses($recipientsList, $mail_parser, 'bcc');
$recipientsList[] = array('address' => $mailc['rcpt'], 'type' => 'smtp');
$data['recipients'] = $recipientsList;
}
// Get from
$data['header_from'] = $mail_parser->getHeader('from');
$data['env_from'] = $mailc['sender'];
// Get rspamd score
$data['score'] = $mailc['score'];
// Get rspamd action
$data['action'] = $mailc['action'];
// Get rspamd symbols
$data['symbols'] = json_decode($mailc['symbols']);
// Get fuzzy hashes
$data['fuzzy_hashes'] = json_decode($mailc['fuzzy_hashes']);
$data['subject'] = mb_convert_encoding($mail_parser->getHeader('subject'), "UTF-8", "auto");
(empty($data['subject'])) ? $data['subject'] = '-' : null;
echo json_encode($data);
}
}
elseif (!empty($_GET['id']) && ctype_alnum($_GET['id'])) {
if (!isset($_SESSION['mailcow_cc_role'])) {
echo json_encode(array('error' => 'Access denied'));
exit();
}
$tmpdir = '/tmp/' . $_GET['id'] . '/';
$mailc = quarantine('details', $_GET['id']);
if ($mailc === false) {
echo json_encode(array('error' => 'Access denied'));
exit;
}
if (strlen($mailc['msg']) > 10485760) {
echo json_encode(array('error' => 'Message size exceeds 10 MiB.'));
exit;
}
if (!empty($mailc['msg'])) {
if (isset($_GET['quick_release'])) {
$hash = hash('sha256', $mailc['id'] . $mailc['qid']);
header('Location: /qhandler/release/' . $hash);
exit;
}
if (isset($_GET['quick_delete'])) {
$hash = hash('sha256', $mailc['id'] . $mailc['qid']);
header('Location: /qhandler/delete/' . $hash);
exit;
}
// Init message array
$data = array();
// Init parser
$mail_parser = new PhpMimeMailParser\Parser();
$html2text = new Html2Text\Html2Text();
// Load msg to parser
$mail_parser->setText($mailc['msg']);
// Get mail recipients
{
$recipientsList = array();
addAddresses($recipientsList, $mail_parser, 'to');
addAddresses($recipientsList, $mail_parser, 'cc');
addAddresses($recipientsList, $mail_parser, 'bcc');
$recipientsList[] = array('address' => $mailc['rcpt'], 'type' => 'smtp');
$data['recipients'] = $recipientsList;
}
// Get from
$data['header_from'] = $mail_parser->getHeader('from');
$data['env_from'] = $mailc['sender'];
// Get rspamd score
$data['score'] = $mailc['score'];
// Get rspamd action
$data['action'] = $mailc['action'];
// Get rspamd symbols
$data['symbols'] = json_decode($mailc['symbols']);
// Get fuzzy hashes
$data['fuzzy_hashes'] = json_decode($mailc['fuzzy_hashes']);
// Get text/plain content
$data['text_plain'] = $mail_parser->getMessageBody('text');
if (!json_encode($data['text_plain'])) $data['text_plain'] = '';
// Get html content and convert to text
$data['text_html'] = $html2text->convert($mail_parser->getMessageBody('html'));
if (empty($data['text_plain']) && empty($data['text_html'])) {
// Failed to parse content, try raw
$text = trim(substr($mailc['msg'], strpos($mailc['msg'], "\r\n\r\n") + 1));
// Only return html->text
$data['text_plain'] = 'Parser failed, assuming HTML';
$data['text_html'] = $html2text->convert($text);
}
(empty($data['text_plain'])) ? $data['text_plain'] = '-' : null;
// Get subject
$data['subject'] = $mail_parser->getHeader('subject');
$data['subject'] = mb_convert_encoding($mail_parser->getHeader('subject'), "UTF-8", "auto");
(empty($data['subject'])) ? $data['subject'] = '-' : null;
// 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['eml'])) {
$dl_filename = filter_var($data['subject'], FILTER_SANITIZE_STRING);
$dl_filename = strlen($dl_filename) > 30 ? substr($dl_filename,0,30) : $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: message/rfc822');
header('Content-Disposition: attachment; filename="'. $dl_filename . '.eml";');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . strlen($mailc['msg']));
echo $mailc['msg'];
exit;
}
if (isset($_GET['att'])) {
if ($_SESSION['acl']['quarantine_attachments'] == 0) {
exit(json_encode('Forbidden'));
}
$dl_id = intval($_GET['att']);
$dl_filename = filter_var($data['attachments'][$dl_id][0], FILTER_SANITIZE_STRING);
$dl_filename_short = strlen($dl_filename) > 20 ? substr($dl_filename, 0, 20) : $dl_filename;
$dl_filename_extension = pathinfo($tmpdir . $dl_filename)['extension'];
$dl_filename_short = preg_replace('/\.' . $dl_filename_extension . '$/', '', $dl_filename_short);
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_short . '.' . $dl_filename_extension . '";');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . $data['attachments'][$dl_id][2]);
readfile($tmpdir . $dl_filename);
exit;
}
}
echo json_encode($data);
}
}
?>
<?php
header("Content-Type: application/json");
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
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);
}
function addAddresses(&$list, $mail, $headerName) {
$addresses = $mail->getAddresses($headerName);
foreach ($addresses as $address) {
if (filter_var($address['address'], FILTER_VALIDATE_EMAIL)) {
$list[] = array('address' => $address['address'], 'type' => $headerName);
}
}
}
if (!empty($_GET['hash']) && ctype_alnum($_GET['hash'])) {
$mailc = quarantine('hash_details', $_GET['hash']);
if ($mailc === false) {
echo json_encode(array('error' => 'Message invalid'));
exit;
}
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();
$html2text = new Html2Text\Html2Text();
// Load msg to parser
$mail_parser->setText($mailc['msg']);
// Get mail recipients
{
$recipientsList = array();
addAddresses($recipientsList, $mail_parser, 'to');
addAddresses($recipientsList, $mail_parser, 'cc');
addAddresses($recipientsList, $mail_parser, 'bcc');
$recipientsList[] = array('address' => $mailc['rcpt'], 'type' => 'smtp');
$data['recipients'] = $recipientsList;
}
// Get from
$data['header_from'] = $mail_parser->getHeader('from');
$data['env_from'] = $mailc['sender'];
// Get rspamd score
$data['score'] = $mailc['score'];
// Get rspamd action
$data['action'] = $mailc['action'];
// Get rspamd symbols
$data['symbols'] = json_decode($mailc['symbols']);
// Get fuzzy hashes
$data['fuzzy_hashes'] = json_decode($mailc['fuzzy_hashes']);
$data['subject'] = mb_convert_encoding($mail_parser->getHeader('subject'), "UTF-8", "auto");
(empty($data['subject'])) ? $data['subject'] = '-' : null;
echo json_encode($data);
}
}
elseif (!empty($_GET['id']) && ctype_alnum($_GET['id'])) {
if (!isset($_SESSION['mailcow_cc_role'])) {
echo json_encode(array('error' => 'Access denied'));
exit();
}
$tmpdir = '/tmp/' . $_GET['id'] . '/';
$mailc = quarantine('details', $_GET['id']);
if ($mailc === false) {
echo json_encode(array('error' => 'Access denied'));
exit;
}
if (strlen($mailc['msg']) > 10485760) {
echo json_encode(array('error' => 'Message size exceeds 10 MiB.'));
exit;
}
if (!empty($mailc['msg'])) {
if (isset($_GET['quick_release'])) {
$hash = hash('sha256', $mailc['id'] . $mailc['qid']);
header('Location: /qhandler/release/' . $hash);
exit;
}
if (isset($_GET['quick_delete'])) {
$hash = hash('sha256', $mailc['id'] . $mailc['qid']);
header('Location: /qhandler/delete/' . $hash);
exit;
}
// Init message array
$data = array();
// Init parser
$mail_parser = new PhpMimeMailParser\Parser();
$html2text = new Html2Text\Html2Text();
// Load msg to parser
$mail_parser->setText($mailc['msg']);
// Get mail recipients
{
$recipientsList = array();
addAddresses($recipientsList, $mail_parser, 'to');
addAddresses($recipientsList, $mail_parser, 'cc');
addAddresses($recipientsList, $mail_parser, 'bcc');
$recipientsList[] = array('address' => $mailc['rcpt'], 'type' => 'smtp');
$data['recipients'] = $recipientsList;
}
// Get from
$data['header_from'] = $mail_parser->getHeader('from');
$data['env_from'] = $mailc['sender'];
// Get rspamd score
$data['score'] = $mailc['score'];
// Get rspamd action
$data['action'] = $mailc['action'];
// Get rspamd symbols
$data['symbols'] = json_decode($mailc['symbols']);
// Get fuzzy hashes
$data['fuzzy_hashes'] = json_decode($mailc['fuzzy_hashes']);
// Get text/plain content
$data['text_plain'] = $mail_parser->getMessageBody('text');
if (!json_encode($data['text_plain'])) $data['text_plain'] = '';
// Get html content and convert to text
$data['text_html'] = $html2text->convert($mail_parser->getMessageBody('html'));
if (empty($data['text_plain']) && empty($data['text_html'])) {
// Failed to parse content, try raw
$text = trim(substr($mailc['msg'], strpos($mailc['msg'], "\r\n\r\n") + 1));
// Only return html->text
$data['text_plain'] = 'Parser failed, assuming HTML';
$data['text_html'] = $html2text->convert($text);
}
(empty($data['text_plain'])) ? $data['text_plain'] = '-' : null;
// Get subject
$data['subject'] = $mail_parser->getHeader('subject');
$data['subject'] = mb_convert_encoding($mail_parser->getHeader('subject'), "UTF-8", "auto");
(empty($data['subject'])) ? $data['subject'] = '-' : null;
// 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['eml'])) {
$dl_filename = filter_var($data['subject'], FILTER_SANITIZE_STRING);
$dl_filename = strlen($dl_filename) > 30 ? substr($dl_filename,0,30) : $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: message/rfc822');
header('Content-Disposition: attachment; filename="'. $dl_filename . '.eml";');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . strlen($mailc['msg']));
echo $mailc['msg'];
exit;
}
if (isset($_GET['att'])) {
if ($_SESSION['acl']['quarantine_attachments'] == 0) {
exit(json_encode('Forbidden'));
}
$dl_id = intval($_GET['att']);
$dl_filename = filter_var($data['attachments'][$dl_id][0], FILTER_SANITIZE_STRING);
$dl_filename_short = strlen($dl_filename) > 20 ? substr($dl_filename, 0, 20) : $dl_filename;
$dl_filename_extension = pathinfo($tmpdir . $dl_filename)['extension'];
$dl_filename_short = preg_replace('/\.' . $dl_filename_extension . '$/', '', $dl_filename_short);
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_short . '.' . $dl_filename_extension . '";');
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,10 +1,10 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
header('Content-Type: text/plain');
if (!isset($_SESSION['mailcow_cc_role'])) {
exit();
}
if (isset($_GET['token']) && ctype_alnum($_GET['token'])) {
echo $tfa->getQRCodeImageAsDataUri($_SESSION['mailcow_cc_username'], $_GET['token']);
}
?>
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
header('Content-Type: text/plain');
if (!isset($_SESSION['mailcow_cc_role'])) {
exit();
}
if (isset($_GET['token']) && ctype_alnum($_GET['token'])) {
echo $tfa->getQRCodeImageAsDataUri($_SESSION['mailcow_cc_username'], $_GET['token']);
}
?>

View File

@ -1,3 +1,3 @@
<?php
session_start();
<?php
session_start();
$_SESSION['show_rspamd_global_filters'] = true;

View File

@ -1,22 +1,22 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
header('Content-Type: application/json');
if (!isset($_SESSION['mailcow_cc_role'])) {
exit();
}
if (isset($_GET['script'])) {
$sieve = new Sieve\SieveParser();
try {
if (empty($_GET['script'])) {
echo json_encode(array('type' => 'danger', 'msg' => $lang['danger']['script_empty']));
exit();
}
$sieve->parse($_GET['script']);
}
catch (Exception $e) {
echo json_encode(array('type' => 'danger', 'msg' => $e->getMessage()));
exit();
}
echo json_encode(array('type' => 'success', 'msg' => $lang['add']['validation_success']));
}
?>
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
header('Content-Type: application/json');
if (!isset($_SESSION['mailcow_cc_role'])) {
exit();
}
if (isset($_GET['script'])) {
$sieve = new Sieve\SieveParser();
try {
if (empty($_GET['script'])) {
echo json_encode(array('type' => 'danger', 'msg' => $lang['danger']['script_empty']));
exit();
}
$sieve->parse($_GET['script']);
}
catch (Exception $e) {
echo json_encode(array('type' => 'danger', 'msg' => $e->getMessage()));
exit();
}
echo json_encode(array('type' => 'success', 'msg' => $lang['add']['validation_success']));
}
?>

View File

@ -1,14 +1,14 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
header('Content-Type: text/plain');
if (!isset($_SESSION['mailcow_cc_role'])) {
exit();
}
if (isset($_GET['id']) && is_numeric($_GET['id'])) {
if ($details = mailbox('get', 'syncjob_details', intval($_GET['id']))) {
echo (empty($details['log'])) ? '-' : $details['log'];
}
}
?>
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
header('Content-Type: text/plain');
if (!isset($_SESSION['mailcow_cc_role'])) {
exit();
}
if (isset($_GET['id']) && is_numeric($_GET['id'])) {
if ($details = mailbox('get', 'syncjob_details', intval($_GET['id']))) {
echo (empty($details['log'])) ? '-' : $details['log'];
}
}
?>

View File

@ -1,149 +1,149 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/vars.inc.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
error_reporting(0);
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
$transport_id = intval($_GET['transport_id']);
$transport_type = $_GET['transport_type'];
if (isset($_GET['mail_from']) && filter_var($_GET['mail_from'], FILTER_VALIDATE_EMAIL)) {
$mail_from = $_GET['mail_from'];
}
else {
$mail_from = "relay@example.org";
}
if (isset($_GET['mail_rcpt']) && filter_var($_GET['mail_rcpt'], FILTER_VALIDATE_EMAIL)) {
$mail_rcpt = $_GET['mail_rcpt'];
}
else {
$mail_rcpt = "null@hosted.mailcow.de";
}
if ($transport_type == 'transport-map') {
$transport_details = transport('details', $transport_id);
$nexthop = $transport_details['nexthop'];
}
elseif ($transport_type == 'sender-dependent') {
$transport_details = relayhost('details', $transport_id);
$nexthop = $transport_details['hostname'];
}
if (!empty($transport_details)) {
// Remove [ and ]
$hostname_w_port = preg_replace('/\[|\]/', '', $nexthop);
preg_match('/\[.+\](:.+)/', $nexthop, $hostname_port_match);
preg_match('/\[\d\.\d\.\d\.\d\](:.+)/', $nexthop, $ipv4_port_match);
$has_bracket_and_port = (isset($hostname_port_match[1])) ? true : false;
$is_ipv4_and_has_port = (isset($ipv4_port_match[1])) ? true : false;
$skip_lookup_mx = strpos($nexthop, '[');
// Explode to hostname and port
if ($has_bracket_and_port) {
$port = substr($hostname_w_port, strrpos($hostname_w_port, ':') + 1);
$hostname = preg_replace('/'. preg_quote(':' . $port, '/') . '$/', '', $hostname_w_port);
if (filter_var($hostname, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$hostname = '[' . $hostname . ']';
}
}
else {
if ($is_ipv4_and_has_port) {
$port = substr($hostname_w_port, strrpos($hostname_w_port, ':') + 1);
$hostname = preg_replace('/'. preg_quote(':' . $port, '/') . '$/', '', $hostname_w_port);
}
if (filter_var($hostname_w_port, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
$hostname = $hostname_w_port;
$port = null;
}
elseif (filter_var($hostname_w_port, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$hostname = '[' . $hostname_w_port . ']';
$port = null;
}
else {
$hostname = preg_replace('/'. preg_quote(':' . $port, '/') . '$/', '', $hostname_w_port);
$port = null;
}
}
// Try to get MX if host is not [host]
if ($skip_lookup_mx === false) {
getmxrr($hostname, $mx_records, $mx_weight);
if (!empty($mx_records)) {
for ($i = 0; $i < count($mx_records); $i++) {
$mxs[$mx_records[$i]] = $mx_weight[$i];
}
asort ($mxs);
$records = array_keys($mxs);
echo 'Using first matched primary MX for "' . $hostname . '": ';
$hostname = $records[0];
echo $hostname . '<br>';
}
else {
echo 'No MX records for ' . $hostname . ' were found in DNS, skipping and using hostname as next-hop.<br>';
}
}
// Use port 25 if no port was given
$port = (empty($port)) ? 25 : $port;
$username = $transport_details['username'];
$password = $transport_details['password'];
$mail = new PHPMailer;
$mail->Timeout = 15;
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
$mail->SMTPDebug = 3;
// smtp: and smtp_enforced_tls: do not support wrapped tls, todo?
// change postfix map to detect wrapped tls or add a checkbox to toggle wrapped tls
// if ($port == 465) {
// $mail->SMTPSecure = "ssl";
// }
$mail->Debugoutput = function($str, $level) {
foreach(preg_split("/((\r?\n)|(\r\n?)|\n)/", $str) as $line){
if (empty($line)) { continue; }
if (preg_match("/SERVER \-\> CLIENT: 2\d\d.+/i", $line)) {
echo '<span style="color:darkgreen;font-weight:bold">' . htmlspecialchars($line) . '</span><br>';
}
elseif (preg_match("/SERVER \-\> CLIENT: 3\d\d.+/i", $line)) {
echo '<span style="color:lightgreen;font-weight:bold">' . htmlspecialchars($line) . '</span><br>';
}
elseif (preg_match("/SERVER \-\> CLIENT: 4\d\d.+/i", $line)) {
echo '<span style="color:yellow;font-weight:bold">' . htmlspecialchars($line) . '</span><br>';
}
elseif (preg_match("/SERVER \-\> CLIENT: 5\d\d.+/i", $line)) {
echo '<span style="color:red;font-weight:bold">' . htmlspecialchars($line) . '</span><br>';
}
elseif (preg_match("/CLIENT \-\> SERVER:.+/i", $line)) {
echo '<span style="color:#999;font-weight:bold">' . htmlspecialchars($line) . '</span><br>';
}
elseif (preg_match("/^(?!SERVER|CLIENT|Connection:|\)).+$/i", $line)) {
echo '<span>&nbsp;&nbsp;&nbsp;&nbsp;↪ ' . htmlspecialchars($line) . '</span><br>';
}
else {
echo htmlspecialchars($line) . '<br>';
}
}
};
$mail->isSMTP();
$mail->Host = $hostname;
if (!empty($username)) {
$mail->SMTPAuth = true;
$mail->Username = $username;
$mail->Password = $password;
}
$mail->Port = $port;
$mail->setFrom($mail_from, 'Mailer');
$mail->Subject = 'A subject for a SMTP test';
$mail->addAddress($mail_rcpt, 'Joe Null');
$mail->Body = 'This is our test body';
$mail->send();
}
else {
echo "Unknown transport.";
}
}
else {
echo "Permission denied.";
}
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/vars.inc.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
error_reporting(0);
if (isset($_SESSION['mailcow_cc_role']) && $_SESSION['mailcow_cc_role'] == "admin") {
$transport_id = intval($_GET['transport_id']);
$transport_type = $_GET['transport_type'];
if (isset($_GET['mail_from']) && filter_var($_GET['mail_from'], FILTER_VALIDATE_EMAIL)) {
$mail_from = $_GET['mail_from'];
}
else {
$mail_from = "relay@example.org";
}
if (isset($_GET['mail_rcpt']) && filter_var($_GET['mail_rcpt'], FILTER_VALIDATE_EMAIL)) {
$mail_rcpt = $_GET['mail_rcpt'];
}
else {
$mail_rcpt = "null@hosted.mailcow.de";
}
if ($transport_type == 'transport-map') {
$transport_details = transport('details', $transport_id);
$nexthop = $transport_details['nexthop'];
}
elseif ($transport_type == 'sender-dependent') {
$transport_details = relayhost('details', $transport_id);
$nexthop = $transport_details['hostname'];
}
if (!empty($transport_details)) {
// Remove [ and ]
$hostname_w_port = preg_replace('/\[|\]/', '', $nexthop);
preg_match('/\[.+\](:.+)/', $nexthop, $hostname_port_match);
preg_match('/\[\d\.\d\.\d\.\d\](:.+)/', $nexthop, $ipv4_port_match);
$has_bracket_and_port = (isset($hostname_port_match[1])) ? true : false;
$is_ipv4_and_has_port = (isset($ipv4_port_match[1])) ? true : false;
$skip_lookup_mx = strpos($nexthop, '[');
// Explode to hostname and port
if ($has_bracket_and_port) {
$port = substr($hostname_w_port, strrpos($hostname_w_port, ':') + 1);
$hostname = preg_replace('/'. preg_quote(':' . $port, '/') . '$/', '', $hostname_w_port);
if (filter_var($hostname, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$hostname = '[' . $hostname . ']';
}
}
else {
if ($is_ipv4_and_has_port) {
$port = substr($hostname_w_port, strrpos($hostname_w_port, ':') + 1);
$hostname = preg_replace('/'. preg_quote(':' . $port, '/') . '$/', '', $hostname_w_port);
}
if (filter_var($hostname_w_port, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
$hostname = $hostname_w_port;
$port = null;
}
elseif (filter_var($hostname_w_port, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
$hostname = '[' . $hostname_w_port . ']';
$port = null;
}
else {
$hostname = preg_replace('/'. preg_quote(':' . $port, '/') . '$/', '', $hostname_w_port);
$port = null;
}
}
// Try to get MX if host is not [host]
if ($skip_lookup_mx === false) {
getmxrr($hostname, $mx_records, $mx_weight);
if (!empty($mx_records)) {
for ($i = 0; $i < count($mx_records); $i++) {
$mxs[$mx_records[$i]] = $mx_weight[$i];
}
asort ($mxs);
$records = array_keys($mxs);
echo 'Using first matched primary MX for "' . $hostname . '": ';
$hostname = $records[0];
echo $hostname . '<br>';
}
else {
echo 'No MX records for ' . $hostname . ' were found in DNS, skipping and using hostname as next-hop.<br>';
}
}
// Use port 25 if no port was given
$port = (empty($port)) ? 25 : $port;
$username = $transport_details['username'];
$password = $transport_details['password'];
$mail = new PHPMailer;
$mail->Timeout = 15;
$mail->SMTPOptions = array(
'ssl' => array(
'verify_peer' => false,
'verify_peer_name' => false,
'allow_self_signed' => true
)
);
$mail->SMTPDebug = 3;
// smtp: and smtp_enforced_tls: do not support wrapped tls, todo?
// change postfix map to detect wrapped tls or add a checkbox to toggle wrapped tls
// if ($port == 465) {
// $mail->SMTPSecure = "ssl";
// }
$mail->Debugoutput = function($str, $level) {
foreach(preg_split("/((\r?\n)|(\r\n?)|\n)/", $str) as $line){
if (empty($line)) { continue; }
if (preg_match("/SERVER \-\> CLIENT: 2\d\d.+/i", $line)) {
echo '<span style="color:darkgreen;font-weight:bold">' . htmlspecialchars($line) . '</span><br>';
}
elseif (preg_match("/SERVER \-\> CLIENT: 3\d\d.+/i", $line)) {
echo '<span style="color:lightgreen;font-weight:bold">' . htmlspecialchars($line) . '</span><br>';
}
elseif (preg_match("/SERVER \-\> CLIENT: 4\d\d.+/i", $line)) {
echo '<span style="color:yellow;font-weight:bold">' . htmlspecialchars($line) . '</span><br>';
}
elseif (preg_match("/SERVER \-\> CLIENT: 5\d\d.+/i", $line)) {
echo '<span style="color:red;font-weight:bold">' . htmlspecialchars($line) . '</span><br>';
}
elseif (preg_match("/CLIENT \-\> SERVER:.+/i", $line)) {
echo '<span style="color:#999;font-weight:bold">' . htmlspecialchars($line) . '</span><br>';
}
elseif (preg_match("/^(?!SERVER|CLIENT|Connection:|\)).+$/i", $line)) {
echo '<span>&nbsp;&nbsp;&nbsp;&nbsp;↪ ' . htmlspecialchars($line) . '</span><br>';
}
else {
echo htmlspecialchars($line) . '<br>';
}
}
};
$mail->isSMTP();
$mail->Host = $hostname;
if (!empty($username)) {
$mail->SMTPAuth = true;
$mail->Username = $username;
$mail->Password = $password;
}
$mail->Port = $port;
$mail->setFrom($mail_from, 'Mailer');
$mail->Subject = 'A subject for a SMTP test';
$mail->addAddress($mail_rcpt, 'Joe Null');
$mail->Body = 'This is our test body';
$mail->send();
}
else {
echo "Unknown transport.";
}
}
else {
echo "Permission denied.";
}

View File

@ -1,226 +1,226 @@
<?php
function acl($_action, $_scope = null, $_data = null) {
global $pdo;
global $lang;
$_data_log = $_data;
switch ($_action) {
case 'edit':
switch ($_scope) {
case 'user':
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
// Cast to array for single selections
$acls = (array)$_data['user_acl'];
// Create associative array from index array
// All set items are given 1 as value
foreach ($acls as $acl_key => $acl_val) {
$acl_post[$acl_val] = 1;
}
// Users cannot change their own ACL
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)
|| ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
// Read all available acl options by calling acl(get)
// Set all available acl options we cannot find in the post data to 0, else 1
$is_now = acl('get', 'user', $username);
if (!empty($is_now)) {
foreach ($is_now as $acl_now_name => $acl_now_val) {
$set_acls[$acl_now_name] = (isset($acl_post[$acl_now_name])) ? 1 : 0;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'Cannot determine current ACL'
);
continue;
}
foreach ($set_acls as $set_acl_key => $set_acl_val) {
$stmt = $pdo->prepare("UPDATE `user_acl` SET " . $set_acl_key . " = " . intval($set_acl_val) . "
WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('acl_saved', $username)
);
}
break;
case 'domainadmin':
if ($_SESSION['mailcow_cc_role'] != 'admin') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
// Cast to array for single selections
$acls = (array)$_data['da_acl'];
// Create associative array from index array
// All set items are given 1 as value
foreach ($acls as $acl_key => $acl_val) {
$acl_post[$acl_val] = 1;
}
// Users cannot change their own ACL
if ($_SESSION['mailcow_cc_role'] != 'admin') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
// Read all available acl options by calling acl(get)
// Set all available acl options we cannot find in the post data to 0, else 1
$is_now = acl('get', 'domainadmin', $username);
if (!empty($is_now)) {
foreach ($is_now as $acl_now_name => $acl_now_val) {
$set_acls[$acl_now_name] = (isset($acl_post[$acl_now_name])) ? 1 : 0;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'Cannot determine current ACL'
);
continue;
}
foreach ($set_acls as $set_acl_key => $set_acl_val) {
$stmt = $pdo->prepare("UPDATE `da_acl` SET " . $set_acl_key . " = " . intval($set_acl_val) . "
WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('acl_saved', $username)
);
}
break;
}
break;
case 'get':
switch ($_scope) {
case 'user':
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
$stmt = $pdo->prepare("SELECT * FROM `user_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $_data));
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if ($_SESSION['mailcow_cc_role'] == 'domainadmin') {
// Domain admins cannot see, add or remove user ACLs they don't have access to by themselves
// Editing a user will use acl("get", "user") to determine granted ACLs and therefore block unallowed access escalation via form editing
$self_da_acl = acl('get', 'domainadmin', $_SESSION['mailcow_cc_username']);
foreach ($self_da_acl as $self_da_acl_key => $self_da_acl_val) {
if ($self_da_acl_val == 0) {
unset($data[$self_da_acl_key]);
}
}
}
if (!empty($data)) {
unset($data['username']);
return $data;
}
else {
return false;
}
break;
case 'domainadmin':
if ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin') {
return false;
}
if ($_SESSION['mailcow_cc_role'] == 'domainadmin' && $_SESSION['mailcow_cc_username'] != $_data) {
return false;
}
$stmt = $pdo->prepare("SELECT * FROM `da_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $_data));
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($data)) {
unset($data['username']);
return $data;
}
else {
return false;
}
break;
}
break;
case 'to_session':
if (!isset($_SESSION['mailcow_cc_role'])) {
return false;
}
unset($_SESSION['acl']);
$username = strtolower(trim($_SESSION['mailcow_cc_username']));
// Admins get access to all modules
if ($_SESSION['mailcow_cc_role'] == 'admin' ||
(isset($_SESSION["dual-login"]["role"]) && $_SESSION["dual-login"]["role"] == 'admin')) {
$stmt = $pdo->query("SHOW COLUMNS FROM `user_acl` WHERE `Field` != 'username';");
$acl_all = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($acl_all)) {
$acl['acl'][$row['Field']] = 1;
}
$stmt = $pdo->query("SHOW COLUMNS FROM `da_acl` WHERE `Field` != 'username';");
$acl_all = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($acl_all)) {
$acl['acl'][$row['Field']] = 1;
}
}
elseif ($_SESSION['mailcow_cc_role'] == 'domainadmin' ||
(isset($_SESSION["dual-login"]["role"]) && $_SESSION["dual-login"]["role"] == 'domainadmin')) {
// Read all exting user_acl modules and set to 1
$stmt = $pdo->query("SHOW COLUMNS FROM `user_acl` WHERE `Field` != 'username';");
$acl_all = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($acl_all)) {
$acl['acl'][$row['Field']] = 1;
}
// Read da_acl rules for current user, OVERWRITE overlapping modules
// This prevents access to a users sync jobs, when a domain admins was not given access to sync jobs
$stmt = $pdo->prepare("SELECT * FROM `da_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => (isset($_SESSION["dual-login"]["username"])) ? $_SESSION["dual-login"]["username"] : $username));
$acl_user = $stmt->fetch(PDO::FETCH_ASSOC);
foreach ($acl_user as $acl_user_key => $acl_user_val) {
$acl['acl'][$acl_user_key] = $acl_user_val;
}
unset($acl['acl']['username']);
}
elseif ($_SESSION['mailcow_cc_role'] == 'user') {
$stmt = $pdo->prepare("SELECT * FROM `user_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$acl['acl'] = $stmt->fetch(PDO::FETCH_ASSOC);
unset($acl['acl']['username']);
}
if (!empty($acl)) {
$_SESSION = array_merge($_SESSION, $acl);
}
break;
}
<?php
function acl($_action, $_scope = null, $_data = null) {
global $pdo;
global $lang;
$_data_log = $_data;
switch ($_action) {
case 'edit':
switch ($_scope) {
case 'user':
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
// Cast to array for single selections
$acls = (array)$_data['user_acl'];
// Create associative array from index array
// All set items are given 1 as value
foreach ($acls as $acl_key => $acl_val) {
$acl_post[$acl_val] = 1;
}
// Users cannot change their own ACL
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)
|| ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
// Read all available acl options by calling acl(get)
// Set all available acl options we cannot find in the post data to 0, else 1
$is_now = acl('get', 'user', $username);
if (!empty($is_now)) {
foreach ($is_now as $acl_now_name => $acl_now_val) {
$set_acls[$acl_now_name] = (isset($acl_post[$acl_now_name])) ? 1 : 0;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'Cannot determine current ACL'
);
continue;
}
foreach ($set_acls as $set_acl_key => $set_acl_val) {
$stmt = $pdo->prepare("UPDATE `user_acl` SET " . $set_acl_key . " = " . intval($set_acl_val) . "
WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('acl_saved', $username)
);
}
break;
case 'domainadmin':
if ($_SESSION['mailcow_cc_role'] != 'admin') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
// Cast to array for single selections
$acls = (array)$_data['da_acl'];
// Create associative array from index array
// All set items are given 1 as value
foreach ($acls as $acl_key => $acl_val) {
$acl_post[$acl_val] = 1;
}
// Users cannot change their own ACL
if ($_SESSION['mailcow_cc_role'] != 'admin') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
// Read all available acl options by calling acl(get)
// Set all available acl options we cannot find in the post data to 0, else 1
$is_now = acl('get', 'domainadmin', $username);
if (!empty($is_now)) {
foreach ($is_now as $acl_now_name => $acl_now_val) {
$set_acls[$acl_now_name] = (isset($acl_post[$acl_now_name])) ? 1 : 0;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'Cannot determine current ACL'
);
continue;
}
foreach ($set_acls as $set_acl_key => $set_acl_val) {
$stmt = $pdo->prepare("UPDATE `da_acl` SET " . $set_acl_key . " = " . intval($set_acl_val) . "
WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('acl_saved', $username)
);
}
break;
}
break;
case 'get':
switch ($_scope) {
case 'user':
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
$stmt = $pdo->prepare("SELECT * FROM `user_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $_data));
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if ($_SESSION['mailcow_cc_role'] == 'domainadmin') {
// Domain admins cannot see, add or remove user ACLs they don't have access to by themselves
// Editing a user will use acl("get", "user") to determine granted ACLs and therefore block unallowed access escalation via form editing
$self_da_acl = acl('get', 'domainadmin', $_SESSION['mailcow_cc_username']);
foreach ($self_da_acl as $self_da_acl_key => $self_da_acl_val) {
if ($self_da_acl_val == 0) {
unset($data[$self_da_acl_key]);
}
}
}
if (!empty($data)) {
unset($data['username']);
return $data;
}
else {
return false;
}
break;
case 'domainadmin':
if ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin') {
return false;
}
if ($_SESSION['mailcow_cc_role'] == 'domainadmin' && $_SESSION['mailcow_cc_username'] != $_data) {
return false;
}
$stmt = $pdo->prepare("SELECT * FROM `da_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $_data));
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($data)) {
unset($data['username']);
return $data;
}
else {
return false;
}
break;
}
break;
case 'to_session':
if (!isset($_SESSION['mailcow_cc_role'])) {
return false;
}
unset($_SESSION['acl']);
$username = strtolower(trim($_SESSION['mailcow_cc_username']));
// Admins get access to all modules
if ($_SESSION['mailcow_cc_role'] == 'admin' ||
(isset($_SESSION["dual-login"]["role"]) && $_SESSION["dual-login"]["role"] == 'admin')) {
$stmt = $pdo->query("SHOW COLUMNS FROM `user_acl` WHERE `Field` != 'username';");
$acl_all = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($acl_all)) {
$acl['acl'][$row['Field']] = 1;
}
$stmt = $pdo->query("SHOW COLUMNS FROM `da_acl` WHERE `Field` != 'username';");
$acl_all = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($acl_all)) {
$acl['acl'][$row['Field']] = 1;
}
}
elseif ($_SESSION['mailcow_cc_role'] == 'domainadmin' ||
(isset($_SESSION["dual-login"]["role"]) && $_SESSION["dual-login"]["role"] == 'domainadmin')) {
// Read all exting user_acl modules and set to 1
$stmt = $pdo->query("SHOW COLUMNS FROM `user_acl` WHERE `Field` != 'username';");
$acl_all = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($acl_all)) {
$acl['acl'][$row['Field']] = 1;
}
// Read da_acl rules for current user, OVERWRITE overlapping modules
// This prevents access to a users sync jobs, when a domain admins was not given access to sync jobs
$stmt = $pdo->prepare("SELECT * FROM `da_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => (isset($_SESSION["dual-login"]["username"])) ? $_SESSION["dual-login"]["username"] : $username));
$acl_user = $stmt->fetch(PDO::FETCH_ASSOC);
foreach ($acl_user as $acl_user_key => $acl_user_val) {
$acl['acl'][$acl_user_key] = $acl_user_val;
}
unset($acl['acl']['username']);
}
elseif ($_SESSION['mailcow_cc_role'] == 'user') {
$stmt = $pdo->prepare("SELECT * FROM `user_acl` WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$acl['acl'] = $stmt->fetch(PDO::FETCH_ASSOC);
unset($acl['acl']['username']);
}
if (!empty($acl)) {
$_SESSION = array_merge($_SESSION, $acl);
}
break;
}
}

View File

@ -1,436 +1,436 @@
<?php
function bcc($_action, $_data = null, $_attr = null) {
global $pdo;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
return false;
}
switch ($_action) {
case 'add':
if (!isset($_SESSION['acl']['bcc_maps']) || $_SESSION['acl']['bcc_maps'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$local_dest = strtolower(trim($_data['local_dest']));
$bcc_dest = $_data['bcc_dest'];
$active = intval($_data['active']);
$type = $_data['type'];
if ($type != 'sender' && $type != 'rcpt') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'invalid_bcc_map_type'
);
return false;
}
if (empty($bcc_dest)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'bcc_empty'
);
return false;
}
if (is_valid_domain_name($local_dest)) {
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $local_dest)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$domain = idn_to_ascii($local_dest, 0, INTL_IDNA_VARIANT_UTS46);
$local_dest_sane = '@' . idn_to_ascii($local_dest, 0, INTL_IDNA_VARIANT_UTS46);
}
elseif (filter_var($local_dest, FILTER_VALIDATE_EMAIL)) {
$mailbox = mailbox('get', 'mailbox_details', $local_dest);
if ($mailbox === false && array_key_exists($local_dest, array_merge($direct_aliases, $shared_aliases)) === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $local_dest) &&
!hasAliasObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $local_dest)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$domain = idn_to_ascii(substr(strstr($local_dest, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46);
$local_dest_sane = $local_dest;
}
else {
return false;
}
if (!filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_must_be_email', htmlspecialchars($bcc_dest))
);
return false;
}
$stmt = $pdo->prepare("SELECT `id` FROM `bcc_maps`
WHERE `local_dest` = :local_dest AND `type` = :type");
$stmt->execute(array(':local_dest' => $local_dest_sane, ':type' => $type));
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_exists', htmlspecialchars($local_dest_sane), $type)
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `bcc_maps` (`local_dest`, `bcc_dest`, `domain`, `active`, `type`) VALUES
(:local_dest, :bcc_dest, :domain, :active, :type)");
$stmt->execute(array(
':local_dest' => $local_dest_sane,
':bcc_dest' => $bcc_dest,
':domain' => $domain,
':active' => $active,
':type' => $type
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'bcc_saved'
);
break;
case 'edit':
if (!isset($_SESSION['acl']['bcc_maps']) || $_SESSION['acl']['bcc_maps'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = bcc('details', $id);
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$bcc_dest = (!empty($_data['bcc_dest'])) ? $_data['bcc_dest'] : $is_now['bcc_dest'];
$local_dest = $is_now['local_dest'];
$type = (!empty($_data['type'])) ? $_data['type'] : $is_now['type'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
continue;
}
if (!filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_must_be_email', $bcc_dest)
);
continue;
}
if (empty($bcc_dest)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_must_be_email', $bcc_dest)
);
continue;
}
$stmt = $pdo->prepare("SELECT `id` FROM `bcc_maps`
WHERE `local_dest` = :local_dest AND `type` = :type");
$stmt->execute(array(':local_dest' => $local_dest, ':type' => $type));
$id_now = $stmt->fetch(PDO::FETCH_ASSOC)['id'];
if (isset($id_now) && $id_now != $id) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_exists', htmlspecialchars($local_dest), $type)
);
continue;
}
$stmt = $pdo->prepare("UPDATE `bcc_maps` SET `bcc_dest` = :bcc_dest, `active` = :active, `type` = :type WHERE `id`= :id");
$stmt->execute(array(
':bcc_dest' => $bcc_dest,
':active' => $active,
':type' => $type,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_edited', $bcc_dest)
);
}
break;
case 'details':
$bccdata = array();
$id = intval($_data);
$stmt = $pdo->prepare("SELECT `id`,
`local_dest`,
`bcc_dest`,
`active`,
`type`,
`created`,
`domain`,
`modified` FROM `bcc_maps`
WHERE `id` = :id");
$stmt->execute(array(':id' => $id));
$bccdata = $stmt->fetch(PDO::FETCH_ASSOC);
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $bccdata['domain'])) {
$bccdata = null;
return false;
}
return $bccdata;
break;
case 'get':
$bccdata = array();
$all_items = array();
$id = intval($_data);
$stmt = $pdo->query("SELECT `id`, `domain` FROM `bcc_maps`");
$all_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($all_items as $i) {
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $i['domain'])) {
$bccdata[] = $i['id'];
}
}
$all_items = null;
return $bccdata;
break;
case 'delete':
if (!isset($_SESSION['acl']['bcc_maps']) || $_SESSION['acl']['bcc_maps'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
if (!is_numeric($id)) {
return false;
}
$stmt = $pdo->prepare("SELECT `domain` FROM `bcc_maps` WHERE id = :id");
$stmt->execute(array(':id' => $id));
$domain = $stmt->fetch(PDO::FETCH_ASSOC)['domain'];
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_deleted', $id)
);
}
break;
}
}
function recipient_map($_action, $_data = null, $attr = null) {
global $pdo;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
switch ($_action) {
case 'add':
$old_dest = strtolower(trim($_data['recipient_map_old']));
if (substr($old_dest, 0, 1) == '@') {
$old_dest = substr($old_dest, 1);
}
$new_dest = strtolower(trim($_data['recipient_map_new']));
$active = intval($_data['active']);
if (is_valid_domain_name($old_dest)) {
$old_dest_sane = '@' . idn_to_ascii($old_dest, 0, INTL_IDNA_VARIANT_UTS46);
}
elseif (filter_var($old_dest, FILTER_VALIDATE_EMAIL)) {
$old_dest_sane = $old_dest;
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('invalid_recipient_map_old', htmlspecialchars($old_dest))
);
return false;
}
if (!filter_var($new_dest, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('invalid_recipient_map_new', htmlspecialchars($new_dest))
);
return false;
}
$rmaps = recipient_map('get');
foreach ($rmaps as $rmap) {
if (recipient_map('details', $rmap)['recipient_map_old'] == $old_dest_sane) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_exists', htmlspecialchars($old_dest_sane))
);
return false;
}
}
$stmt = $pdo->prepare("INSERT INTO `recipient_maps` (`old_dest`, `new_dest`, `active`) VALUES
(:old_dest, :new_dest, :active)");
$stmt->execute(array(
':old_dest' => $old_dest_sane,
':new_dest' => $new_dest,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_saved', htmlspecialchars($old_dest_sane))
);
break;
case 'edit':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = recipient_map('details', $id);
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$new_dest = (!empty($_data['recipient_map_new'])) ? $_data['recipient_map_new'] : $is_now['recipient_map_new'];
$old_dest = (!empty($_data['recipient_map_old'])) ? $_data['recipient_map_old'] : $is_now['recipient_map_old'];
if (substr($old_dest, 0, 1) == '@') {
$old_dest = substr($old_dest, 1);
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
continue;
}
if (is_valid_domain_name($old_dest)) {
$old_dest_sane = '@' . idn_to_ascii($old_dest, 0, INTL_IDNA_VARIANT_UTS46);
}
elseif (filter_var($old_dest, FILTER_VALIDATE_EMAIL)) {
$old_dest_sane = $old_dest;
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('invalid_recipient_map_old', htmlspecialchars($old_dest))
);
continue;
}
if (!filter_var($new_dest, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('invalid_recipient_map_new', htmlspecialchars($new_dest))
);
continue;
}
$rmaps = recipient_map('get');
foreach ($rmaps as $rmap) {
if ($rmap == $id) { continue; }
if (recipient_map('details', $rmap)['recipient_map_old'] == $old_dest_sane) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_exists', htmlspecialchars($old_dest_sane))
);
return false;
}
}
$stmt = $pdo->prepare("UPDATE `recipient_maps` SET
`old_dest` = :old_dest,
`new_dest` = :new_dest,
`active` = :active
WHERE `id`= :id");
$stmt->execute(array(
':old_dest' => $old_dest_sane,
':new_dest' => $new_dest,
':active' => $active,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_saved', htmlspecialchars($old_dest_sane))
);
}
break;
case 'details':
$mapdata = array();
$id = intval($_data);
$stmt = $pdo->prepare("SELECT `id`,
`old_dest` AS `recipient_map_old`,
`new_dest` AS `recipient_map_new`,
`active`,
`created`,
`modified` FROM `recipient_maps`
WHERE `id` = :id");
$stmt->execute(array(':id' => $id));
$mapdata = $stmt->fetch(PDO::FETCH_ASSOC);
return $mapdata;
break;
case 'get':
$mapdata = array();
$all_items = array();
$id = intval($_data);
$stmt = $pdo->query("SELECT `id` FROM `recipient_maps`");
$all_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($all_items as $i) {
$mapdata[] = $i['id'];
}
$all_items = null;
return $mapdata;
break;
case 'delete':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
if (!is_numeric($id)) {
return false;
}
$stmt = $pdo->prepare("DELETE FROM `recipient_maps` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_deleted', htmlspecialchars($id))
);
}
break;
}
}
<?php
function bcc($_action, $_data = null, $_attr = null) {
global $pdo;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
return false;
}
switch ($_action) {
case 'add':
if (!isset($_SESSION['acl']['bcc_maps']) || $_SESSION['acl']['bcc_maps'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$local_dest = strtolower(trim($_data['local_dest']));
$bcc_dest = $_data['bcc_dest'];
$active = intval($_data['active']);
$type = $_data['type'];
if ($type != 'sender' && $type != 'rcpt') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'invalid_bcc_map_type'
);
return false;
}
if (empty($bcc_dest)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'bcc_empty'
);
return false;
}
if (is_valid_domain_name($local_dest)) {
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $local_dest)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$domain = idn_to_ascii($local_dest, 0, INTL_IDNA_VARIANT_UTS46);
$local_dest_sane = '@' . idn_to_ascii($local_dest, 0, INTL_IDNA_VARIANT_UTS46);
}
elseif (filter_var($local_dest, FILTER_VALIDATE_EMAIL)) {
$mailbox = mailbox('get', 'mailbox_details', $local_dest);
if ($mailbox === false && array_key_exists($local_dest, array_merge($direct_aliases, $shared_aliases)) === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $local_dest) &&
!hasAliasObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $local_dest)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$domain = idn_to_ascii(substr(strstr($local_dest, '@'), 1), 0, INTL_IDNA_VARIANT_UTS46);
$local_dest_sane = $local_dest;
}
else {
return false;
}
if (!filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_must_be_email', htmlspecialchars($bcc_dest))
);
return false;
}
$stmt = $pdo->prepare("SELECT `id` FROM `bcc_maps`
WHERE `local_dest` = :local_dest AND `type` = :type");
$stmt->execute(array(':local_dest' => $local_dest_sane, ':type' => $type));
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_exists', htmlspecialchars($local_dest_sane), $type)
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `bcc_maps` (`local_dest`, `bcc_dest`, `domain`, `active`, `type`) VALUES
(:local_dest, :bcc_dest, :domain, :active, :type)");
$stmt->execute(array(
':local_dest' => $local_dest_sane,
':bcc_dest' => $bcc_dest,
':domain' => $domain,
':active' => $active,
':type' => $type
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'bcc_saved'
);
break;
case 'edit':
if (!isset($_SESSION['acl']['bcc_maps']) || $_SESSION['acl']['bcc_maps'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = bcc('details', $id);
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$bcc_dest = (!empty($_data['bcc_dest'])) ? $_data['bcc_dest'] : $is_now['bcc_dest'];
$local_dest = $is_now['local_dest'];
$type = (!empty($_data['type'])) ? $_data['type'] : $is_now['type'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
continue;
}
if (!filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_must_be_email', $bcc_dest)
);
continue;
}
if (empty($bcc_dest)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_must_be_email', $bcc_dest)
);
continue;
}
$stmt = $pdo->prepare("SELECT `id` FROM `bcc_maps`
WHERE `local_dest` = :local_dest AND `type` = :type");
$stmt->execute(array(':local_dest' => $local_dest, ':type' => $type));
$id_now = $stmt->fetch(PDO::FETCH_ASSOC)['id'];
if (isset($id_now) && $id_now != $id) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_exists', htmlspecialchars($local_dest), $type)
);
continue;
}
$stmt = $pdo->prepare("UPDATE `bcc_maps` SET `bcc_dest` = :bcc_dest, `active` = :active, `type` = :type WHERE `id`= :id");
$stmt->execute(array(
':bcc_dest' => $bcc_dest,
':active' => $active,
':type' => $type,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_edited', $bcc_dest)
);
}
break;
case 'details':
$bccdata = array();
$id = intval($_data);
$stmt = $pdo->prepare("SELECT `id`,
`local_dest`,
`bcc_dest`,
`active`,
`type`,
`created`,
`domain`,
`modified` FROM `bcc_maps`
WHERE `id` = :id");
$stmt->execute(array(':id' => $id));
$bccdata = $stmt->fetch(PDO::FETCH_ASSOC);
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $bccdata['domain'])) {
$bccdata = null;
return false;
}
return $bccdata;
break;
case 'get':
$bccdata = array();
$all_items = array();
$id = intval($_data);
$stmt = $pdo->query("SELECT `id`, `domain` FROM `bcc_maps`");
$all_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($all_items as $i) {
if (hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $i['domain'])) {
$bccdata[] = $i['id'];
}
}
$all_items = null;
return $bccdata;
break;
case 'delete':
if (!isset($_SESSION['acl']['bcc_maps']) || $_SESSION['acl']['bcc_maps'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
if (!is_numeric($id)) {
return false;
}
$stmt = $pdo->prepare("SELECT `domain` FROM `bcc_maps` WHERE id = :id");
$stmt->execute(array(':id' => $id));
$domain = $stmt->fetch(PDO::FETCH_ASSOC)['domain'];
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `bcc_maps` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('bcc_deleted', $id)
);
}
break;
}
}
function recipient_map($_action, $_data = null, $attr = null) {
global $pdo;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
switch ($_action) {
case 'add':
$old_dest = strtolower(trim($_data['recipient_map_old']));
if (substr($old_dest, 0, 1) == '@') {
$old_dest = substr($old_dest, 1);
}
$new_dest = strtolower(trim($_data['recipient_map_new']));
$active = intval($_data['active']);
if (is_valid_domain_name($old_dest)) {
$old_dest_sane = '@' . idn_to_ascii($old_dest, 0, INTL_IDNA_VARIANT_UTS46);
}
elseif (filter_var($old_dest, FILTER_VALIDATE_EMAIL)) {
$old_dest_sane = $old_dest;
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('invalid_recipient_map_old', htmlspecialchars($old_dest))
);
return false;
}
if (!filter_var($new_dest, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('invalid_recipient_map_new', htmlspecialchars($new_dest))
);
return false;
}
$rmaps = recipient_map('get');
foreach ($rmaps as $rmap) {
if (recipient_map('details', $rmap)['recipient_map_old'] == $old_dest_sane) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_exists', htmlspecialchars($old_dest_sane))
);
return false;
}
}
$stmt = $pdo->prepare("INSERT INTO `recipient_maps` (`old_dest`, `new_dest`, `active`) VALUES
(:old_dest, :new_dest, :active)");
$stmt->execute(array(
':old_dest' => $old_dest_sane,
':new_dest' => $new_dest,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_saved', htmlspecialchars($old_dest_sane))
);
break;
case 'edit':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = recipient_map('details', $id);
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$new_dest = (!empty($_data['recipient_map_new'])) ? $_data['recipient_map_new'] : $is_now['recipient_map_new'];
$old_dest = (!empty($_data['recipient_map_old'])) ? $_data['recipient_map_old'] : $is_now['recipient_map_old'];
if (substr($old_dest, 0, 1) == '@') {
$old_dest = substr($old_dest, 1);
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
continue;
}
if (is_valid_domain_name($old_dest)) {
$old_dest_sane = '@' . idn_to_ascii($old_dest, 0, INTL_IDNA_VARIANT_UTS46);
}
elseif (filter_var($old_dest, FILTER_VALIDATE_EMAIL)) {
$old_dest_sane = $old_dest;
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('invalid_recipient_map_old', htmlspecialchars($old_dest))
);
continue;
}
if (!filter_var($new_dest, FILTER_VALIDATE_EMAIL)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('invalid_recipient_map_new', htmlspecialchars($new_dest))
);
continue;
}
$rmaps = recipient_map('get');
foreach ($rmaps as $rmap) {
if ($rmap == $id) { continue; }
if (recipient_map('details', $rmap)['recipient_map_old'] == $old_dest_sane) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_exists', htmlspecialchars($old_dest_sane))
);
return false;
}
}
$stmt = $pdo->prepare("UPDATE `recipient_maps` SET
`old_dest` = :old_dest,
`new_dest` = :new_dest,
`active` = :active
WHERE `id`= :id");
$stmt->execute(array(
':old_dest' => $old_dest_sane,
':new_dest' => $new_dest,
':active' => $active,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_saved', htmlspecialchars($old_dest_sane))
);
}
break;
case 'details':
$mapdata = array();
$id = intval($_data);
$stmt = $pdo->prepare("SELECT `id`,
`old_dest` AS `recipient_map_old`,
`new_dest` AS `recipient_map_new`,
`active`,
`created`,
`modified` FROM `recipient_maps`
WHERE `id` = :id");
$stmt->execute(array(':id' => $id));
$mapdata = $stmt->fetch(PDO::FETCH_ASSOC);
return $mapdata;
break;
case 'get':
$mapdata = array();
$all_items = array();
$id = intval($_data);
$stmt = $pdo->query("SELECT `id` FROM `recipient_maps`");
$all_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($all_items as $i) {
$mapdata[] = $i['id'];
}
$all_items = null;
return $mapdata;
break;
case 'delete':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
if (!is_numeric($id)) {
return false;
}
$stmt = $pdo->prepare("DELETE FROM `recipient_maps` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_deleted', htmlspecialchars($id))
);
}
break;
}
}

View File

@ -1,246 +1,246 @@
<?php
function admin($_action, $_data = null) {
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
global $pdo;
global $lang;
$_data_log = $_data;
!isset($_data_log['password']) ?: $_data_log['password'] = '*';
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
switch ($_action) {
case 'add':
$username = strtolower(trim($_data['username']));
$password = $_data['password'];
$password2 = $_data['password2'];
$active = intval($_data['active']);
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username)
);
return false;
}
$stmt = $pdo->prepare("SELECT `username` FROM `admin`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
foreach ($num_results as $num_results_each) {
if ($num_results_each != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_exists', htmlspecialchars($username))
);
return false;
}
}
if (password_check($password, $password2) !== true) {
return false;
}
$password_hashed = hash_password($password);
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
VALUES (:username, :password_hashed, '1', :active)");
$stmt->execute(array(
':username' => $username,
':password_hashed' => $password_hashed,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('admin_added', htmlspecialchars($username))
);
break;
case 'edit':
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
$is_now = admin('details', $username);
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
continue;
}
$password = $_data['password'];
$password2 = $_data['password2'];
if ($active == 0) {
$left_active = 0;
foreach (admin('get') as $admin) {
$left_active = $left_active + admin('details', $admin)['active'];
}
if ($left_active == 1) {
$_SESSION['return'][] = array(
'type' => 'warning',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'no_active_admin'
);
continue;
}
}
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username_new)
);
continue;
}
if ($username_new != $username) {
if (!empty(admin('details', $username_new)['username'])) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username_new)
);
continue;
}
}
if (!empty($password)) {
if (password_check($password, $password2) !== true) {
return false;
}
$password_hashed = hash_password($password);
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
}
else {
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
$stmt->execute(array(
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('admin_modified', htmlspecialchars($username))
);
}
return true;
break;
case 'delete':
$usernames = (array)$_data['username'];
foreach ($usernames as $username) {
if ($_SESSION['mailcow_cc_username'] == $username) {
$_SESSION['return'][] = array(
'type' => 'warning',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'cannot_delete_self'
);
continue;
}
if (empty(admin('details', $username))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username)
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('admin_removed', htmlspecialchars($username))
);
}
break;
case 'get':
$admins = array();
$stmt = $pdo->query("SELECT `username` FROM `admin` WHERE `superadmin` = '1'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$admins[] = $row['username'];
}
return $admins;
break;
case 'details':
$admindata = array();
$stmt = $pdo->prepare("SELECT
`tfa`.`active` AS `tfa_active`,
`admin`.`username`,
`admin`.`created`,
`admin`.`active` AS `active`
FROM `admin`
LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`admin`.`username`
WHERE `admin`.`username`= :admin AND `superadmin` = '1'");
$stmt->execute(array(
':admin' => $_data
));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (empty($row)) {
return false;
}
$admindata['username'] = $row['username'];
$admindata['tfa_active'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
$admindata['tfa_active_int'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
$admindata['active'] = $row['active'];
$admindata['active_int'] = $row['active'];
$admindata['created'] = $row['created'];
return $admindata;
break;
}
}
<?php
function admin($_action, $_data = null) {
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
global $pdo;
global $lang;
$_data_log = $_data;
!isset($_data_log['password']) ?: $_data_log['password'] = '*';
!isset($_data_log['password2']) ?: $_data_log['password2'] = '*';
switch ($_action) {
case 'add':
$username = strtolower(trim($_data['username']));
$password = $_data['password'];
$password2 = $_data['password2'];
$active = intval($_data['active']);
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username)) || empty ($username) || $username == 'API') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username)
);
return false;
}
$stmt = $pdo->prepare("SELECT `username` FROM `admin`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
$stmt = $pdo->prepare("SELECT `username` FROM `domain_admins`
WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
$num_results[] = count($stmt->fetchAll(PDO::FETCH_ASSOC));
foreach ($num_results as $num_results_each) {
if ($num_results_each != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_exists', htmlspecialchars($username))
);
return false;
}
}
if (password_check($password, $password2) !== true) {
return false;
}
$password_hashed = hash_password($password);
$stmt = $pdo->prepare("INSERT INTO `admin` (`username`, `password`, `superadmin`, `active`)
VALUES (:username, :password_hashed, '1', :active)");
$stmt->execute(array(
':username' => $username,
':password_hashed' => $password_hashed,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('admin_added', htmlspecialchars($username))
);
break;
case 'edit':
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
$is_now = admin('details', $username);
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$username_new = (!empty($_data['username_new'])) ? $_data['username_new'] : $is_now['username'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
continue;
}
$password = $_data['password'];
$password2 = $_data['password2'];
if ($active == 0) {
$left_active = 0;
foreach (admin('get') as $admin) {
$left_active = $left_active + admin('details', $admin)['active'];
}
if ($left_active == 1) {
$_SESSION['return'][] = array(
'type' => 'warning',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'no_active_admin'
);
continue;
}
}
if (!ctype_alnum(str_replace(array('_', '.', '-'), '', $username_new))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username_new)
);
continue;
}
if ($username_new != $username) {
if (!empty(admin('details', $username_new)['username'])) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username_new)
);
continue;
}
}
if (!empty($password)) {
if (password_check($password, $password2) !== true) {
return false;
}
$password_hashed = hash_password($password);
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active, `password` = :password_hashed WHERE `username` = :username");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
}
else {
$stmt = $pdo->prepare("UPDATE `admin` SET `username` = :username_new, `active` = :active WHERE `username` = :username");
$stmt->execute(array(
':username_new' => $username_new,
':username' => $username,
':active' => $active
));
if (isset($_data['disable_tfa'])) {
$stmt = $pdo->prepare("UPDATE `tfa` SET `active` = '0' WHERE `username` = :username");
$stmt->execute(array(':username' => $username));
}
else {
$stmt = $pdo->prepare("UPDATE `tfa` SET `username` = :username_new WHERE `username` = :username");
$stmt->execute(array(':username_new' => $username_new, ':username' => $username));
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('admin_modified', htmlspecialchars($username))
);
}
return true;
break;
case 'delete':
$usernames = (array)$_data['username'];
foreach ($usernames as $username) {
if ($_SESSION['mailcow_cc_username'] == $username) {
$_SESSION['return'][] = array(
'type' => 'warning',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'cannot_delete_self'
);
continue;
}
if (empty(admin('details', $username))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('username_invalid', $username)
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `admin` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `domain_admins` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `tfa` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$stmt = $pdo->prepare("DELETE FROM `fido2` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username,
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('admin_removed', htmlspecialchars($username))
);
}
break;
case 'get':
$admins = array();
$stmt = $pdo->query("SELECT `username` FROM `admin` WHERE `superadmin` = '1'");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$admins[] = $row['username'];
}
return $admins;
break;
case 'details':
$admindata = array();
$stmt = $pdo->prepare("SELECT
`tfa`.`active` AS `tfa_active`,
`admin`.`username`,
`admin`.`created`,
`admin`.`active` AS `active`
FROM `admin`
LEFT OUTER JOIN `tfa` ON `tfa`.`username`=`admin`.`username`
WHERE `admin`.`username`= :admin AND `superadmin` = '1'");
$stmt->execute(array(
':admin' => $_data
));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (empty($row)) {
return false;
}
$admindata['username'] = $row['username'];
$admindata['tfa_active'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
$admindata['tfa_active_int'] = (is_null($row['tfa_active'])) ? 0 : $row['tfa_active'];
$admindata['active'] = $row['active'];
$admindata['active_int'] = $row['active'];
$admindata['created'] = $row['created'];
return $admindata;
break;
}
}

View File

@ -1,242 +1,242 @@
<?php
function app_passwd($_action, $_data = null) {
global $pdo;
global $lang;
$_data_log = $_data;
!isset($_data_log['app_passwd']) ?: $_data_log['app_passwd'] = '*';
!isset($_data_log['app_passwd2']) ?: $_data_log['app_passwd2'] = '*';
if (isset($_data['username']) && filter_var($_data['username'], FILTER_VALIDATE_EMAIL)) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data['username'])) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
else {
$username = $_data['username'];
}
}
else {
$username = $_SESSION['mailcow_cc_username'];
}
switch ($_action) {
case 'add':
$app_name = htmlspecialchars(trim($_data['app_name']));
$password = $_data['app_passwd'];
$password2 = $_data['app_passwd2'];
$active = intval($_data['active']);
$protocols = (array)$_data['protocols'];
$imap_access = (in_array('imap_access', $protocols)) ? 1 : 0;
$dav_access = (in_array('dav_access', $protocols)) ? 1 : 0;
$smtp_access = (in_array('smtp_access', $protocols)) ? 1 : 0;
$eas_access = (in_array('eas_access', $protocols)) ? 1 : 0;
$pop3_access = (in_array('pop3_access', $protocols)) ? 1 : 0;
$sieve_access = (in_array('sieve_access', $protocols)) ? 1 : 0;
$domain = mailbox('get', 'mailbox_details', $username)['domain'];
if (empty($domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'password_complexity'
);
return false;
}
if ($password != $password2) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'password_mismatch'
);
return false;
}
$password_hashed = hash_password($password);
if (empty($app_name)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'app_name_empty'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `app_passwd` (`name`, `mailbox`, `domain`, `password`, `imap_access`, `smtp_access`, `eas_access`, `dav_access`, `pop3_access`, `sieve_access`, `active`)
VALUES (:app_name, :mailbox, :domain, :password, :imap_access, :smtp_access, :eas_access, :dav_access, :pop3_access, :sieve_access, :active)");
$stmt->execute(array(
':app_name' => $app_name,
':mailbox' => $username,
':domain' => $domain,
':password' => $password_hashed,
':imap_access' => $imap_access,
':smtp_access' => $smtp_access,
':eas_access' => $eas_access,
':dav_access' => $dav_access,
':pop3_access' => $pop3_access,
':sieve_access' => $sieve_access,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'app_passwd_added'
);
break;
case 'edit':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = app_passwd('details', $id);
if (!empty($is_now)) {
$app_name = (!empty($_data['app_name'])) ? $_data['app_name'] : $is_now['name'];
$password = (!empty($_data['password'])) ? $_data['password'] : null;
$password2 = (!empty($_data['password2'])) ? $_data['password2'] : null;
if (isset($_data['protocols'])) {
$protocols = (array)$_data['protocols'];
$imap_access = (in_array('imap_access', $protocols)) ? 1 : 0;
$dav_access = (in_array('dav_access', $protocols)) ? 1 : 0;
$smtp_access = (in_array('smtp_access', $protocols)) ? 1 : 0;
$eas_access = (in_array('eas_access', $protocols)) ? 1 : 0;
$pop3_access = (in_array('pop3_access', $protocols)) ? 1 : 0;
$sieve_access = (in_array('sieve_access', $protocols)) ? 1 : 0;
}
else {
$imap_access = $is_now['imap_access'];
$smtp_access = $is_now['smtp_access'];
$dav_access = $is_now['dav_access'];
$eas_access = $is_now['eas_access'];
$pop3_access = $is_now['pop3_access'];
$sieve_access = $is_now['sieve_access'];
}
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('app_passwd_id_invalid', $id)
);
continue;
}
$app_name = htmlspecialchars(trim($app_name));
if (!empty($password) && !empty($password2)) {
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'password_complexity'
);
continue;
}
if ($password != $password2) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'password_mismatch'
);
continue;
}
$password_hashed = hash_password($password);
$stmt = $pdo->prepare("UPDATE `app_passwd` SET
`password` = :password_hashed
WHERE `mailbox` = :username AND `id` = :id");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username' => $username,
':id' => $id
));
}
$stmt = $pdo->prepare("UPDATE `app_passwd` SET
`name` = :app_name,
`mailbox` = :username,
`imap_access` = :imap_access,
`smtp_access` = :smtp_access,
`eas_access` = :eas_access,
`dav_access` = :dav_access,
`pop3_access` = :pop3_access,
`sieve_access` = :sieve_access,
`active` = :active
WHERE `id` = :id");
$stmt->execute(array(
':app_name' => $app_name,
':username' => $username,
':imap_access' => $imap_access,
':smtp_access' => $smtp_access,
':eas_access' => $eas_access,
':dav_access' => $dav_access,
':pop3_access' => $pop3_access,
':sieve_access' => $sieve_access,
':active' => $active,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_modified', htmlspecialchars(implode(', ', $ids)))
);
}
break;
case 'delete':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$stmt = $pdo->prepare("SELECT `mailbox` FROM `app_passwd` WHERE `id` = :id");
$stmt->execute(array(':id' => $id));
$mailbox = $stmt->fetch(PDO::FETCH_ASSOC)['mailbox'];
if (empty($mailbox)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'app_passwd_id_invalid'
);
return false;
}
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $mailbox)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("DELETE FROM `app_passwd` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('app_passwd_removed', htmlspecialchars($id))
);
}
break;
case 'get':
$app_passwds = array();
$stmt = $pdo->prepare("SELECT `id`, `name` FROM `app_passwd` WHERE `mailbox` = :username");
$stmt->execute(array(':username' => $username));
$app_passwds = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $app_passwds;
break;
case 'details':
$app_passwd_data = array();
$stmt = $pdo->prepare("SELECT *
FROM `app_passwd`
WHERE `id` = :id");
$stmt->execute(array(':id' => $_data));
$app_passwd_data = $stmt->fetch(PDO::FETCH_ASSOC);
if (empty($app_passwd_data)) {
return false;
}
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $app_passwd_data['mailbox'])) {
$app_passwd_data = array();
return false;
}
$app_passwd_data['name'] = htmlspecialchars(trim($app_passwd_data['name']));
return $app_passwd_data;
break;
}
}
<?php
function app_passwd($_action, $_data = null) {
global $pdo;
global $lang;
$_data_log = $_data;
!isset($_data_log['app_passwd']) ?: $_data_log['app_passwd'] = '*';
!isset($_data_log['app_passwd2']) ?: $_data_log['app_passwd2'] = '*';
if (isset($_data['username']) && filter_var($_data['username'], FILTER_VALIDATE_EMAIL)) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data['username'])) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
else {
$username = $_data['username'];
}
}
else {
$username = $_SESSION['mailcow_cc_username'];
}
switch ($_action) {
case 'add':
$app_name = htmlspecialchars(trim($_data['app_name']));
$password = $_data['app_passwd'];
$password2 = $_data['app_passwd2'];
$active = intval($_data['active']);
$protocols = (array)$_data['protocols'];
$imap_access = (in_array('imap_access', $protocols)) ? 1 : 0;
$dav_access = (in_array('dav_access', $protocols)) ? 1 : 0;
$smtp_access = (in_array('smtp_access', $protocols)) ? 1 : 0;
$eas_access = (in_array('eas_access', $protocols)) ? 1 : 0;
$pop3_access = (in_array('pop3_access', $protocols)) ? 1 : 0;
$sieve_access = (in_array('sieve_access', $protocols)) ? 1 : 0;
$domain = mailbox('get', 'mailbox_details', $username)['domain'];
if (empty($domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'password_complexity'
);
return false;
}
if ($password != $password2) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'password_mismatch'
);
return false;
}
$password_hashed = hash_password($password);
if (empty($app_name)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'app_name_empty'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `app_passwd` (`name`, `mailbox`, `domain`, `password`, `imap_access`, `smtp_access`, `eas_access`, `dav_access`, `pop3_access`, `sieve_access`, `active`)
VALUES (:app_name, :mailbox, :domain, :password, :imap_access, :smtp_access, :eas_access, :dav_access, :pop3_access, :sieve_access, :active)");
$stmt->execute(array(
':app_name' => $app_name,
':mailbox' => $username,
':domain' => $domain,
':password' => $password_hashed,
':imap_access' => $imap_access,
':smtp_access' => $smtp_access,
':eas_access' => $eas_access,
':dav_access' => $dav_access,
':pop3_access' => $pop3_access,
':sieve_access' => $sieve_access,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'app_passwd_added'
);
break;
case 'edit':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = app_passwd('details', $id);
if (!empty($is_now)) {
$app_name = (!empty($_data['app_name'])) ? $_data['app_name'] : $is_now['name'];
$password = (!empty($_data['password'])) ? $_data['password'] : null;
$password2 = (!empty($_data['password2'])) ? $_data['password2'] : null;
if (isset($_data['protocols'])) {
$protocols = (array)$_data['protocols'];
$imap_access = (in_array('imap_access', $protocols)) ? 1 : 0;
$dav_access = (in_array('dav_access', $protocols)) ? 1 : 0;
$smtp_access = (in_array('smtp_access', $protocols)) ? 1 : 0;
$eas_access = (in_array('eas_access', $protocols)) ? 1 : 0;
$pop3_access = (in_array('pop3_access', $protocols)) ? 1 : 0;
$sieve_access = (in_array('sieve_access', $protocols)) ? 1 : 0;
}
else {
$imap_access = $is_now['imap_access'];
$smtp_access = $is_now['smtp_access'];
$dav_access = $is_now['dav_access'];
$eas_access = $is_now['eas_access'];
$pop3_access = $is_now['pop3_access'];
$sieve_access = $is_now['sieve_access'];
}
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('app_passwd_id_invalid', $id)
);
continue;
}
$app_name = htmlspecialchars(trim($app_name));
if (!empty($password) && !empty($password2)) {
if (!preg_match('/' . $GLOBALS['PASSWD_REGEP'] . '/', $password)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'password_complexity'
);
continue;
}
if ($password != $password2) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'password_mismatch'
);
continue;
}
$password_hashed = hash_password($password);
$stmt = $pdo->prepare("UPDATE `app_passwd` SET
`password` = :password_hashed
WHERE `mailbox` = :username AND `id` = :id");
$stmt->execute(array(
':password_hashed' => $password_hashed,
':username' => $username,
':id' => $id
));
}
$stmt = $pdo->prepare("UPDATE `app_passwd` SET
`name` = :app_name,
`mailbox` = :username,
`imap_access` = :imap_access,
`smtp_access` = :smtp_access,
`eas_access` = :eas_access,
`dav_access` = :dav_access,
`pop3_access` = :pop3_access,
`sieve_access` = :sieve_access,
`active` = :active
WHERE `id` = :id");
$stmt->execute(array(
':app_name' => $app_name,
':username' => $username,
':imap_access' => $imap_access,
':smtp_access' => $smtp_access,
':eas_access' => $eas_access,
':dav_access' => $dav_access,
':pop3_access' => $pop3_access,
':sieve_access' => $sieve_access,
':active' => $active,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_modified', htmlspecialchars(implode(', ', $ids)))
);
}
break;
case 'delete':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$stmt = $pdo->prepare("SELECT `mailbox` FROM `app_passwd` WHERE `id` = :id");
$stmt->execute(array(':id' => $id));
$mailbox = $stmt->fetch(PDO::FETCH_ASSOC)['mailbox'];
if (empty($mailbox)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'app_passwd_id_invalid'
);
return false;
}
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $mailbox)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("DELETE FROM `app_passwd` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('app_passwd_removed', htmlspecialchars($id))
);
}
break;
case 'get':
$app_passwds = array();
$stmt = $pdo->prepare("SELECT `id`, `name` FROM `app_passwd` WHERE `mailbox` = :username");
$stmt->execute(array(':username' => $username));
$app_passwds = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $app_passwds;
break;
case 'details':
$app_passwd_data = array();
$stmt = $pdo->prepare("SELECT *
FROM `app_passwd`
WHERE `id` = :id");
$stmt->execute(array(':id' => $_data));
$app_passwd_data = $stmt->fetch(PDO::FETCH_ASSOC);
if (empty($app_passwd_data)) {
return false;
}
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $app_passwd_data['mailbox'])) {
$app_passwd_data = array();
return false;
}
$app_passwd_data['name'] = htmlspecialchars(trim($app_passwd_data['name']));
return $app_passwd_data;
break;
}
}

View File

@ -1,315 +1,315 @@
<?php
function customize($_action, $_item, $_data = null) {
global $redis;
global $lang;
switch ($_action) {
case 'add':
// disable functionality when demo mode is enabled
if ($GLOBALS["DEMO_MODE"]) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'demo_mode_enabled'
);
return false;
}
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'access_denied'
);
return false;
}
switch ($_item) {
case 'main_logo':
if (in_array($_data['main_logo']['type'], array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/x-png', 'image/png', 'image/svg+xml'))) {
try {
if (file_exists($_data['main_logo']['tmp_name']) !== true) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'img_tmp_missing'
);
return false;
}
$image = new Imagick($_data['main_logo']['tmp_name']);
if ($image->valid() !== true) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'img_invalid'
);
return false;
}
$image->destroy();
}
catch (ImagickException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'img_invalid'
);
return false;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'invalid_mime_type'
);
return false;
}
try {
$redis->Set('MAIN_LOGO', 'data:' . $_data['main_logo']['type'] . ';base64,' . base64_encode(file_get_contents($_data['main_logo']['tmp_name'])));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'upload_success'
);
break;
}
break;
case 'edit':
// disable functionality when demo mode is enabled
if ($GLOBALS["DEMO_MODE"]) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'demo_mode_enabled'
);
return false;
}
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'access_denied'
);
return false;
}
switch ($_item) {
case 'app_links':
$apps = (array)$_data['app'];
$links = (array)$_data['href'];
$out = array();
if (count($apps) == count($links)) {
for ($i = 0; $i < count($apps); $i++) {
$out[] = array($apps[$i] => $links[$i]);
}
try {
$redis->set('APP_LINKS', json_encode($out));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'app_links'
);
break;
case 'ui_texts':
$title_name = $_data['title_name'];
$main_name = $_data['main_name'];
$apps_name = $_data['apps_name'];
$help_text = $_data['help_text'];
$ui_footer = $_data['ui_footer'];
$ui_announcement_text = $_data['ui_announcement_text'];
$ui_announcement_type = (in_array($_data['ui_announcement_type'], array('info', 'warning', 'danger'))) ? $_data['ui_announcement_type'] : false;
$ui_announcement_active = (!empty($_data['ui_announcement_active']) ? 1 : 0);
try {
$redis->set('TITLE_NAME', htmlspecialchars($title_name));
$redis->set('MAIN_NAME', htmlspecialchars($main_name));
$redis->set('APPS_NAME', htmlspecialchars($apps_name));
$redis->set('HELP_TEXT', $help_text);
$redis->set('UI_FOOTER', $ui_footer);
$redis->set('UI_ANNOUNCEMENT_TEXT', $ui_announcement_text);
$redis->set('UI_ANNOUNCEMENT_TYPE', $ui_announcement_type);
$redis->set('UI_ANNOUNCEMENT_ACTIVE', $ui_announcement_active);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'ui_texts'
);
break;
case 'ip_check':
$ip_check = ($_data['ip_check_opt_in'] == "1") ? 1 : 0;
try {
$redis->set('IP_CHECK', $ip_check);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'ip_check_opt_in_modified'
);
break;
}
break;
case 'delete':
// disable functionality when demo mode is enabled
if ($GLOBALS["DEMO_MODE"]) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'demo_mode_enabled'
);
return false;
}
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'access_denied'
);
return false;
}
switch ($_item) {
case 'main_logo':
try {
if ($redis->del('MAIN_LOGO')) {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'reset_main_logo'
);
return true;
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
break;
}
break;
case 'get':
switch ($_item) {
case 'app_links':
try {
$app_links = json_decode($redis->get('APP_LINKS'), true);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
return ($app_links) ? $app_links : false;
break;
case 'main_logo':
try {
return $redis->get('MAIN_LOGO');
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
break;
case 'ui_texts':
try {
$data['title_name'] = ($title_name = $redis->get('TITLE_NAME')) ? $title_name : 'mailcow UI';
$data['main_name'] = ($main_name = $redis->get('MAIN_NAME')) ? $main_name : 'mailcow UI';
$data['apps_name'] = ($apps_name = $redis->get('APPS_NAME')) ? $apps_name : $lang['header']['apps'];
$data['help_text'] = ($help_text = $redis->get('HELP_TEXT')) ? $help_text : false;
if (!empty($redis->get('UI_IMPRESS'))) {
$redis->set('UI_FOOTER', $redis->get('UI_IMPRESS'));
$redis->del('UI_IMPRESS');
}
$data['ui_footer'] = ($ui_footer = $redis->get('UI_FOOTER')) ? $ui_footer : false;
$data['ui_announcement_text'] = ($ui_announcement_text = $redis->get('UI_ANNOUNCEMENT_TEXT')) ? $ui_announcement_text : false;
$data['ui_announcement_type'] = ($ui_announcement_type = $redis->get('UI_ANNOUNCEMENT_TYPE')) ? $ui_announcement_type : false;
$data['ui_announcement_active'] = ($redis->get('UI_ANNOUNCEMENT_ACTIVE') == 1) ? 1 : 0;
return $data;
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
break;
case 'main_logo_specs':
try {
$image = new Imagick();
$img_data = explode('base64,', customize('get', 'main_logo'));
if ($img_data[1]) {
$image->readImageBlob(base64_decode($img_data[1]));
return $image->identifyImage();
}
return false;
}
catch (ImagickException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'imagick_exception'
);
return false;
}
break;
case 'ip_check':
try {
$ip_check = ($ip_check = $redis->get('IP_CHECK')) ? $ip_check : 0;
return $ip_check;
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
break;
}
break;
}
}
<?php
function customize($_action, $_item, $_data = null) {
global $redis;
global $lang;
switch ($_action) {
case 'add':
// disable functionality when demo mode is enabled
if ($GLOBALS["DEMO_MODE"]) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'demo_mode_enabled'
);
return false;
}
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'access_denied'
);
return false;
}
switch ($_item) {
case 'main_logo':
if (in_array($_data['main_logo']['type'], array('image/gif', 'image/jpeg', 'image/pjpeg', 'image/x-png', 'image/png', 'image/svg+xml'))) {
try {
if (file_exists($_data['main_logo']['tmp_name']) !== true) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'img_tmp_missing'
);
return false;
}
$image = new Imagick($_data['main_logo']['tmp_name']);
if ($image->valid() !== true) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'img_invalid'
);
return false;
}
$image->destroy();
}
catch (ImagickException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'img_invalid'
);
return false;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'invalid_mime_type'
);
return false;
}
try {
$redis->Set('MAIN_LOGO', 'data:' . $_data['main_logo']['type'] . ';base64,' . base64_encode(file_get_contents($_data['main_logo']['tmp_name'])));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'upload_success'
);
break;
}
break;
case 'edit':
// disable functionality when demo mode is enabled
if ($GLOBALS["DEMO_MODE"]) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'demo_mode_enabled'
);
return false;
}
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'access_denied'
);
return false;
}
switch ($_item) {
case 'app_links':
$apps = (array)$_data['app'];
$links = (array)$_data['href'];
$out = array();
if (count($apps) == count($links)) {
for ($i = 0; $i < count($apps); $i++) {
$out[] = array($apps[$i] => $links[$i]);
}
try {
$redis->set('APP_LINKS', json_encode($out));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'app_links'
);
break;
case 'ui_texts':
$title_name = $_data['title_name'];
$main_name = $_data['main_name'];
$apps_name = $_data['apps_name'];
$help_text = $_data['help_text'];
$ui_footer = $_data['ui_footer'];
$ui_announcement_text = $_data['ui_announcement_text'];
$ui_announcement_type = (in_array($_data['ui_announcement_type'], array('info', 'warning', 'danger'))) ? $_data['ui_announcement_type'] : false;
$ui_announcement_active = (!empty($_data['ui_announcement_active']) ? 1 : 0);
try {
$redis->set('TITLE_NAME', htmlspecialchars($title_name));
$redis->set('MAIN_NAME', htmlspecialchars($main_name));
$redis->set('APPS_NAME', htmlspecialchars($apps_name));
$redis->set('HELP_TEXT', $help_text);
$redis->set('UI_FOOTER', $ui_footer);
$redis->set('UI_ANNOUNCEMENT_TEXT', $ui_announcement_text);
$redis->set('UI_ANNOUNCEMENT_TYPE', $ui_announcement_type);
$redis->set('UI_ANNOUNCEMENT_ACTIVE', $ui_announcement_active);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'ui_texts'
);
break;
case 'ip_check':
$ip_check = ($_data['ip_check_opt_in'] == "1") ? 1 : 0;
try {
$redis->set('IP_CHECK', $ip_check);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'ip_check_opt_in_modified'
);
break;
}
break;
case 'delete':
// disable functionality when demo mode is enabled
if ($GLOBALS["DEMO_MODE"]) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'demo_mode_enabled'
);
return false;
}
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'access_denied'
);
return false;
}
switch ($_item) {
case 'main_logo':
try {
if ($redis->del('MAIN_LOGO')) {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'reset_main_logo'
);
return true;
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
break;
}
break;
case 'get':
switch ($_item) {
case 'app_links':
try {
$app_links = json_decode($redis->get('APP_LINKS'), true);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
return ($app_links) ? $app_links : false;
break;
case 'main_logo':
try {
return $redis->get('MAIN_LOGO');
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
break;
case 'ui_texts':
try {
$data['title_name'] = ($title_name = $redis->get('TITLE_NAME')) ? $title_name : 'mailcow UI';
$data['main_name'] = ($main_name = $redis->get('MAIN_NAME')) ? $main_name : 'mailcow UI';
$data['apps_name'] = ($apps_name = $redis->get('APPS_NAME')) ? $apps_name : $lang['header']['apps'];
$data['help_text'] = ($help_text = $redis->get('HELP_TEXT')) ? $help_text : false;
if (!empty($redis->get('UI_IMPRESS'))) {
$redis->set('UI_FOOTER', $redis->get('UI_IMPRESS'));
$redis->del('UI_IMPRESS');
}
$data['ui_footer'] = ($ui_footer = $redis->get('UI_FOOTER')) ? $ui_footer : false;
$data['ui_announcement_text'] = ($ui_announcement_text = $redis->get('UI_ANNOUNCEMENT_TEXT')) ? $ui_announcement_text : false;
$data['ui_announcement_type'] = ($ui_announcement_type = $redis->get('UI_ANNOUNCEMENT_TYPE')) ? $ui_announcement_type : false;
$data['ui_announcement_active'] = ($redis->get('UI_ANNOUNCEMENT_ACTIVE') == 1) ? 1 : 0;
return $data;
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
break;
case 'main_logo_specs':
try {
$image = new Imagick();
$img_data = explode('base64,', customize('get', 'main_logo'));
if ($img_data[1]) {
$image->readImageBlob(base64_decode($img_data[1]));
return $image->identifyImage();
}
return false;
}
catch (ImagickException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => 'imagick_exception'
);
return false;
}
break;
case 'ip_check':
try {
$ip_check = ($ip_check = $redis->get('IP_CHECK')) ? $ip_check : 0;
return $ip_check;
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_item, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
break;
}
break;
}
}

View File

@ -1,325 +1,325 @@
<?php
function dkim($_action, $_data = null, $privkey = false) {
global $redis;
global $lang;
switch ($_action) {
case 'add':
$key_length = intval($_data['key_size']);
$dkim_selector = (isset($_data['dkim_selector'])) ? $_data['dkim_selector'] : 'dkim';
$domains = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['domains']));
$domains = array_filter($domains);
foreach ($domains as $domain) {
if (!is_valid_domain_name($domain) || !is_numeric($key_length)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
continue;
}
if ($redis->hGet('DKIM_PUB_KEYS', $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
continue;
}
if (!ctype_alnum(str_replace(['-', '_'], '', $dkim_selector))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
continue;
}
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('access_denied', $domain)
);
continue;
}
$config = array(
"digest_alg" => "sha256",
"private_key_bits" => $key_length,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
if ($keypair_ressource = openssl_pkey_new($config)) {
$key_details = openssl_pkey_get_details($keypair_ressource);
$pubKey = implode(array_slice(
array_filter(
explode(PHP_EOL, $key_details['key'])
), 1, -1)
);
// Save public key and selector to redis
try {
$redis->hSet('DKIM_PUB_KEYS', $domain, $pubKey);
$redis->hSet('DKIM_SELECTORS', $domain, $dkim_selector);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
continue;
}
// Export private key and save private key to redis
openssl_pkey_export($keypair_ressource, $privKey);
if (isset($privKey) && !empty($privKey)) {
try {
$redis->hSet('DKIM_PRIV_KEYS', $dkim_selector . '.' . $domain, trim($privKey));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
continue;
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_added', $domain)
);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
continue;
}
}
break;
case 'duplicate':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
$from_domain = $_data['from_domain'];
$from_domain_dkim = dkim('details', $from_domain, true);
if (empty($from_domain_dkim)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $from_domain)
);
continue;
}
$to_domains = (array)$_data['to_domain'];
$to_domains = array_filter($to_domains);
foreach ($to_domains as $to_domain) {
try {
$redis->hSet('DKIM_PUB_KEYS', $to_domain, $from_domain_dkim['pubkey']);
$redis->hSet('DKIM_SELECTORS', $to_domain, $from_domain_dkim['dkim_selector']);
$redis->hSet('DKIM_PRIV_KEYS', $from_domain_dkim['dkim_selector'] . '.' . $to_domain, base64_decode(trim($from_domain_dkim['privkey'])));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
continue;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_duplicated', $from_domain, $to_domain)
);
}
break;
case 'import':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
$private_key_input = trim($_data['private_key_file']);
$overwrite_existing = intval($_data['overwrite_existing']);
$private_key_normalized = preg_replace('~\r\n?~', "\n", $private_key_input);
$private_key = openssl_pkey_get_private($private_key_normalized);
if ($ssl_error = openssl_error_string()) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('private_key_error', $ssl_error)
);
return false;
}
// Explode by nl
$pem_public_key_array = explode(PHP_EOL, trim(openssl_pkey_get_details($private_key)['key']));
// Remove first and last line/item
array_shift($pem_public_key_array);
array_pop($pem_public_key_array);
// Implode as single string
$pem_public_key = implode('', (array)$pem_public_key_array);
$dkim_selector = (isset($_data['dkim_selector'])) ? $_data['dkim_selector'] : 'dkim';
$domain = $_data['domain'];
if (!is_valid_domain_name($domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
return false;
}
if ($redis->hGet('DKIM_PUB_KEYS', $domain)) {
if ($overwrite_existing == 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_exists', $domain)
);
return false;
}
}
if (!ctype_alnum($dkim_selector)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
return false;
}
try {
dkim('delete', array('domains' => $domain));
$redis->hSet('DKIM_PUB_KEYS', $domain, $pem_public_key);
$redis->hSet('DKIM_SELECTORS', $domain, $dkim_selector);
$redis->hSet('DKIM_PRIV_KEYS', $dkim_selector . '.' . $domain, $private_key_normalized);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
unset($private_key_normalized);
unset($private_key);
unset($private_key_input);
try {
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_added', $domain)
);
return true;
break;
case 'details':
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data) && $_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
$dkimdata = array();
if ($redis_dkim_key_data = $redis->hGet('DKIM_PUB_KEYS', $_data)) {
$dkimdata['pubkey'] = $redis_dkim_key_data;
if (strlen($dkimdata['pubkey']) < 391) {
$dkimdata['length'] = "1024";
}
elseif (strlen($dkimdata['pubkey']) < 736) {
$dkimdata['length'] = "2048";
}
elseif (strlen($dkimdata['pubkey']) < 1416) {
$dkimdata['length'] = "4096";
}
else {
$dkimdata['length'] = ">= 8192";
}
if ($GLOBALS['SPLIT_DKIM_255'] === true) {
$dkim_txt_tmp = str_split('v=DKIM1;k=rsa;t=s;s=email;p=' . $redis_dkim_key_data, 255);
$dkimdata['dkim_txt'] = sprintf('"%s"', implode('" "', (array)$dkim_txt_tmp ) );
}
else {
$dkimdata['dkim_txt'] = 'v=DKIM1;k=rsa;t=s;s=email;p=' . $redis_dkim_key_data;
}
$dkimdata['dkim_selector'] = $redis->hGet('DKIM_SELECTORS', $_data);
if ($GLOBALS['SHOW_DKIM_PRIV_KEYS'] || $privkey == true) {
$dkimdata['privkey'] = base64_encode($redis->hGet('DKIM_PRIV_KEYS', $dkimdata['dkim_selector'] . '.' . $_data));
}
else {
$dkimdata['privkey'] = '';
}
}
return $dkimdata;
break;
case 'blind':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
$blinddkim = array();
foreach ($redis->hKeys('DKIM_PUB_KEYS') as $redis_dkim_domain) {
$blinddkim[] = $redis_dkim_domain;
}
return array_diff($blinddkim, array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')));
break;
case 'delete':
$domains = (array)$_data['domains'];
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
foreach ($domains as $domain) {
if (!is_valid_domain_name($domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
continue;
}
try {
$selector = $redis->hGet('DKIM_SELECTORS', $domain);
$redis->hDel('DKIM_PUB_KEYS', $domain);
$redis->hDel('DKIM_PRIV_KEYS', $selector . '.' . $domain);
$redis->hDel('DKIM_SELECTORS', $domain);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
continue;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_removed', htmlspecialchars($domain))
);
}
break;
}
}
<?php
function dkim($_action, $_data = null, $privkey = false) {
global $redis;
global $lang;
switch ($_action) {
case 'add':
$key_length = intval($_data['key_size']);
$dkim_selector = (isset($_data['dkim_selector'])) ? $_data['dkim_selector'] : 'dkim';
$domains = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['domains']));
$domains = array_filter($domains);
foreach ($domains as $domain) {
if (!is_valid_domain_name($domain) || !is_numeric($key_length)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
continue;
}
if ($redis->hGet('DKIM_PUB_KEYS', $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
continue;
}
if (!ctype_alnum(str_replace(['-', '_'], '', $dkim_selector))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
continue;
}
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('access_denied', $domain)
);
continue;
}
$config = array(
"digest_alg" => "sha256",
"private_key_bits" => $key_length,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
);
if ($keypair_ressource = openssl_pkey_new($config)) {
$key_details = openssl_pkey_get_details($keypair_ressource);
$pubKey = implode(array_slice(
array_filter(
explode(PHP_EOL, $key_details['key'])
), 1, -1)
);
// Save public key and selector to redis
try {
$redis->hSet('DKIM_PUB_KEYS', $domain, $pubKey);
$redis->hSet('DKIM_SELECTORS', $domain, $dkim_selector);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
continue;
}
// Export private key and save private key to redis
openssl_pkey_export($keypair_ressource, $privKey);
if (isset($privKey) && !empty($privKey)) {
try {
$redis->hSet('DKIM_PRIV_KEYS', $dkim_selector . '.' . $domain, trim($privKey));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
continue;
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_added', $domain)
);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
continue;
}
}
break;
case 'duplicate':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
$from_domain = $_data['from_domain'];
$from_domain_dkim = dkim('details', $from_domain, true);
if (empty($from_domain_dkim)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $from_domain)
);
continue;
}
$to_domains = (array)$_data['to_domain'];
$to_domains = array_filter($to_domains);
foreach ($to_domains as $to_domain) {
try {
$redis->hSet('DKIM_PUB_KEYS', $to_domain, $from_domain_dkim['pubkey']);
$redis->hSet('DKIM_SELECTORS', $to_domain, $from_domain_dkim['dkim_selector']);
$redis->hSet('DKIM_PRIV_KEYS', $from_domain_dkim['dkim_selector'] . '.' . $to_domain, base64_decode(trim($from_domain_dkim['privkey'])));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
continue;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_duplicated', $from_domain, $to_domain)
);
}
break;
case 'import':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
$private_key_input = trim($_data['private_key_file']);
$overwrite_existing = intval($_data['overwrite_existing']);
$private_key_normalized = preg_replace('~\r\n?~', "\n", $private_key_input);
$private_key = openssl_pkey_get_private($private_key_normalized);
if ($ssl_error = openssl_error_string()) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('private_key_error', $ssl_error)
);
return false;
}
// Explode by nl
$pem_public_key_array = explode(PHP_EOL, trim(openssl_pkey_get_details($private_key)['key']));
// Remove first and last line/item
array_shift($pem_public_key_array);
array_pop($pem_public_key_array);
// Implode as single string
$pem_public_key = implode('', (array)$pem_public_key_array);
$dkim_selector = (isset($_data['dkim_selector'])) ? $_data['dkim_selector'] : 'dkim';
$domain = $_data['domain'];
if (!is_valid_domain_name($domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
return false;
}
if ($redis->hGet('DKIM_PUB_KEYS', $domain)) {
if ($overwrite_existing == 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_exists', $domain)
);
return false;
}
}
if (!ctype_alnum($dkim_selector)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
return false;
}
try {
dkim('delete', array('domains' => $domain));
$redis->hSet('DKIM_PUB_KEYS', $domain, $pem_public_key);
$redis->hSet('DKIM_SELECTORS', $domain, $dkim_selector);
$redis->hSet('DKIM_PRIV_KEYS', $dkim_selector . '.' . $domain, $private_key_normalized);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
unset($private_key_normalized);
unset($private_key);
unset($private_key_input);
try {
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_added', $domain)
);
return true;
break;
case 'details':
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data) && $_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
$dkimdata = array();
if ($redis_dkim_key_data = $redis->hGet('DKIM_PUB_KEYS', $_data)) {
$dkimdata['pubkey'] = $redis_dkim_key_data;
if (strlen($dkimdata['pubkey']) < 391) {
$dkimdata['length'] = "1024";
}
elseif (strlen($dkimdata['pubkey']) < 736) {
$dkimdata['length'] = "2048";
}
elseif (strlen($dkimdata['pubkey']) < 1416) {
$dkimdata['length'] = "4096";
}
else {
$dkimdata['length'] = ">= 8192";
}
if ($GLOBALS['SPLIT_DKIM_255'] === true) {
$dkim_txt_tmp = str_split('v=DKIM1;k=rsa;t=s;s=email;p=' . $redis_dkim_key_data, 255);
$dkimdata['dkim_txt'] = sprintf('"%s"', implode('" "', (array)$dkim_txt_tmp ) );
}
else {
$dkimdata['dkim_txt'] = 'v=DKIM1;k=rsa;t=s;s=email;p=' . $redis_dkim_key_data;
}
$dkimdata['dkim_selector'] = $redis->hGet('DKIM_SELECTORS', $_data);
if ($GLOBALS['SHOW_DKIM_PRIV_KEYS'] || $privkey == true) {
$dkimdata['privkey'] = base64_encode($redis->hGet('DKIM_PRIV_KEYS', $dkimdata['dkim_selector'] . '.' . $_data));
}
else {
$dkimdata['privkey'] = '';
}
}
return $dkimdata;
break;
case 'blind':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
$blinddkim = array();
foreach ($redis->hKeys('DKIM_PUB_KEYS') as $redis_dkim_domain) {
$blinddkim[] = $redis_dkim_domain;
}
return array_diff($blinddkim, array_merge(mailbox('get', 'domains'), mailbox('get', 'alias_domains')));
break;
case 'delete':
$domains = (array)$_data['domains'];
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
foreach ($domains as $domain) {
if (!is_valid_domain_name($domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_domain_or_sel_invalid', $domain)
);
continue;
}
try {
$selector = $redis->hGet('DKIM_SELECTORS', $domain);
$redis->hDel('DKIM_PUB_KEYS', $domain);
$redis->hDel('DKIM_PRIV_KEYS', $selector . '.' . $domain);
$redis->hDel('DKIM_SELECTORS', $domain);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('redis_error', $e)
);
continue;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => array('dkim_removed', htmlspecialchars($domain))
);
}
break;
}
}

View File

@ -1,196 +1,196 @@
<?php
function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $extra_headers = null) {
global $DOCKER_TIMEOUT;
global $redis;
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER,array('Content-Type: application/json' ));
// We are using our mail certificates for dockerapi, the names will not match, the certs are trusted anyway
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
switch($action) {
case 'get_id':
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
$containers = json_decode($response, true);
if (!empty($containers)) {
foreach ($containers as $container) {
if (isset($container['Config']['Labels']['com.docker.compose.service'])
&& $container['Config']['Labels']['com.docker.compose.service'] == $service_name
&& strtolower($container['Config']['Labels']['com.docker.compose.project']) == strtolower(getenv('COMPOSE_PROJECT_NAME'))) {
return trim($container['Id']);
}
}
}
}
return false;
break;
case 'containers':
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
$containers = json_decode($response, true);
if (!empty($containers)) {
foreach ($containers as $container) {
if (strtolower($container['Config']['Labels']['com.docker.compose.project']) == strtolower(getenv('COMPOSE_PROJECT_NAME'))) {
$out[$container['Config']['Labels']['com.docker.compose.service']]['State'] = $container['State'];
$out[$container['Config']['Labels']['com.docker.compose.service']]['Config'] = $container['Config'];
$out[$container['Config']['Labels']['com.docker.compose.service']]['Id'] = trim($container['Id']);
}
}
}
return (!empty($out)) ? $out : false;
}
return false;
break;
case 'info':
if (empty($service_name)) {
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
}
else {
$container_id = docker('get_id', $service_name);
if (ctype_xdigit($container_id)) {
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/' . $container_id . '/json');
}
else {
return false;
}
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
$decoded_response = json_decode($response, true);
if (!empty($decoded_response)) {
if (empty($service_name)) {
foreach ($decoded_response as $container) {
if (isset($container['Config']['Labels']['com.docker.compose.project'])
&& strtolower($container['Config']['Labels']['com.docker.compose.project']) == strtolower(getenv('COMPOSE_PROJECT_NAME'))) {
unset($container['Config']['Env']);
$out[$container['Config']['Labels']['com.docker.compose.service']]['State'] = $container['State'];
$out[$container['Config']['Labels']['com.docker.compose.service']]['Config'] = $container['Config'];
$out[$container['Config']['Labels']['com.docker.compose.service']]['Id'] = trim($container['Id']);
}
}
}
else {
if (isset($decoded_response['Config']['Labels']['com.docker.compose.project'])
&& strtolower($decoded_response['Config']['Labels']['com.docker.compose.project']) == strtolower(getenv('COMPOSE_PROJECT_NAME'))) {
unset($container['Config']['Env']);
$out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['State'] = $decoded_response['State'];
$out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['Config'] = $decoded_response['Config'];
$out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['Id'] = trim($decoded_response['Id']);
}
}
}
if (empty($response)) {
return true;
}
else {
return (!empty($out)) ? $out : false;
}
}
break;
case 'post':
if (!empty($attr1)) {
$container_id = docker('get_id', $service_name);
if (ctype_xdigit($container_id) && ctype_alnum($attr1)) {
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/' . $container_id . '/' . $attr1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
if (!empty($attr2)) {
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);
$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 $response;
}
}
}
}
break;
case 'container_stats':
if (empty($service_name)){
return false;
}
$container_id = $service_name;
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/container/' . $container_id . '/stats/update');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
$stats = json_decode($response, true);
if (!empty($stats)) return $stats;
}
return false;
break;
case 'host_stats':
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/host/stats');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
$stats = json_decode($response, true);
if (!empty($stats)) return $stats;
}
return false;
break;
}
}
<?php
function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $extra_headers = null) {
global $DOCKER_TIMEOUT;
global $redis;
$curl = curl_init();
curl_setopt($curl, CURLOPT_HTTPHEADER,array('Content-Type: application/json' ));
// We are using our mail certificates for dockerapi, the names will not match, the certs are trusted anyway
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
switch($action) {
case 'get_id':
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
$containers = json_decode($response, true);
if (!empty($containers)) {
foreach ($containers as $container) {
if (isset($container['Config']['Labels']['com.docker.compose.service'])
&& $container['Config']['Labels']['com.docker.compose.service'] == $service_name
&& strtolower($container['Config']['Labels']['com.docker.compose.project']) == strtolower(getenv('COMPOSE_PROJECT_NAME'))) {
return trim($container['Id']);
}
}
}
}
return false;
break;
case 'containers':
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
$containers = json_decode($response, true);
if (!empty($containers)) {
foreach ($containers as $container) {
if (strtolower($container['Config']['Labels']['com.docker.compose.project']) == strtolower(getenv('COMPOSE_PROJECT_NAME'))) {
$out[$container['Config']['Labels']['com.docker.compose.service']]['State'] = $container['State'];
$out[$container['Config']['Labels']['com.docker.compose.service']]['Config'] = $container['Config'];
$out[$container['Config']['Labels']['com.docker.compose.service']]['Id'] = trim($container['Id']);
}
}
}
return (!empty($out)) ? $out : false;
}
return false;
break;
case 'info':
if (empty($service_name)) {
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
}
else {
$container_id = docker('get_id', $service_name);
if (ctype_xdigit($container_id)) {
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/' . $container_id . '/json');
}
else {
return false;
}
}
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
$decoded_response = json_decode($response, true);
if (!empty($decoded_response)) {
if (empty($service_name)) {
foreach ($decoded_response as $container) {
if (isset($container['Config']['Labels']['com.docker.compose.project'])
&& strtolower($container['Config']['Labels']['com.docker.compose.project']) == strtolower(getenv('COMPOSE_PROJECT_NAME'))) {
unset($container['Config']['Env']);
$out[$container['Config']['Labels']['com.docker.compose.service']]['State'] = $container['State'];
$out[$container['Config']['Labels']['com.docker.compose.service']]['Config'] = $container['Config'];
$out[$container['Config']['Labels']['com.docker.compose.service']]['Id'] = trim($container['Id']);
}
}
}
else {
if (isset($decoded_response['Config']['Labels']['com.docker.compose.project'])
&& strtolower($decoded_response['Config']['Labels']['com.docker.compose.project']) == strtolower(getenv('COMPOSE_PROJECT_NAME'))) {
unset($container['Config']['Env']);
$out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['State'] = $decoded_response['State'];
$out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['Config'] = $decoded_response['Config'];
$out[$decoded_response['Config']['Labels']['com.docker.compose.service']]['Id'] = trim($decoded_response['Id']);
}
}
}
if (empty($response)) {
return true;
}
else {
return (!empty($out)) ? $out : false;
}
}
break;
case 'post':
if (!empty($attr1)) {
$container_id = docker('get_id', $service_name);
if (ctype_xdigit($container_id) && ctype_alnum($attr1)) {
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/' . $container_id . '/' . $attr1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
if (!empty($attr2)) {
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);
$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 $response;
}
}
}
}
break;
case 'container_stats':
if (empty($service_name)){
return false;
}
$container_id = $service_name;
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/container/' . $container_id . '/stats/update');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
$stats = json_decode($response, true);
if (!empty($stats)) return $stats;
}
return false;
break;
case 'host_stats':
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/host/stats');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 0);
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
$response = curl_exec($curl);
if ($response === false) {
$err = curl_error($curl);
curl_close($curl);
return $err;
}
else {
curl_close($curl);
$stats = json_decode($response, true);
if (!empty($stats)) return $stats;
}
return false;
break;
}
}

View File

@ -1,333 +1,333 @@
<?php
function fail2ban($_action, $_data = null) {
global $redis;
$_data_log = $_data;
switch ($_action) {
case 'get':
$f2b_options = array();
if ($_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
try {
$f2b_options = json_decode($redis->Get('F2B_OPTIONS'), true);
$f2b_options['regex'] = json_decode($redis->Get('F2B_REGEX'), true);
$wl = $redis->hGetAll('F2B_WHITELIST');
if (is_array($wl)) {
foreach ($wl as $key => $value) {
$tmp_wl_data[] = $key;
}
if (isset($tmp_wl_data)) {
natsort($tmp_wl_data);
$f2b_options['whitelist'] = implode(PHP_EOL, (array)$tmp_wl_data);
}
else {
$f2b_options['whitelist'] = "";
}
}
else {
$f2b_options['whitelist'] = "";
}
$bl = $redis->hGetAll('F2B_BLACKLIST');
if (is_array($bl)) {
foreach ($bl as $key => $value) {
$tmp_bl_data[] = $key;
}
if (isset($tmp_bl_data)) {
natsort($tmp_bl_data);
$f2b_options['blacklist'] = implode(PHP_EOL, (array)$tmp_bl_data);
}
else {
$f2b_options['blacklist'] = "";
}
}
else {
$f2b_options['blacklist'] = "";
}
$pb = $redis->hGetAll('F2B_PERM_BANS');
if (is_array($pb)) {
foreach ($pb as $key => $value) {
$f2b_options['perm_bans'][] = array(
'network'=>$key,
'ip' => strtok($key,'/')
);
}
}
else {
$f2b_options['perm_bans'] = "";
}
$active_bans = $redis->hGetAll('F2B_ACTIVE_BANS');
$queue_unban = $redis->hGetAll('F2B_QUEUE_UNBAN');
if (is_array($active_bans)) {
foreach ($active_bans as $network => $banned_until) {
$queued_for_unban = (isset($queue_unban[$network]) && $queue_unban[$network] == 1) ? 1 : 0;
$difference = $banned_until - time();
$f2b_options['active_bans'][] = array(
'queued_for_unban' => $queued_for_unban,
'network' => $network,
'ip' => strtok($network,'/'),
'banned_until' => sprintf('%02dh %02dm %02ds', ($difference/3600), ($difference/60%60), $difference%60)
);
}
}
else {
$f2b_options['active_bans'] = "";
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return $f2b_options;
break;
case 'edit':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
// Start to read actions, if any
if (isset($_data['action'])) {
// Reset regex filters
if ($_data['action'] == "reset-regex") {
try {
$redis->Del('F2B_REGEX');
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
// Rules will also be recreated on log events, but rules may seem empty for a second in the UI
docker('post', 'netfilter-mailcow', 'restart');
$fail_count = 0;
$regex_result = json_decode($redis->Get('F2B_REGEX'), true);
while (empty($regex_result) && $fail_count < 10) {
$regex_result = json_decode($redis->Get('F2B_REGEX'), true);
$fail_count++;
sleep(1);
}
if ($fail_count >= 10) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('reset_f2b_regex')
);
return false;
}
}
elseif ($_data['action'] == "edit-regex") {
if (!empty($_data['regex'])) {
$rule_id = 1;
$regex_array = array();
foreach($_data['regex'] as $regex) {
$regex_array[$rule_id] = $regex;
$rule_id++;
}
if (!empty($regex_array)) {
$redis->Set('F2B_REGEX', json_encode($regex_array, JSON_UNESCAPED_SLASHES));
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_modified', htmlspecialchars($network))
);
return true;
}
// Start actions in dependency of network
if (!empty($_data['network'])) {
$networks = (array)$_data['network'];
foreach ($networks as $network) {
// Unban network
if ($_data['action'] == "unban") {
if (valid_network($network)) {
try {
$redis->hSet('F2B_QUEUE_UNBAN', $network, 1);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
}
// Whitelist network
elseif ($_data['action'] == "whitelist") {
if (empty($network)) { continue; }
if (valid_network($network)) {
try {
$redis->hSet('F2B_WHITELIST', $network, 1);
$redis->hDel('F2B_BLACKLIST', $network, 1);
$redis->hSet('F2B_QUEUE_UNBAN', $network, 1);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('network_host_invalid', $network)
);
continue;
}
}
// Blacklist network
elseif ($_data['action'] == "blacklist") {
if (empty($network)) { continue; }
if (valid_network($network) && !in_array($network, array(
'0.0.0.0',
'0.0.0.0/0',
getenv('IPV4_NETWORK') . '0/24',
getenv('IPV4_NETWORK') . '0',
getenv('IPV6_NETWORK')
))) {
try {
$redis->hSet('F2B_BLACKLIST', $network, 1);
$redis->hDel('F2B_WHITELIST', $network, 1);
//$response = docker('post', 'netfilter-mailcow', 'restart');
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('network_host_invalid', $network)
);
continue;
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_modified', htmlspecialchars($network))
);
}
return true;
}
}
// Start default edit without specific action
$is_now = fail2ban('get');
if (!empty($is_now)) {
$ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']);
$ban_time_increment = (isset($_data['ban_time_increment']) && $_data['ban_time_increment'] == "1") ? 1 : 0;
$max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['max_attempts']);
$max_ban_time = intval((isset($_data['max_ban_time'])) ? $_data['max_ban_time'] : $is_now['max_ban_time']);
$retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']);
$netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']);
$netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']);
$wl = (isset($_data['whitelist'])) ? $_data['whitelist'] : $is_now['whitelist'];
$bl = (isset($_data['blacklist'])) ? $_data['blacklist'] : $is_now['blacklist'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$f2b_options = array();
$f2b_options['ban_time'] = ($ban_time < 60) ? 60 : $ban_time;
$f2b_options['ban_time_increment'] = ($ban_time_increment == 1) ? true : false;
$f2b_options['max_ban_time'] = ($max_ban_time < 60) ? 60 : $max_ban_time;
$f2b_options['netban_ipv4'] = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
$f2b_options['netban_ipv6'] = ($netban_ipv6 < 8) ? 8 : $netban_ipv6;
$f2b_options['netban_ipv4'] = ($netban_ipv4 > 32) ? 32 : $netban_ipv4;
$f2b_options['netban_ipv6'] = ($netban_ipv6 > 128) ? 128 : $netban_ipv6;
$f2b_options['max_attempts'] = ($max_attempts < 1) ? 1 : $max_attempts;
$f2b_options['retry_window'] = ($retry_window < 1) ? 1 : $retry_window;
try {
$redis->Set('F2B_OPTIONS', json_encode($f2b_options));
$redis->Del('F2B_WHITELIST');
$redis->Del('F2B_BLACKLIST');
if(!empty($wl)) {
$wl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $wl));
$wl_array = array_filter($wl_array);
if (is_array($wl_array)) {
foreach ($wl_array as $wl_item) {
if (valid_network($wl_item) || valid_hostname($wl_item)) {
$redis->hSet('F2B_WHITELIST', $wl_item, 1);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('network_host_invalid', $wl_item)
);
continue;
}
}
}
}
if(!empty($bl)) {
$bl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $bl));
$bl_array = array_filter($bl_array);
if (is_array($bl_array)) {
foreach ($bl_array as $bl_item) {
if (valid_network($bl_item) && !in_array($bl_item, array(
'0.0.0.0',
'0.0.0.0/0',
getenv('IPV4_NETWORK') . '0/24',
getenv('IPV4_NETWORK') . '0',
getenv('IPV6_NETWORK')
))) {
$redis->hSet('F2B_BLACKLIST', $bl_item, 1);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('network_host_invalid', $bl_item)
);
continue;
}
}
}
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'f2b_modified'
);
break;
}
}
<?php
function fail2ban($_action, $_data = null) {
global $redis;
$_data_log = $_data;
switch ($_action) {
case 'get':
$f2b_options = array();
if ($_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
try {
$f2b_options = json_decode($redis->Get('F2B_OPTIONS'), true);
$f2b_options['regex'] = json_decode($redis->Get('F2B_REGEX'), true);
$wl = $redis->hGetAll('F2B_WHITELIST');
if (is_array($wl)) {
foreach ($wl as $key => $value) {
$tmp_wl_data[] = $key;
}
if (isset($tmp_wl_data)) {
natsort($tmp_wl_data);
$f2b_options['whitelist'] = implode(PHP_EOL, (array)$tmp_wl_data);
}
else {
$f2b_options['whitelist'] = "";
}
}
else {
$f2b_options['whitelist'] = "";
}
$bl = $redis->hGetAll('F2B_BLACKLIST');
if (is_array($bl)) {
foreach ($bl as $key => $value) {
$tmp_bl_data[] = $key;
}
if (isset($tmp_bl_data)) {
natsort($tmp_bl_data);
$f2b_options['blacklist'] = implode(PHP_EOL, (array)$tmp_bl_data);
}
else {
$f2b_options['blacklist'] = "";
}
}
else {
$f2b_options['blacklist'] = "";
}
$pb = $redis->hGetAll('F2B_PERM_BANS');
if (is_array($pb)) {
foreach ($pb as $key => $value) {
$f2b_options['perm_bans'][] = array(
'network'=>$key,
'ip' => strtok($key,'/')
);
}
}
else {
$f2b_options['perm_bans'] = "";
}
$active_bans = $redis->hGetAll('F2B_ACTIVE_BANS');
$queue_unban = $redis->hGetAll('F2B_QUEUE_UNBAN');
if (is_array($active_bans)) {
foreach ($active_bans as $network => $banned_until) {
$queued_for_unban = (isset($queue_unban[$network]) && $queue_unban[$network] == 1) ? 1 : 0;
$difference = $banned_until - time();
$f2b_options['active_bans'][] = array(
'queued_for_unban' => $queued_for_unban,
'network' => $network,
'ip' => strtok($network,'/'),
'banned_until' => sprintf('%02dh %02dm %02ds', ($difference/3600), ($difference/60%60), $difference%60)
);
}
}
else {
$f2b_options['active_bans'] = "";
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return $f2b_options;
break;
case 'edit':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
// Start to read actions, if any
if (isset($_data['action'])) {
// Reset regex filters
if ($_data['action'] == "reset-regex") {
try {
$redis->Del('F2B_REGEX');
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
// Rules will also be recreated on log events, but rules may seem empty for a second in the UI
docker('post', 'netfilter-mailcow', 'restart');
$fail_count = 0;
$regex_result = json_decode($redis->Get('F2B_REGEX'), true);
while (empty($regex_result) && $fail_count < 10) {
$regex_result = json_decode($redis->Get('F2B_REGEX'), true);
$fail_count++;
sleep(1);
}
if ($fail_count >= 10) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('reset_f2b_regex')
);
return false;
}
}
elseif ($_data['action'] == "edit-regex") {
if (!empty($_data['regex'])) {
$rule_id = 1;
$regex_array = array();
foreach($_data['regex'] as $regex) {
$regex_array[$rule_id] = $regex;
$rule_id++;
}
if (!empty($regex_array)) {
$redis->Set('F2B_REGEX', json_encode($regex_array, JSON_UNESCAPED_SLASHES));
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_modified', htmlspecialchars($network))
);
return true;
}
// Start actions in dependency of network
if (!empty($_data['network'])) {
$networks = (array)$_data['network'];
foreach ($networks as $network) {
// Unban network
if ($_data['action'] == "unban") {
if (valid_network($network)) {
try {
$redis->hSet('F2B_QUEUE_UNBAN', $network, 1);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
}
// Whitelist network
elseif ($_data['action'] == "whitelist") {
if (empty($network)) { continue; }
if (valid_network($network)) {
try {
$redis->hSet('F2B_WHITELIST', $network, 1);
$redis->hDel('F2B_BLACKLIST', $network, 1);
$redis->hSet('F2B_QUEUE_UNBAN', $network, 1);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('network_host_invalid', $network)
);
continue;
}
}
// Blacklist network
elseif ($_data['action'] == "blacklist") {
if (empty($network)) { continue; }
if (valid_network($network) && !in_array($network, array(
'0.0.0.0',
'0.0.0.0/0',
getenv('IPV4_NETWORK') . '0/24',
getenv('IPV4_NETWORK') . '0',
getenv('IPV6_NETWORK')
))) {
try {
$redis->hSet('F2B_BLACKLIST', $network, 1);
$redis->hDel('F2B_WHITELIST', $network, 1);
//$response = docker('post', 'netfilter-mailcow', 'restart');
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('network_host_invalid', $network)
);
continue;
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_modified', htmlspecialchars($network))
);
}
return true;
}
}
// Start default edit without specific action
$is_now = fail2ban('get');
if (!empty($is_now)) {
$ban_time = intval((isset($_data['ban_time'])) ? $_data['ban_time'] : $is_now['ban_time']);
$ban_time_increment = (isset($_data['ban_time_increment']) && $_data['ban_time_increment'] == "1") ? 1 : 0;
$max_attempts = intval((isset($_data['max_attempts'])) ? $_data['max_attempts'] : $is_now['max_attempts']);
$max_ban_time = intval((isset($_data['max_ban_time'])) ? $_data['max_ban_time'] : $is_now['max_ban_time']);
$retry_window = intval((isset($_data['retry_window'])) ? $_data['retry_window'] : $is_now['retry_window']);
$netban_ipv4 = intval((isset($_data['netban_ipv4'])) ? $_data['netban_ipv4'] : $is_now['netban_ipv4']);
$netban_ipv6 = intval((isset($_data['netban_ipv6'])) ? $_data['netban_ipv6'] : $is_now['netban_ipv6']);
$wl = (isset($_data['whitelist'])) ? $_data['whitelist'] : $is_now['whitelist'];
$bl = (isset($_data['blacklist'])) ? $_data['blacklist'] : $is_now['blacklist'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$f2b_options = array();
$f2b_options['ban_time'] = ($ban_time < 60) ? 60 : $ban_time;
$f2b_options['ban_time_increment'] = ($ban_time_increment == 1) ? true : false;
$f2b_options['max_ban_time'] = ($max_ban_time < 60) ? 60 : $max_ban_time;
$f2b_options['netban_ipv4'] = ($netban_ipv4 < 8) ? 8 : $netban_ipv4;
$f2b_options['netban_ipv6'] = ($netban_ipv6 < 8) ? 8 : $netban_ipv6;
$f2b_options['netban_ipv4'] = ($netban_ipv4 > 32) ? 32 : $netban_ipv4;
$f2b_options['netban_ipv6'] = ($netban_ipv6 > 128) ? 128 : $netban_ipv6;
$f2b_options['max_attempts'] = ($max_attempts < 1) ? 1 : $max_attempts;
$f2b_options['retry_window'] = ($retry_window < 1) ? 1 : $retry_window;
try {
$redis->Set('F2B_OPTIONS', json_encode($f2b_options));
$redis->Del('F2B_WHITELIST');
$redis->Del('F2B_BLACKLIST');
if(!empty($wl)) {
$wl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $wl));
$wl_array = array_filter($wl_array);
if (is_array($wl_array)) {
foreach ($wl_array as $wl_item) {
if (valid_network($wl_item) || valid_hostname($wl_item)) {
$redis->hSet('F2B_WHITELIST', $wl_item, 1);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('network_host_invalid', $wl_item)
);
continue;
}
}
}
}
if(!empty($bl)) {
$bl_array = array_map('trim', preg_split( "/( |,|;|\n)/", $bl));
$bl_array = array_filter($bl_array);
if (is_array($bl_array)) {
foreach ($bl_array as $bl_item) {
if (valid_network($bl_item) && !in_array($bl_item, array(
'0.0.0.0',
'0.0.0.0/0',
getenv('IPV4_NETWORK') . '0/24',
getenv('IPV4_NETWORK') . '0',
getenv('IPV6_NETWORK')
))) {
$redis->hSet('F2B_BLACKLIST', $bl_item, 1);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('network_host_invalid', $bl_item)
);
continue;
}
}
}
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'f2b_modified'
);
break;
}
}

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,121 +1,121 @@
<?php
function mailq($_action, $_data = null) {
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
function process_mailq_output($returned_output, $_action, $_data) {
if ($returned_output !== NULL) {
if ($_action == 'cat') {
logger(array('return' => array(
array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'queue_cat_success'
)
)));
return $returned_output;
}
else {
if (isset($returned_output['type']) && $returned_output['type'] == 'danger') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'Error: ' . $returned_output['msg']
);
}
if (isset($returned_output['type']) && $returned_output['type'] == 'success') {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'queue_command_success'
);
}
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'unknown'
);
}
}
if ($_action == 'get') {
$mailq_lines = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => 'list'));
$lines = 0;
// Hard limit to 10000 items
foreach (preg_split("/((\r?\n)|(\r\n?))/", $mailq_lines) as $mailq_item) if ($lines++ < 10000) {
if (empty($mailq_item) || $mailq_item == '1') {
continue;
}
$mq_line = json_decode($mailq_item, true);
if ($mq_line !== NULL) {
$rcpts = array();
foreach ($mq_line['recipients'] as $rcpt) {
if (isset($rcpt['delay_reason'])) {
$rcpts[] = $rcpt['address'] . ' (' . $rcpt['delay_reason'] . ')';
}
else {
$rcpts[] = $rcpt['address'];
}
}
if (!empty($rcpts)) {
$mq_line['recipients'] = $rcpts;
}
$line[] = $mq_line;
}
}
if (!isset($line) || empty($line)) {
return '[]';
}
else {
return json_encode($line);
}
}
elseif ($_action == 'delete') {
if (!is_array($_data['qid'])) {
$qids = array();
$qids[] = $_data['qid'];
}
else {
$qids = $_data['qid'];
}
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => 'delete', 'items' => $qids));
process_mailq_output(json_decode($docker_return, true), $_action, $_data);
}
elseif ($_action == 'cat') {
if (!is_array($_data['qid'])) {
$qids = array();
$qids[] = $_data['qid'];
}
else {
$qids = $_data['qid'];
}
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => 'cat', 'items' => $qids));
return process_mailq_output($docker_return, $_action, $_data);
}
elseif ($_action == 'edit') {
if (in_array($_data['action'], array('hold', 'unhold', 'deliver'))) {
if (!is_array($_data['qid'])) {
$qids = array();
$qids[] = $_data['qid'];
}
else {
$qids = $_data['qid'];
}
if (!empty($qids)) {
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => $_data['action'], 'items' => $qids));
process_mailq_output(json_decode($docker_return, true), $_action, $_data);
}
}
if (in_array($_data['action'], array('flush', 'super_delete'))) {
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => $_data['action']));
process_mailq_output(json_decode($docker_return, true), $_action, $_data);
}
}
}
<?php
function mailq($_action, $_data = null) {
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
function process_mailq_output($returned_output, $_action, $_data) {
if ($returned_output !== NULL) {
if ($_action == 'cat') {
logger(array('return' => array(
array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'queue_cat_success'
)
)));
return $returned_output;
}
else {
if (isset($returned_output['type']) && $returned_output['type'] == 'danger') {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'Error: ' . $returned_output['msg']
);
}
if (isset($returned_output['type']) && $returned_output['type'] == 'success') {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'queue_command_success'
);
}
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'unknown'
);
}
}
if ($_action == 'get') {
$mailq_lines = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => 'list'));
$lines = 0;
// Hard limit to 10000 items
foreach (preg_split("/((\r?\n)|(\r\n?))/", $mailq_lines) as $mailq_item) if ($lines++ < 10000) {
if (empty($mailq_item) || $mailq_item == '1') {
continue;
}
$mq_line = json_decode($mailq_item, true);
if ($mq_line !== NULL) {
$rcpts = array();
foreach ($mq_line['recipients'] as $rcpt) {
if (isset($rcpt['delay_reason'])) {
$rcpts[] = $rcpt['address'] . ' (' . $rcpt['delay_reason'] . ')';
}
else {
$rcpts[] = $rcpt['address'];
}
}
if (!empty($rcpts)) {
$mq_line['recipients'] = $rcpts;
}
$line[] = $mq_line;
}
}
if (!isset($line) || empty($line)) {
return '[]';
}
else {
return json_encode($line);
}
}
elseif ($_action == 'delete') {
if (!is_array($_data['qid'])) {
$qids = array();
$qids[] = $_data['qid'];
}
else {
$qids = $_data['qid'];
}
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => 'delete', 'items' => $qids));
process_mailq_output(json_decode($docker_return, true), $_action, $_data);
}
elseif ($_action == 'cat') {
if (!is_array($_data['qid'])) {
$qids = array();
$qids[] = $_data['qid'];
}
else {
$qids = $_data['qid'];
}
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => 'cat', 'items' => $qids));
return process_mailq_output($docker_return, $_action, $_data);
}
elseif ($_action == 'edit') {
if (in_array($_data['action'], array('hold', 'unhold', 'deliver'))) {
if (!is_array($_data['qid'])) {
$qids = array();
$qids[] = $_data['qid'];
}
else {
$qids = $_data['qid'];
}
if (!empty($qids)) {
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => $_data['action'], 'items' => $qids));
process_mailq_output(json_decode($docker_return, true), $_action, $_data);
}
}
if (in_array($_data['action'], array('flush', 'super_delete'))) {
$docker_return = docker('post', 'postfix-mailcow', 'exec', array('cmd' => 'mailq', 'task' => $_data['action']));
process_mailq_output(json_decode($docker_return, true), $_action, $_data);
}
}
}

View File

@ -1,242 +1,242 @@
<?php
function oauth2($_action, $_type, $_data = null) {
global $pdo;
global $redis;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'access_denied'
);
return false;
}
switch ($_action) {
case 'add':
switch ($_type) {
case 'client':
$client_id = bin2hex(random_bytes(6));
$client_secret = bin2hex(random_bytes(12));
$redirect_uri = $_data['redirect_uri'];
$scope = 'profile';
// For future use
// $grant_type = isset($_data['grant_type']) ? $_data['grant_type'] : 'authorization_code';
// $scope = isset($_data['scope']) ? $_data['scope'] : 'profile';
// if ($grant_type != "authorization_code" && $grant_type != "password") {
// $_SESSION['return'][] = array(
// 'type' => 'danger',
// 'log' => array(__FUNCTION__, $_action, $_type, $_data),
// 'msg' => 'access_denied'
// );
// return false;
// }
if ($scope != "profile") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'Invalid scope'
);
return false;
}
$stmt = $pdo->prepare("SELECT 'client' FROM `oauth_clients`
WHERE `client_id` = :client_id");
$stmt->execute(array(':client_id' => $client_id));
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'Client ID exists'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `oauth_clients` (`client_id`, `client_secret`, `redirect_uri`, `scope`)
VALUES (:client_id, :client_secret, :redirect_uri, :scope)");
$stmt->execute(array(
':client_id' => $client_id,
':client_secret' => $client_secret,
':redirect_uri' => $redirect_uri,
':scope' => $scope
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'Added client access'
);
break;
}
break;
case 'edit':
switch ($_type) {
case 'client':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = oauth2('details', 'client', $id);
if (!empty($is_now)) {
$redirect_uri = (!empty($_data['redirect_uri'])) ? $_data['redirect_uri'] : $is_now['redirect_uri'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'access_denied'
);
return false;
}
if (isset($_data['revoke_tokens'])) {
$stmt = $pdo->prepare("DELETE FROM `oauth_access_tokens`
WHERE `client_id` IN (
SELECT `client_id` FROM `oauth_clients` WHERE `id` = :id
)");
$stmt->execute(array(
':id' => $id
));
$stmt = $pdo->prepare("DELETE FROM `oauth_refresh_tokens`
WHERE `client_id` IN (
SELECT `client_id` FROM `oauth_clients` WHERE `id` = :id
)");
$stmt->execute(array(
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => array('object_modified', htmlspecialchars($id))
);
continue;
}
if (isset($_data['renew_secret'])) {
$client_secret = bin2hex(random_bytes(12));
$stmt = $pdo->prepare("UPDATE `oauth_clients` SET `client_secret` = :client_secret WHERE `id` = :id");
$stmt->execute(array(
':client_secret' => $client_secret,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => array('object_modified', htmlspecialchars($id))
);
continue;
}
if (empty($redirect_uri)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'Redirect/Callback URL cannot be empty'
);
continue;
}
$stmt = $pdo->prepare("UPDATE `oauth_clients` SET
`redirect_uri` = :redirect_uri
WHERE `id` = :id");
$stmt->execute(array(
':id' => $id,
':redirect_uri' => $redirect_uri
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => array('object_modified', htmlspecialchars($id))
);
}
break;
}
break;
case 'delete':
switch ($_type) {
case 'client':
(array)$ids = $_data['id'];
foreach ($ids as $id) {
if (!is_numeric($id)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `oauth_clients`
WHERE `id` = :id");
$stmt->execute(array(
':id' => $id
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => array('items_deleted', htmlspecialchars($id))
);
break;
case 'access_token':
(array)$access_tokens = $_data['access_token'];
foreach ($access_tokens as $access_token) {
if (!ctype_alnum($access_token)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("DELETE FROM `oauth_access_tokens`
WHERE `access_token` = :access_token");
$stmt->execute(array(
':access_token' => $access_token
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => sprintf($lang['success']['items_deleted'], implode(', ', (array)$access_tokens))
);
break;
case 'refresh_token':
(array)$refresh_tokens = $_data['refresh_token'];
foreach ($refresh_tokens as $refresh_token) {
if (!ctype_alnum($refresh_token)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("DELETE FROM `oauth_refresh_tokens` WHERE `refresh_token` = :refresh_token");
$stmt->execute(array(
':refresh_token' => $refresh_token
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => sprintf($lang['success']['items_deleted'], implode(', ', (array)$refresh_tokens))
);
break;
}
break;
case 'get':
switch ($_type) {
case 'clients':
$stmt = $pdo->query("SELECT `id` FROM `oauth_clients`");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$oauth_clients[] = $row['id'];
}
return $oauth_clients;
break;
}
break;
case 'details':
switch ($_type) {
case 'client':
$stmt = $pdo->prepare("SELECT * FROM `oauth_clients`
WHERE `id` = :id");
$stmt->execute(array(':id' => $_data));
$oauth_client_details = $stmt->fetch(PDO::FETCH_ASSOC);
return $oauth_client_details;
break;
}
break;
}
}
<?php
function oauth2($_action, $_type, $_data = null) {
global $pdo;
global $redis;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'access_denied'
);
return false;
}
switch ($_action) {
case 'add':
switch ($_type) {
case 'client':
$client_id = bin2hex(random_bytes(6));
$client_secret = bin2hex(random_bytes(12));
$redirect_uri = $_data['redirect_uri'];
$scope = 'profile';
// For future use
// $grant_type = isset($_data['grant_type']) ? $_data['grant_type'] : 'authorization_code';
// $scope = isset($_data['scope']) ? $_data['scope'] : 'profile';
// if ($grant_type != "authorization_code" && $grant_type != "password") {
// $_SESSION['return'][] = array(
// 'type' => 'danger',
// 'log' => array(__FUNCTION__, $_action, $_type, $_data),
// 'msg' => 'access_denied'
// );
// return false;
// }
if ($scope != "profile") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'Invalid scope'
);
return false;
}
$stmt = $pdo->prepare("SELECT 'client' FROM `oauth_clients`
WHERE `client_id` = :client_id");
$stmt->execute(array(':client_id' => $client_id));
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'Client ID exists'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `oauth_clients` (`client_id`, `client_secret`, `redirect_uri`, `scope`)
VALUES (:client_id, :client_secret, :redirect_uri, :scope)");
$stmt->execute(array(
':client_id' => $client_id,
':client_secret' => $client_secret,
':redirect_uri' => $redirect_uri,
':scope' => $scope
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'Added client access'
);
break;
}
break;
case 'edit':
switch ($_type) {
case 'client':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = oauth2('details', 'client', $id);
if (!empty($is_now)) {
$redirect_uri = (!empty($_data['redirect_uri'])) ? $_data['redirect_uri'] : $is_now['redirect_uri'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'access_denied'
);
return false;
}
if (isset($_data['revoke_tokens'])) {
$stmt = $pdo->prepare("DELETE FROM `oauth_access_tokens`
WHERE `client_id` IN (
SELECT `client_id` FROM `oauth_clients` WHERE `id` = :id
)");
$stmt->execute(array(
':id' => $id
));
$stmt = $pdo->prepare("DELETE FROM `oauth_refresh_tokens`
WHERE `client_id` IN (
SELECT `client_id` FROM `oauth_clients` WHERE `id` = :id
)");
$stmt->execute(array(
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => array('object_modified', htmlspecialchars($id))
);
continue;
}
if (isset($_data['renew_secret'])) {
$client_secret = bin2hex(random_bytes(12));
$stmt = $pdo->prepare("UPDATE `oauth_clients` SET `client_secret` = :client_secret WHERE `id` = :id");
$stmt->execute(array(
':client_secret' => $client_secret,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => array('object_modified', htmlspecialchars($id))
);
continue;
}
if (empty($redirect_uri)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'Redirect/Callback URL cannot be empty'
);
continue;
}
$stmt = $pdo->prepare("UPDATE `oauth_clients` SET
`redirect_uri` = :redirect_uri
WHERE `id` = :id");
$stmt->execute(array(
':id' => $id,
':redirect_uri' => $redirect_uri
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => array('object_modified', htmlspecialchars($id))
);
}
break;
}
break;
case 'delete':
switch ($_type) {
case 'client':
(array)$ids = $_data['id'];
foreach ($ids as $id) {
if (!is_numeric($id)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("DELETE FROM `oauth_clients`
WHERE `id` = :id");
$stmt->execute(array(
':id' => $id
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => array('items_deleted', htmlspecialchars($id))
);
break;
case 'access_token':
(array)$access_tokens = $_data['access_token'];
foreach ($access_tokens as $access_token) {
if (!ctype_alnum($access_token)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("DELETE FROM `oauth_access_tokens`
WHERE `access_token` = :access_token");
$stmt->execute(array(
':access_token' => $access_token
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => sprintf($lang['success']['items_deleted'], implode(', ', (array)$access_tokens))
);
break;
case 'refresh_token':
(array)$refresh_tokens = $_data['refresh_token'];
foreach ($refresh_tokens as $refresh_token) {
if (!ctype_alnum($refresh_token)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("DELETE FROM `oauth_refresh_tokens` WHERE `refresh_token` = :refresh_token");
$stmt->execute(array(
':refresh_token' => $refresh_token
));
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_type, $_data),
'msg' => sprintf($lang['success']['items_deleted'], implode(', ', (array)$refresh_tokens))
);
break;
}
break;
case 'get':
switch ($_type) {
case 'clients':
$stmt = $pdo->query("SELECT `id` FROM `oauth_clients`");
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($row = array_shift($rows)) {
$oauth_clients[] = $row['id'];
}
return $oauth_clients;
break;
}
break;
case 'details':
switch ($_type) {
case 'client':
$stmt = $pdo->prepare("SELECT * FROM `oauth_clients`
WHERE `id` = :id");
$stmt->execute(array(':id' => $_data));
$oauth_client_details = $stmt->fetch(PDO::FETCH_ASSOC);
return $oauth_client_details;
break;
}
break;
}
}

View File

@ -1,320 +1,320 @@
<?php
function policy($_action, $_scope, $_data = null) {
global $pdo;
global $redis;
global $lang;
$_data_log = $_data;
switch ($_action) {
case 'add':
if (!isset($_SESSION['acl']['spam_policy']) || $_SESSION['acl']['spam_policy'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($_scope) {
case 'domain':
$object = $_data['domain'];
if (is_valid_domain_name($object)) {
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$object = idn_to_ascii(strtolower(trim($object)), 0, INTL_IDNA_VARIANT_UTS46);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if ($_data['object_list'] == "bl") {
$object_list = "blacklist_from";
}
elseif ($_data['object_list'] == "wl") {
$object_list = "whitelist_from";
}
$object_from = trim(strtolower($_data['object_from']));
if (!ctype_alnum(str_replace(array('@', '_', '.', '-', '*'), '', $object_from))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'policy_list_from_invalid'
);
return false;
}
if ($object_list != "blacklist_from" && $object_list != "whitelist_from") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("SELECT `object` FROM `filterconf`
WHERE (`option` = 'whitelist_from' OR `option` = 'blacklist_from')
AND `object` = :object
AND `value` = :object_from");
$stmt->execute(array(':object' => $object, ':object_from' => $object_from));
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'policy_list_from_exists'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option` ,`value`)
VALUES (:object, :object_list, :object_from)");
$stmt->execute(array(
':object' => $object,
':object_list' => $object_list,
':object_from' => $object_from
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('domain_modified', $object)
);
break;
case 'mailbox':
$object = $_data['username'];
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if ($_data['object_list'] == "bl") {
$object_list = "blacklist_from";
}
elseif ($_data['object_list'] == "wl") {
$object_list = "whitelist_from";
}
$object_from = trim(strtolower($_data['object_from']));
if (!ctype_alnum(str_replace(array('@', '_', '.', '-', '*'), '', $object_from))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'policy_list_from_invalid'
);
return false;
}
if ($object_list != "blacklist_from" && $object_list != "whitelist_from") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("SELECT `object` FROM `filterconf`
WHERE (`option` = 'whitelist_from' OR `option` = 'blacklist_from')
AND `object` = :object
AND `value` = :object_from");
$stmt->execute(array(':object' => $object, ':object_from' => $object_from));
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'policy_list_from_exists'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option` ,`value`)
VALUES (:object, :object_list, :object_from)");
$stmt->execute(array(
':object' => $object,
':object_list' => $object_list,
':object_from' => $object_from
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('mailbox_modified', $object)
);
break;
}
break;
case 'delete':
if (!isset($_SESSION['acl']['spam_policy']) || $_SESSION['acl']['spam_policy'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($_scope) {
case 'domain':
(array)$prefids = $_data['prefid'];
foreach ($prefids as $prefid) {
if (!is_numeric($prefid)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("SELECT `object` FROM `filterconf` WHERE `prefid` = :prefid");
$stmt->execute(array(':prefid' => $prefid));
$object = $stmt->fetch(PDO::FETCH_ASSOC)['object'];
if (is_valid_domain_name($object)) {
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
$object = idn_to_ascii(strtolower(trim($object)), 0, INTL_IDNA_VARIANT_UTS46);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
try {
$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :object AND `prefid` = :prefid");
$stmt->execute(array(
':object' => $object,
':prefid' => $prefid
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('mysql_error', $e)
);
continue;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('item_deleted',$prefid)
);
}
break;
case 'mailbox':
if (!is_array($_data['prefid'])) {
$prefids = array();
$prefids[] = $_data['prefid'];
}
else {
$prefids = $_data['prefid'];
}
foreach ($prefids as $prefid) {
if (!is_numeric($prefid)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("SELECT `object` FROM `filterconf` WHERE `prefid` = :prefid");
$stmt->execute(array(':prefid' => $prefid));
$object = $stmt->fetch(PDO::FETCH_ASSOC)['object'];
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
try {
$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :object AND `prefid` = :prefid");
$stmt->execute(array(
':object' => $object,
':prefid' => $prefid
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('mysql_error', $e)
);
continue;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('items_deleted', implode(', ', (array)$prefids))
);
}
break;
}
break;
case 'get':
switch ($_scope) {
case 'domain':
if (!is_valid_domain_name($_data)) {
return false;
}
else {
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
$_data = idn_to_ascii(strtolower(trim($_data)), 0, INTL_IDNA_VARIANT_UTS46);
}
// WHITELIST
$stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='whitelist_from' AND (`object` LIKE :object_mail OR `object` = :object_domain)");
$stmt->execute(array(':object_mail' => '%@' . $_data, ':object_domain' => $_data));
$rows['whitelist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// BLACKLIST
$stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='blacklist_from' AND (`object` LIKE :object_mail OR `object` = :object_domain)");
$stmt->execute(array(':object_mail' => '%@' . $_data, ':object_domain' => $_data));
$rows['blacklist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $rows;
break;
case 'mailbox':
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
}
else {
$_data = $_SESSION['mailcow_cc_username'];
}
$domain = mailbox('get', 'mailbox_details', $_data)['domain'];
if (empty($domain)) {
return false;
}
// WHITELIST
$stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='whitelist_from' AND (`object` = :username OR `object` = :domain)");
$stmt->execute(array(':username' => $_data, ':domain' => $domain));
$rows['whitelist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// BLACKLIST
$stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='blacklist_from' AND (`object` = :username OR `object` = :domain)");
$stmt->execute(array(':username' => $_data, ':domain' => $domain));
$rows['blacklist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $rows;
break;
}
break;
}
}
<?php
function policy($_action, $_scope, $_data = null) {
global $pdo;
global $redis;
global $lang;
$_data_log = $_data;
switch ($_action) {
case 'add':
if (!isset($_SESSION['acl']['spam_policy']) || $_SESSION['acl']['spam_policy'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($_scope) {
case 'domain':
$object = $_data['domain'];
if (is_valid_domain_name($object)) {
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$object = idn_to_ascii(strtolower(trim($object)), 0, INTL_IDNA_VARIANT_UTS46);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if ($_data['object_list'] == "bl") {
$object_list = "blacklist_from";
}
elseif ($_data['object_list'] == "wl") {
$object_list = "whitelist_from";
}
$object_from = trim(strtolower($_data['object_from']));
if (!ctype_alnum(str_replace(array('@', '_', '.', '-', '*'), '', $object_from))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'policy_list_from_invalid'
);
return false;
}
if ($object_list != "blacklist_from" && $object_list != "whitelist_from") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("SELECT `object` FROM `filterconf`
WHERE (`option` = 'whitelist_from' OR `option` = 'blacklist_from')
AND `object` = :object
AND `value` = :object_from");
$stmt->execute(array(':object' => $object, ':object_from' => $object_from));
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'policy_list_from_exists'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option` ,`value`)
VALUES (:object, :object_list, :object_from)");
$stmt->execute(array(
':object' => $object,
':object_list' => $object_list,
':object_from' => $object_from
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('domain_modified', $object)
);
break;
case 'mailbox':
$object = $_data['username'];
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
if ($_data['object_list'] == "bl") {
$object_list = "blacklist_from";
}
elseif ($_data['object_list'] == "wl") {
$object_list = "whitelist_from";
}
$object_from = trim(strtolower($_data['object_from']));
if (!ctype_alnum(str_replace(array('@', '_', '.', '-', '*'), '', $object_from))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'policy_list_from_invalid'
);
return false;
}
if ($object_list != "blacklist_from" && $object_list != "whitelist_from") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("SELECT `object` FROM `filterconf`
WHERE (`option` = 'whitelist_from' OR `option` = 'blacklist_from')
AND `object` = :object
AND `value` = :object_from");
$stmt->execute(array(':object' => $object, ':object_from' => $object_from));
$num_results = count($stmt->fetchAll(PDO::FETCH_ASSOC));
if ($num_results != 0) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'policy_list_from_exists'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `filterconf` (`object`, `option` ,`value`)
VALUES (:object, :object_list, :object_from)");
$stmt->execute(array(
':object' => $object,
':object_list' => $object_list,
':object_from' => $object_from
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('mailbox_modified', $object)
);
break;
}
break;
case 'delete':
if (!isset($_SESSION['acl']['spam_policy']) || $_SESSION['acl']['spam_policy'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($_scope) {
case 'domain':
(array)$prefids = $_data['prefid'];
foreach ($prefids as $prefid) {
if (!is_numeric($prefid)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("SELECT `object` FROM `filterconf` WHERE `prefid` = :prefid");
$stmt->execute(array(':prefid' => $prefid));
$object = $stmt->fetch(PDO::FETCH_ASSOC)['object'];
if (is_valid_domain_name($object)) {
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
$object = idn_to_ascii(strtolower(trim($object)), 0, INTL_IDNA_VARIANT_UTS46);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
try {
$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :object AND `prefid` = :prefid");
$stmt->execute(array(
':object' => $object,
':prefid' => $prefid
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('mysql_error', $e)
);
continue;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('item_deleted',$prefid)
);
}
break;
case 'mailbox':
if (!is_array($_data['prefid'])) {
$prefids = array();
$prefids[] = $_data['prefid'];
}
else {
$prefids = $_data['prefid'];
}
foreach ($prefids as $prefid) {
if (!is_numeric($prefid)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("SELECT `object` FROM `filterconf` WHERE `prefid` = :prefid");
$stmt->execute(array(':prefid' => $prefid));
$object = $stmt->fetch(PDO::FETCH_ASSOC)['object'];
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
try {
$stmt = $pdo->prepare("DELETE FROM `filterconf` WHERE `object` = :object AND `prefid` = :prefid");
$stmt->execute(array(
':object' => $object,
':prefid' => $prefid
));
}
catch (PDOException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('mysql_error', $e)
);
continue;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('items_deleted', implode(', ', (array)$prefids))
);
}
break;
}
break;
case 'get':
switch ($_scope) {
case 'domain':
if (!is_valid_domain_name($_data)) {
return false;
}
else {
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
$_data = idn_to_ascii(strtolower(trim($_data)), 0, INTL_IDNA_VARIANT_UTS46);
}
// WHITELIST
$stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='whitelist_from' AND (`object` LIKE :object_mail OR `object` = :object_domain)");
$stmt->execute(array(':object_mail' => '%@' . $_data, ':object_domain' => $_data));
$rows['whitelist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// BLACKLIST
$stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='blacklist_from' AND (`object` LIKE :object_mail OR `object` = :object_domain)");
$stmt->execute(array(':object_mail' => '%@' . $_data, ':object_domain' => $_data));
$rows['blacklist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $rows;
break;
case 'mailbox':
if (isset($_data) && filter_var($_data, FILTER_VALIDATE_EMAIL)) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
}
else {
$_data = $_SESSION['mailcow_cc_username'];
}
$domain = mailbox('get', 'mailbox_details', $_data)['domain'];
if (empty($domain)) {
return false;
}
// WHITELIST
$stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='whitelist_from' AND (`object` = :username OR `object` = :domain)");
$stmt->execute(array(':username' => $_data, ':domain' => $domain));
$rows['whitelist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
// BLACKLIST
$stmt = $pdo->prepare("SELECT `object`, `value`, `prefid` FROM `filterconf` WHERE `option`='blacklist_from' AND (`object` = :username OR `object` = :domain)");
$stmt->execute(array(':username' => $_data, ':domain' => $domain));
$rows['blacklist'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $rows;
break;
}
break;
}
}

View File

@ -1,223 +1,223 @@
<?php
function pushover($_action, $_data = null) {
global $pdo;
switch ($_action) {
case 'edit':
if (!isset($_SESSION['acl']['pushover']) || $_SESSION['acl']['pushover'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
continue;
}
$delete = $_data['delete'];
if ($delete == "true") {
$stmt = $pdo->prepare("DELETE FROM `pushover` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'pushover_settings_edited'
);
continue;
}
$is_now = pushover('get', $username);
if (!empty($is_now)) {
$key = (!empty($_data['key'])) ? $_data['key'] : $is_now['key'];
$token = (!empty($_data['token'])) ? $_data['token'] : $is_now['token'];
$senders = (isset($_data['senders'])) ? $_data['senders'] : $is_now['senders'];
$senders_regex = (isset($_data['senders_regex'])) ? $_data['senders_regex'] : $is_now['senders_regex'];
$title = (!empty($_data['title'])) ? $_data['title'] : $is_now['title'];
$text = (!empty($_data['text'])) ? $_data['text'] : $is_now['text'];
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$evaluate_x_prio = (isset($_data['evaluate_x_prio'])) ? intval($_data['evaluate_x_prio']) : $is_now['evaluate_x_prio'];
$only_x_prio = (isset($_data['only_x_prio'])) ? intval($_data['only_x_prio']) : $is_now['only_x_prio'];
$sound = (isset($_data['sound'])) ? $_data['sound'] : $is_now['sound'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
continue;
}
if (!empty($senders_regex) && !is_valid_regex($senders_regex)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'Invalid regex'
);
continue;
}
$senders = array_map('trim', preg_split( "/( |,|;|\n)/", $senders));
foreach ($senders as $i => &$sender) {
if (empty($sender)) {
continue;
}
if (!filter_var($sender, FILTER_VALIDATE_EMAIL) === true) {
unset($senders[$i]);
continue;
}
$senders[$i] = preg_replace('/\.(?=.*?@gmail\.com$)/', '$1', $sender);
}
$senders = array_filter($senders);
if (empty($senders)) { $senders = ''; }
$senders = implode(",", (array)$senders);
if (!ctype_alnum($key) || strlen($key) != 30) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_data),
'msg' => 'pushover_key'
);
continue;
}
if (!ctype_alnum($token) || strlen($token) != 30) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_data),
'msg' => 'pushover_token'
);
continue;
}
$po_attributes = json_encode(
array(
'evaluate_x_prio' => strval(intval($evaluate_x_prio)),
'only_x_prio' => strval(intval($only_x_prio)),
'sound' => strval($sound)
)
);
$stmt = $pdo->prepare("REPLACE INTO `pushover` (`username`, `key`, `attributes`, `senders_regex`, `senders`, `token`, `title`, `text`, `active`)
VALUES (:username, :key, :po_attributes, :senders_regex, :senders, :token, :title, :text, :active)");
$stmt->execute(array(
':username' => $username,
':key' => $key,
':po_attributes' => $po_attributes,
':senders_regex' => $senders_regex,
':senders' => $senders,
':token' => $token,
':title' => $title,
':text' => $text,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'pushover_settings_edited'
);
}
break;
case 'get':
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("SELECT * FROM `pushover` WHERE `username` = :username");
$stmt->execute(array(
':username' => $_data
));
$data = $stmt->fetch(PDO::FETCH_ASSOC);
$data['attributes'] = json_decode($data['attributes'], true);
if (empty($data)) {
return false;
}
else {
return $data;
}
break;
case 'test':
if (!isset($_SESSION['acl']['pushover']) || $_SESSION['acl']['pushover'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("SELECT * FROM `pushover`
WHERE `username` = :username");
$stmt->execute(array(
':username' => $username
));
$api_data = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($api_data)) {
$title = (!empty($api_data['title'])) ? $api_data['title'] : 'Mail';
$text = (!empty($api_data['text'])) ? $api_data['text'] : 'You\'ve got mail 📧';
curl_setopt_array($ch = curl_init(), array(
CURLOPT_URL => "https://api.pushover.net/1/users/validate.json",
CURLOPT_POSTFIELDS => array(
"token" => $api_data['token'],
"user" => $api_data['key']
),
CURLOPT_SAFE_UPLOAD => true,
CURLOPT_RETURNTRANSFER => true,
));
$result = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpcode == 200) {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => sprintf('Pushover API OK (%d): %s', $httpcode, $result)
);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => sprintf('Pushover API ERR (%d): %s', $httpcode, $result)
);
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'pushover_credentials_missing'
);
return false;
}
}
break;
}
}
<?php
function pushover($_action, $_data = null) {
global $pdo;
switch ($_action) {
case 'edit':
if (!isset($_SESSION['acl']['pushover']) || $_SESSION['acl']['pushover'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
continue;
}
$delete = $_data['delete'];
if ($delete == "true") {
$stmt = $pdo->prepare("DELETE FROM `pushover` WHERE `username` = :username");
$stmt->execute(array(
':username' => $username
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'pushover_settings_edited'
);
continue;
}
$is_now = pushover('get', $username);
if (!empty($is_now)) {
$key = (!empty($_data['key'])) ? $_data['key'] : $is_now['key'];
$token = (!empty($_data['token'])) ? $_data['token'] : $is_now['token'];
$senders = (isset($_data['senders'])) ? $_data['senders'] : $is_now['senders'];
$senders_regex = (isset($_data['senders_regex'])) ? $_data['senders_regex'] : $is_now['senders_regex'];
$title = (!empty($_data['title'])) ? $_data['title'] : $is_now['title'];
$text = (!empty($_data['text'])) ? $_data['text'] : $is_now['text'];
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$evaluate_x_prio = (isset($_data['evaluate_x_prio'])) ? intval($_data['evaluate_x_prio']) : $is_now['evaluate_x_prio'];
$only_x_prio = (isset($_data['only_x_prio'])) ? intval($_data['only_x_prio']) : $is_now['only_x_prio'];
$sound = (isset($_data['sound'])) ? $_data['sound'] : $is_now['sound'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
continue;
}
if (!empty($senders_regex) && !is_valid_regex($senders_regex)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'Invalid regex'
);
continue;
}
$senders = array_map('trim', preg_split( "/( |,|;|\n)/", $senders));
foreach ($senders as $i => &$sender) {
if (empty($sender)) {
continue;
}
if (!filter_var($sender, FILTER_VALIDATE_EMAIL) === true) {
unset($senders[$i]);
continue;
}
$senders[$i] = preg_replace('/\.(?=.*?@gmail\.com$)/', '$1', $sender);
}
$senders = array_filter($senders);
if (empty($senders)) { $senders = ''; }
$senders = implode(",", (array)$senders);
if (!ctype_alnum($key) || strlen($key) != 30) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_data),
'msg' => 'pushover_key'
);
continue;
}
if (!ctype_alnum($token) || strlen($token) != 30) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_data),
'msg' => 'pushover_token'
);
continue;
}
$po_attributes = json_encode(
array(
'evaluate_x_prio' => strval(intval($evaluate_x_prio)),
'only_x_prio' => strval(intval($only_x_prio)),
'sound' => strval($sound)
)
);
$stmt = $pdo->prepare("REPLACE INTO `pushover` (`username`, `key`, `attributes`, `senders_regex`, `senders`, `token`, `title`, `text`, `active`)
VALUES (:username, :key, :po_attributes, :senders_regex, :senders, :token, :title, :text, :active)");
$stmt->execute(array(
':username' => $username,
':key' => $key,
':po_attributes' => $po_attributes,
':senders_regex' => $senders_regex,
':senders' => $senders,
':token' => $token,
':title' => $title,
':text' => $text,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'pushover_settings_edited'
);
}
break;
case 'get':
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
$stmt = $pdo->prepare("SELECT * FROM `pushover` WHERE `username` = :username");
$stmt->execute(array(
':username' => $_data
));
$data = $stmt->fetch(PDO::FETCH_ASSOC);
$data['attributes'] = json_decode($data['attributes'], true);
if (empty($data)) {
return false;
}
else {
return $data;
}
break;
case 'test':
if (!isset($_SESSION['acl']['pushover']) || $_SESSION['acl']['pushover'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
return false;
}
if (!is_array($_data['username'])) {
$usernames = array();
$usernames[] = $_data['username'];
}
else {
$usernames = $_data['username'];
}
foreach ($usernames as $username) {
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $username)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'access_denied'
);
continue;
}
$stmt = $pdo->prepare("SELECT * FROM `pushover`
WHERE `username` = :username");
$stmt->execute(array(
':username' => $username
));
$api_data = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($api_data)) {
$title = (!empty($api_data['title'])) ? $api_data['title'] : 'Mail';
$text = (!empty($api_data['text'])) ? $api_data['text'] : 'You\'ve got mail 📧';
curl_setopt_array($ch = curl_init(), array(
CURLOPT_URL => "https://api.pushover.net/1/users/validate.json",
CURLOPT_POSTFIELDS => array(
"token" => $api_data['token'],
"user" => $api_data['key']
),
CURLOPT_SAFE_UPLOAD => true,
CURLOPT_RETURNTRANSFER => true,
));
$result = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpcode == 200) {
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => sprintf('Pushover API OK (%d): %s', $httpcode, $result)
);
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => sprintf('Pushover API ERR (%d): %s', $httpcode, $result)
);
}
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data),
'msg' => 'pushover_credentials_missing'
);
return false;
}
}
break;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,150 +1,150 @@
<?php
function quota_notification($_action, $_data = null) {
global $redis;
$_data_log = $_data;
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($_action) {
case 'edit':
$retention_size = $_data['retention_size'];
if ($_data['release_format'] == 'attachment' || $_data['release_format'] == 'raw') {
$release_format = $_data['release_format'];
}
else {
$release_format = 'raw';
}
$subject = $_data['subject'];
$sender = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $_data['sender']);
if (filter_var($sender, FILTER_VALIDATE_EMAIL) === false) {
$sender = '';
}
$html = $_data['html_tmpl'];
try {
$redis->Set('QW_SENDER', $sender);
$redis->Set('QW_SUBJ', $subject);
$redis->Set('QW_HTML', $html);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'saved_settings'
);
break;
case 'get':
try {
$settings['subject'] = $redis->Get('QW_SUBJ');
$settings['sender'] = $redis->Get('QW_SENDER');
$settings['html_tmpl'] = htmlspecialchars($redis->Get('QW_HTML'));
if (empty($settings['html_tmpl'])) {
$settings['html_tmpl'] = htmlspecialchars(file_get_contents("/tpls/quota.tpl"));
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return $settings;
break;
}
}
function quota_notification_bcc($_action, $_data = null) {
global $redis;
$_data_log = $_data;
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($_action) {
case 'edit':
$domain = $_data['domain'];
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$active = intval($_data['active']);
$bcc_rcpts = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['bcc_rcpt']));
foreach ($bcc_rcpts as $i => &$rcpt) {
$rcpt = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $rcpt);
if (!empty($rcpt) && filter_var($rcpt, FILTER_VALIDATE_EMAIL) === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('goto_invalid', htmlspecialchars($rcpt))
);
unset($bcc_rcpts[$i]);
continue;
}
}
$bcc_rcpts = array_unique($bcc_rcpts);
$bcc_rcpts = array_filter($bcc_rcpts);
if (empty($bcc_rcpts)) {
$active = 0;
}
try {
$redis->hSet('QW_BCC', $domain, json_encode(array('bcc_rcpts' => $bcc_rcpts, 'active' => $active)));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'saved_settings'
);
break;
case 'get':
$domain = $_data;
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
try {
return json_decode($redis->hGet('QW_BCC', $domain), true);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
break;
}
}
<?php
function quota_notification($_action, $_data = null) {
global $redis;
$_data_log = $_data;
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($_action) {
case 'edit':
$retention_size = $_data['retention_size'];
if ($_data['release_format'] == 'attachment' || $_data['release_format'] == 'raw') {
$release_format = $_data['release_format'];
}
else {
$release_format = 'raw';
}
$subject = $_data['subject'];
$sender = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $_data['sender']);
if (filter_var($sender, FILTER_VALIDATE_EMAIL) === false) {
$sender = '';
}
$html = $_data['html_tmpl'];
try {
$redis->Set('QW_SENDER', $sender);
$redis->Set('QW_SUBJ', $subject);
$redis->Set('QW_HTML', $html);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'saved_settings'
);
break;
case 'get':
try {
$settings['subject'] = $redis->Get('QW_SUBJ');
$settings['sender'] = $redis->Get('QW_SENDER');
$settings['html_tmpl'] = htmlspecialchars($redis->Get('QW_HTML'));
if (empty($settings['html_tmpl'])) {
$settings['html_tmpl'] = htmlspecialchars(file_get_contents("/tpls/quota.tpl"));
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return $settings;
break;
}
}
function quota_notification_bcc($_action, $_data = null) {
global $redis;
$_data_log = $_data;
if ($_SESSION['mailcow_cc_role'] != "admin" && $_SESSION['mailcow_cc_role'] != "domainadmin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
switch ($_action) {
case 'edit':
$domain = $_data['domain'];
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$active = intval($_data['active']);
$bcc_rcpts = array_map('trim', preg_split( "/( |,|;|\n)/", $_data['bcc_rcpt']));
foreach ($bcc_rcpts as $i => &$rcpt) {
$rcpt = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $rcpt);
if (!empty($rcpt) && filter_var($rcpt, FILTER_VALIDATE_EMAIL) === false) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('goto_invalid', htmlspecialchars($rcpt))
);
unset($bcc_rcpts[$i]);
continue;
}
}
$bcc_rcpts = array_unique($bcc_rcpts);
$bcc_rcpts = array_filter($bcc_rcpts);
if (empty($bcc_rcpts)) {
$active = 0;
}
try {
$redis->hSet('QW_BCC', $domain, json_encode(array('bcc_rcpts' => $bcc_rcpts, 'active' => $active)));
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'saved_settings'
);
break;
case 'get':
$domain = $_data;
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $domain)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
try {
return json_decode($redis->hGet('QW_BCC', $domain), true);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
break;
}
}

View File

@ -1,242 +1,242 @@
<?php
function ratelimit($_action, $_scope, $_data = null) {
global $redis;
$_data_log = $_data;
switch ($_action) {
case 'edit':
if (!isset($_SESSION['acl']['ratelimit']) || $_SESSION['acl']['ratelimit'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
switch ($_scope) {
case 'domain':
if (!is_array($_data['object'])) {
$objects = array();
$objects[] = $_data['object'];
}
else {
$objects = $_data['object'];
}
foreach ($objects as $object) {
$rl_value = intval($_data['rl_value']);
$rl_frame = $_data['rl_frame'];
if (!in_array($rl_frame, array('s', 'm', 'h', 'd'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'rl_timeframe'
);
continue;
}
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
if (empty($rl_value)) {
try {
$redis->hDel('RL_VALUE', $object);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
else {
try {
$redis->hSet('RL_VALUE', $object, $rl_value . ' / 1' . $rl_frame);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('rl_saved', $object)
);
}
break;
case 'mailbox':
if (!is_array($_data['object'])) {
$objects = array();
$objects[] = $_data['object'];
}
else {
$objects = $_data['object'];
}
foreach ($objects as $object) {
$rl_value = intval($_data['rl_value']);
$rl_frame = $_data['rl_frame'];
if (!in_array($rl_frame, array('s', 'm', 'h', 'd'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'rl_timeframe'
);
continue;
}
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)
|| ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
if (empty($rl_value)) {
try {
$redis->hDel('RL_VALUE', $object);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
else {
try {
$redis->hSet('RL_VALUE', $object, $rl_value . ' / 1' . $rl_frame);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('rl_saved', $object)
);
}
break;
}
break;
case 'get':
switch ($_scope) {
case 'domain':
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
try {
if ($rl_value = $redis->hGet('RL_VALUE', $_data)) {
$rl = explode(' / 1', $rl_value);
$data['value'] = $rl[0];
$data['frame'] = $rl[1];
return $data;
}
else {
return false;
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return false;
break;
case 'mailbox':
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)
|| ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) {
return false;
}
try {
if ($rl_value = $redis->hGet('RL_VALUE', $_data)) {
$rl = explode(' / 1', $rl_value);
$data['value'] = $rl[0];
$data['frame'] = $rl[1];
return $data;
}
else {
return false;
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return false;
break;
}
break;
case 'delete':
$data['hash'] = $_data;
if ($_SESSION['mailcow_cc_role'] != 'admin' || !preg_match('/^RL[0-9A-Za-z=]+$/i', trim($data['hash']))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
try {
$data_rllog = $redis->lRange('RL_LOG', 0, -1);
if ($data_rllog) {
foreach ($data_rllog as $json_line) {
if (preg_match('/' . $data['hash'] . '/i', $json_line)) {
$redis->lRem('RL_LOG', $json_line, 0);
}
}
}
if ($redis->type($data['hash']) == Redis::REDIS_HASH) {
$redis->delete($data['hash']);
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'hash_deleted'
);
return true;
}
else {
$_SESSION['return'][] = array(
'type' => 'warning',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'hash_not_found'
);
return false;
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return false;
break;
}
<?php
function ratelimit($_action, $_scope, $_data = null) {
global $redis;
$_data_log = $_data;
switch ($_action) {
case 'edit':
if (!isset($_SESSION['acl']['ratelimit']) || $_SESSION['acl']['ratelimit'] != "1" ) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
switch ($_scope) {
case 'domain':
if (!is_array($_data['object'])) {
$objects = array();
$objects[] = $_data['object'];
}
else {
$objects = $_data['object'];
}
foreach ($objects as $object) {
$rl_value = intval($_data['rl_value']);
$rl_frame = $_data['rl_frame'];
if (!in_array($rl_frame, array('s', 'm', 'h', 'd'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'rl_timeframe'
);
continue;
}
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
if (empty($rl_value)) {
try {
$redis->hDel('RL_VALUE', $object);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
else {
try {
$redis->hSet('RL_VALUE', $object, $rl_value . ' / 1' . $rl_frame);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('rl_saved', $object)
);
}
break;
case 'mailbox':
if (!is_array($_data['object'])) {
$objects = array();
$objects[] = $_data['object'];
}
else {
$objects = $_data['object'];
}
foreach ($objects as $object) {
$rl_value = intval($_data['rl_value']);
$rl_frame = $_data['rl_frame'];
if (!in_array($rl_frame, array('s', 'm', 'h', 'd'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'rl_timeframe'
);
continue;
}
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $object)
|| ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'access_denied'
);
continue;
}
if (empty($rl_value)) {
try {
$redis->hDel('RL_VALUE', $object);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
else {
try {
$redis->hSet('RL_VALUE', $object, $rl_value . ' / 1' . $rl_frame);
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
continue;
}
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('rl_saved', $object)
);
}
break;
}
break;
case 'get':
switch ($_scope) {
case 'domain':
if (!hasDomainAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)) {
return false;
}
try {
if ($rl_value = $redis->hGet('RL_VALUE', $_data)) {
$rl = explode(' / 1', $rl_value);
$data['value'] = $rl[0];
$data['frame'] = $rl[1];
return $data;
}
else {
return false;
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return false;
break;
case 'mailbox':
if (!hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $_data)
|| ($_SESSION['mailcow_cc_role'] != 'admin' && $_SESSION['mailcow_cc_role'] != 'domainadmin')) {
return false;
}
try {
if ($rl_value = $redis->hGet('RL_VALUE', $_data)) {
$rl = explode(' / 1', $rl_value);
$data['value'] = $rl[0];
$data['frame'] = $rl[1];
return $data;
}
else {
return false;
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return false;
break;
}
break;
case 'delete':
$data['hash'] = $_data;
if ($_SESSION['mailcow_cc_role'] != 'admin' || !preg_match('/^RL[0-9A-Za-z=]+$/i', trim($data['hash']))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_type, $_data_log, $_attr),
'msg' => 'access_denied'
);
return false;
}
try {
$data_rllog = $redis->lRange('RL_LOG', 0, -1);
if ($data_rllog) {
foreach ($data_rllog as $json_line) {
if (preg_match('/' . $data['hash'] . '/i', $json_line)) {
$redis->lRem('RL_LOG', $json_line, 0);
}
}
}
if ($redis->type($data['hash']) == Redis::REDIS_HASH) {
$redis->delete($data['hash']);
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'hash_deleted'
);
return true;
}
else {
$_SESSION['return'][] = array(
'type' => 'warning',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => 'hash_not_found'
);
return false;
}
}
catch (RedisException $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_scope, $_data_log),
'msg' => array('redis_error', $e)
);
return false;
}
return false;
break;
}
}

View File

@ -1,211 +1,211 @@
<?php
function rsettings($_action, $_data = null) {
global $pdo;
global $lang;
$_data_log = $_data;
switch ($_action) {
case 'add':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$content = $_data['content'];
$desc = $_data['desc'];
$active = intval($_data['active']);
if (empty($content)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'map_content_empty'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `settingsmap` (`content`, `desc`, `active`)
VALUES (:content, :desc, :active)");
$stmt->execute(array(
':content' => $content,
':desc' => $desc,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'settings_map_added'
);
break;
case 'edit':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = rsettings('details', $id);
if (!empty($is_now)) {
$content = (!empty($_data['content'])) ? $_data['content'] : $is_now['content'];
$desc = (!empty($_data['desc'])) ? $_data['desc'] : $is_now['desc'];
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('settings_map_invalid', $id)
);
continue;
}
$content = trim($content);
$stmt = $pdo->prepare("UPDATE `settingsmap` SET
`content` = :content,
`desc` = :desc,
`active` = :active
WHERE `id` = :id");
$stmt->execute(array(
':content' => $content,
':desc' => $desc,
':active' => $active,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_modified', htmlspecialchars(implode(',', $ids)))
);
}
break;
case 'delete':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$stmt = $pdo->prepare("DELETE FROM `settingsmap` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('settings_map_removed', htmlspecialchars($id))
);
}
break;
case 'get':
if ($_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
$settingsmaps = array();
$stmt = $pdo->query("SELECT `id`, `desc`, `active` FROM `settingsmap`");
$settingsmaps = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $settingsmaps;
break;
case 'details':
if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
return false;
}
$settingsmapdata = array();
$stmt = $pdo->prepare("SELECT `id`,
`desc`,
`content`,
`active`
FROM `settingsmap`
WHERE `id` = :id");
$stmt->execute(array(':id' => $_data));
$settingsmapdata = $stmt->fetch(PDO::FETCH_ASSOC);
return $settingsmapdata;
break;
}
}
function rspamd_maps($_action, $_data = null) {
global $pdo;
global $lang;
global $RSPAMD_MAPS;
$_data_log = $_data;
switch ($_action) {
case 'edit':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, '-'),
'msg' => 'access_denied'
);
return false;
}
$maps = (array)$_data['map'];
foreach ($maps as $map) {
foreach ($RSPAMD_MAPS as $rspamd_map_type) {
if (!in_array($map, $rspamd_map_type)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, '-'),
'msg' => array('global_map_invalid', $map)
);
continue;
}
}
try {
if (file_exists('/rspamd_custom_maps/' . $map)) {
$map_content = trim($_data['rspamd_map_data']);
$map_handle = fopen('/rspamd_custom_maps/' . $map, 'w');
if (!$map_handle) {
throw new Exception($lang['danger']['file_open_error']);
}
fwrite($map_handle, $map_content . PHP_EOL);
fclose($map_handle);
sleep(1.5);
touch('/rspamd_custom_maps/' . $map);
}
}
catch (Exception $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, '-'),
'msg' => array('global_map_write_error', htmlspecialchars($map), htmlspecialchars($e->getMessage()))
);
continue;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, '-'),
'msg' => array('object_modified', htmlspecialchars($map))
);
}
break;
}
}
function rspamd_actions() {
if (isset($_SESSION["mailcow_cc_role"]) && $_SESSION["mailcow_cc_role"] == "admin") {
$curl = curl_init();
curl_setopt($curl, CURLOPT_UNIX_SOCKET_PATH, '/var/lib/rspamd/rspamd.sock');
curl_setopt($curl, CURLOPT_URL,"http://rspamd/stat");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($curl);
if ($data) {
$return = array();
$stats_array = json_decode($data, true)['actions'];
$stats_array['soft reject'] = $stats_array['soft reject'] + $stats_array['greylist'];
unset($stats_array['greylist']);
foreach ($stats_array as $action => $count) {
$return[] = array($action, $count);
}
return $return;
}
else {
return false;
}
}
else {
return false;
}
}
<?php
function rsettings($_action, $_data = null) {
global $pdo;
global $lang;
$_data_log = $_data;
switch ($_action) {
case 'add':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$content = $_data['content'];
$desc = $_data['desc'];
$active = intval($_data['active']);
if (empty($content)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'map_content_empty'
);
return false;
}
$stmt = $pdo->prepare("INSERT INTO `settingsmap` (`content`, `desc`, `active`)
VALUES (:content, :desc, :active)");
$stmt->execute(array(
':content' => $content,
':desc' => $desc,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'settings_map_added'
);
break;
case 'edit':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = rsettings('details', $id);
if (!empty($is_now)) {
$content = (!empty($_data['content'])) ? $_data['content'] : $is_now['content'];
$desc = (!empty($_data['desc'])) ? $_data['desc'] : $is_now['desc'];
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('settings_map_invalid', $id)
);
continue;
}
$content = trim($content);
$stmt = $pdo->prepare("UPDATE `settingsmap` SET
`content` = :content,
`desc` = :desc,
`active` = :active
WHERE `id` = :id");
$stmt->execute(array(
':content' => $content,
':desc' => $desc,
':active' => $active,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('object_modified', htmlspecialchars(implode(',', $ids)))
);
}
break;
case 'delete':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => 'access_denied'
);
return false;
}
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$stmt = $pdo->prepare("DELETE FROM `settingsmap` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data_log),
'msg' => array('settings_map_removed', htmlspecialchars($id))
);
}
break;
case 'get':
if ($_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
$settingsmaps = array();
$stmt = $pdo->query("SELECT `id`, `desc`, `active` FROM `settingsmap`");
$settingsmaps = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $settingsmaps;
break;
case 'details':
if ($_SESSION['mailcow_cc_role'] != "admin" || !isset($_data)) {
return false;
}
$settingsmapdata = array();
$stmt = $pdo->prepare("SELECT `id`,
`desc`,
`content`,
`active`
FROM `settingsmap`
WHERE `id` = :id");
$stmt->execute(array(':id' => $_data));
$settingsmapdata = $stmt->fetch(PDO::FETCH_ASSOC);
return $settingsmapdata;
break;
}
}
function rspamd_maps($_action, $_data = null) {
global $pdo;
global $lang;
global $RSPAMD_MAPS;
$_data_log = $_data;
switch ($_action) {
case 'edit':
if ($_SESSION['mailcow_cc_role'] != "admin") {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, '-'),
'msg' => 'access_denied'
);
return false;
}
$maps = (array)$_data['map'];
foreach ($maps as $map) {
foreach ($RSPAMD_MAPS as $rspamd_map_type) {
if (!in_array($map, $rspamd_map_type)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, '-'),
'msg' => array('global_map_invalid', $map)
);
continue;
}
}
try {
if (file_exists('/rspamd_custom_maps/' . $map)) {
$map_content = trim($_data['rspamd_map_data']);
$map_handle = fopen('/rspamd_custom_maps/' . $map, 'w');
if (!$map_handle) {
throw new Exception($lang['danger']['file_open_error']);
}
fwrite($map_handle, $map_content . PHP_EOL);
fclose($map_handle);
sleep(1.5);
touch('/rspamd_custom_maps/' . $map);
}
}
catch (Exception $e) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, '-'),
'msg' => array('global_map_write_error', htmlspecialchars($map), htmlspecialchars($e->getMessage()))
);
continue;
}
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, '-'),
'msg' => array('object_modified', htmlspecialchars($map))
);
}
break;
}
}
function rspamd_actions() {
if (isset($_SESSION["mailcow_cc_role"]) && $_SESSION["mailcow_cc_role"] == "admin") {
$curl = curl_init();
curl_setopt($curl, CURLOPT_UNIX_SOCKET_PATH, '/var/lib/rspamd/rspamd.sock');
curl_setopt($curl, CURLOPT_URL,"http://rspamd/stat");
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($curl);
if ($data) {
$return = array();
$stats_array = json_decode($data, true)['actions'];
$stats_array['soft reject'] = $stats_array['soft reject'] + $stats_array['greylist'];
unset($stats_array['greylist']);
foreach ($stats_array as $action => $count) {
$return[] = array($action, $count);
}
return $return;
}
else {
return false;
}
}
else {
return false;
}
}

View File

@ -1,172 +1,172 @@
<?php
function tls_policy_maps($_action, $_data = null, $attr = null) {
global $pdo;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
switch ($_action) {
case 'add':
$dest = idn_to_ascii(trim($_data['dest']), 0, INTL_IDNA_VARIANT_UTS46);
$policy = strtolower(trim($_data['policy']));
$parameters = (isset($_data['parameters']) && !empty($_data['parameters'])) ? $_data['parameters'] : '';
if (empty($dest) || in_array($dest, array('.', '*', '@'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'tls_policy_map_dest_invalid'
);
return false;
}
if (!empty($parameters)) {
foreach (explode(' ', $parameters) as $parameter) {
if (!preg_match('/(.+)\=(.+)/i', $parameter)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'tls_policy_map_parameter_invalid'
);
return false;
}
}
}
$active = intval($_data['active']);
$tls_policy_maps = tls_policy_maps('get');
foreach ($tls_policy_maps as $tls_policy_map) {
if (tls_policy_maps('details', $tls_policy_map)['dest'] == $dest) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('tls_policy_map_entry_exists', htmlspecialchars($dest))
);
return false;
}
}
$stmt = $pdo->prepare("INSERT INTO `tls_policy_override` (`dest`, `policy`, `parameters`, `active`) VALUES
(:dest, :policy, :parameters, :active)");
$stmt->execute(array(
':dest' => $dest,
':policy' => $policy,
':parameters' => $parameters,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('tls_policy_map_entry_saved', htmlspecialchars($dest))
);
break;
case 'edit':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = tls_policy_maps('details', $id);
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$dest = (!empty($_data['dest'])) ? $_data['dest'] : $is_now['dest'];
$policy = (!empty($_data['policy'])) ? $_data['policy'] : $is_now['policy'];
$parameters = (isset($_data['parameters'])) ? $_data['parameters'] : $is_now['parameters'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
continue;
}
if (empty($dest) || in_array($dest, array('.', '*', '@'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'tls_policy_map_dest_invalid'
);
return false;
}
if (!empty($parameters)) {
foreach (explode(' ', $parameters) as $parameter) {
if (!preg_match('/(.+)\=(.+)/i', $parameter)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'tls_policy_map_parameter_invalid'
);
return false;
}
}
}
$tls_policy_maps = tls_policy_maps('get');
foreach ($tls_policy_maps as $tls_policy_map) {
if ($tls_policy_map == $id) { continue; }
if (tls_policy_maps('details', $tls_policy_map)['dest'] == $dest) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_exists', htmlspecialchars($dest))
);
return false;
}
}
$stmt = $pdo->prepare("UPDATE `tls_policy_override` SET
`dest` = :dest,
`policy` = :policy,
`parameters` = :parameters,
`active` = :active
WHERE `id`= :id");
$stmt->execute(array(
':dest' => $dest,
':policy' => $policy,
':parameters' => $parameters,
':active' => $active,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('tls_policy_map_entry_saved', htmlspecialchars($dest))
);
}
break;
case 'details':
$mapdata = array();
$id = intval($_data);
$stmt = $pdo->prepare("SELECT `id`,
`dest`,
`policy`,
`parameters`,
`active` AS `active`,
`created`,
`modified` FROM `tls_policy_override`
WHERE `id` = :id");
$stmt->execute(array(':id' => $id));
$mapdata = $stmt->fetch(PDO::FETCH_ASSOC);
return $mapdata;
break;
case 'get':
$mapdata = array();
$all_items = array();
$id = intval($_data);
$stmt = $pdo->query("SELECT `id` FROM `tls_policy_override`");
$all_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($all_items as $i) {
$mapdata[] = $i['id'];
}
$all_items = null;
return $mapdata;
break;
case 'delete':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
if (!is_numeric($id)) {
return false;
}
$stmt = $pdo->prepare("DELETE FROM `tls_policy_override` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('tls_policy_map_entry_deleted', htmlspecialchars($id))
);
}
break;
}
}
<?php
function tls_policy_maps($_action, $_data = null, $attr = null) {
global $pdo;
global $lang;
if ($_SESSION['mailcow_cc_role'] != "admin") {
return false;
}
switch ($_action) {
case 'add':
$dest = idn_to_ascii(trim($_data['dest']), 0, INTL_IDNA_VARIANT_UTS46);
$policy = strtolower(trim($_data['policy']));
$parameters = (isset($_data['parameters']) && !empty($_data['parameters'])) ? $_data['parameters'] : '';
if (empty($dest) || in_array($dest, array('.', '*', '@'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'tls_policy_map_dest_invalid'
);
return false;
}
if (!empty($parameters)) {
foreach (explode(' ', $parameters) as $parameter) {
if (!preg_match('/(.+)\=(.+)/i', $parameter)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'tls_policy_map_parameter_invalid'
);
return false;
}
}
}
$active = intval($_data['active']);
$tls_policy_maps = tls_policy_maps('get');
foreach ($tls_policy_maps as $tls_policy_map) {
if (tls_policy_maps('details', $tls_policy_map)['dest'] == $dest) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('tls_policy_map_entry_exists', htmlspecialchars($dest))
);
return false;
}
}
$stmt = $pdo->prepare("INSERT INTO `tls_policy_override` (`dest`, `policy`, `parameters`, `active`) VALUES
(:dest, :policy, :parameters, :active)");
$stmt->execute(array(
':dest' => $dest,
':policy' => $policy,
':parameters' => $parameters,
':active' => $active
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('tls_policy_map_entry_saved', htmlspecialchars($dest))
);
break;
case 'edit':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
$is_now = tls_policy_maps('details', $id);
if (!empty($is_now)) {
$active = (isset($_data['active'])) ? intval($_data['active']) : $is_now['active'];
$dest = (!empty($_data['dest'])) ? $_data['dest'] : $is_now['dest'];
$policy = (!empty($_data['policy'])) ? $_data['policy'] : $is_now['policy'];
$parameters = (isset($_data['parameters'])) ? $_data['parameters'] : $is_now['parameters'];
}
else {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'access_denied'
);
continue;
}
if (empty($dest) || in_array($dest, array('.', '*', '@'))) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'tls_policy_map_dest_invalid'
);
return false;
}
if (!empty($parameters)) {
foreach (explode(' ', $parameters) as $parameter) {
if (!preg_match('/(.+)\=(.+)/i', $parameter)) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => 'tls_policy_map_parameter_invalid'
);
return false;
}
}
}
$tls_policy_maps = tls_policy_maps('get');
foreach ($tls_policy_maps as $tls_policy_map) {
if ($tls_policy_map == $id) { continue; }
if (tls_policy_maps('details', $tls_policy_map)['dest'] == $dest) {
$_SESSION['return'][] = array(
'type' => 'danger',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('recipient_map_entry_exists', htmlspecialchars($dest))
);
return false;
}
}
$stmt = $pdo->prepare("UPDATE `tls_policy_override` SET
`dest` = :dest,
`policy` = :policy,
`parameters` = :parameters,
`active` = :active
WHERE `id`= :id");
$stmt->execute(array(
':dest' => $dest,
':policy' => $policy,
':parameters' => $parameters,
':active' => $active,
':id' => $id
));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('tls_policy_map_entry_saved', htmlspecialchars($dest))
);
}
break;
case 'details':
$mapdata = array();
$id = intval($_data);
$stmt = $pdo->prepare("SELECT `id`,
`dest`,
`policy`,
`parameters`,
`active` AS `active`,
`created`,
`modified` FROM `tls_policy_override`
WHERE `id` = :id");
$stmt->execute(array(':id' => $id));
$mapdata = $stmt->fetch(PDO::FETCH_ASSOC);
return $mapdata;
break;
case 'get':
$mapdata = array();
$all_items = array();
$id = intval($_data);
$stmt = $pdo->query("SELECT `id` FROM `tls_policy_override`");
$all_items = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($all_items as $i) {
$mapdata[] = $i['id'];
}
$all_items = null;
return $mapdata;
break;
case 'delete':
$ids = (array)$_data['id'];
foreach ($ids as $id) {
if (!is_numeric($id)) {
return false;
}
$stmt = $pdo->prepare("DELETE FROM `tls_policy_override` WHERE `id`= :id");
$stmt->execute(array(':id' => $id));
$_SESSION['return'][] = array(
'type' => 'success',
'log' => array(__FUNCTION__, $_action, $_data, $_attr),
'msg' => array('tls_policy_map_entry_deleted', htmlspecialchars($id))
);
}
break;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,31 @@
-----BEGIN CERTIFICATE-----
MIIFZDCCA0ygAwIBAgIIYsLLTehAXpYwDQYJKoZIhvcNAQELBQAwUDELMAkGA1UE
BhMCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEbMBkG
A1UEAwwSSHVhd2VpIENCRyBSb290IENBMB4XDTE3MDgyMTEwNTYyN1oXDTQyMDgx
NTEwNTYyN1owUDELMAkGA1UEBhMCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UE
CwwKSHVhd2VpIENCRzEbMBkGA1UEAwwSSHVhd2VpIENCRyBSb290IENBMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1OyKm3Ig/6eibB7Uz2o93UqGk2M7
84WdfF8mvffvu218d61G5M3Px54E3kefUTk5Ky1ywHvw7Rp9KDuYv7ktaHkk+yr5
9Ihseu3a7iM/C6SnMSGt+LfB/Bcob9Abw95EigXQ4yQddX9hbNrin3AwZw8wMjEI
SYYDo5GuYDL0NbAiYg2Y5GpfYIqRzoi6GqDz+evLrsl20kJeCEPgJZN4Jg00Iq9k
++EKOZ5Jc/Zx22ZUgKpdwKABkvzshEgG6WWUPB+gosOiLv++inu/9blDpEzQZhjZ
9WVHpURHDK1YlCvubVAMhDpnbqNHZ0AxlPletdoyugrH/OLKl5inhMXNj3Re7Hl8
WsBWLUKp6sXFf0dvSFzqnr2jkhicS+K2IYZnjghC9cOBRO8fnkonh0EBt0evjUIK
r5ClbCKioBX8JU+d4ldtWOpp2FlxeFTLreDJ5ZBU4//bQpTwYMt7gwMK+MO5Wtok
Ux3UF98Z6GdUgbl6nBjBe82c7oIQXhHGHPnURQO7DDPgyVnNOnTPIkmiHJh/e3vk
VhiZNHFCCLTip6GoJVrLxwb9i4q+d0thw4doxVJ5NB9OfDMV64/ybJgpf7m3Ld2y
E0gsf1prrRlDFDXjlYyqqpf1l9Y0u3ctXo7UpXMgbyDEpUQhq3a7txZQO/17luTD
oA6Tz1ADavvBwHkCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFKrE03lH6G4ja+/wqWwicz16GWmhMA0GCSqGSIb3DQEB
CwUAA4ICAQC1d3TMB+VHZdGrWJbfaBShFNiCTN/MceSHOpzBn6JumQP4N7mxCOwd
RSsGKQxV2NPH7LTXWNhUvUw5Sek96FWx/+Oa7jsj3WNAVtmS3zKpCQ5iGb08WIRO
cFnx3oUQ5rcO8r/lUk7Q2cN0E+rF4xsdQrH9k2cd3kAXZXBjfxfKPJTdPy1XnZR/
h8H5EwEK5DWjSzK1wKd3G/Fxdm3E23pcr4FZgdYdOlFSiqW2TJ3Qe6lF4GOKOOyd
WHkpu54ieTsqoYcuMKnKMjT2SLNNgv9Gu5ipaG8Olz6g9C7Htp943lmK/1Vtnhgg
pL3rDTsFX/+ehk7OtxuNzRMD9lXUtEfok7f8XB0dcL4ZjnEhDmp5QZqC1kMubHQt
QnTauEiv0YkSGOwJAUZpK1PIff5GgxXYfaHfBC6Op4q02ppl5Q3URl7XIjYLjvs9
t4S9xPe8tb6416V2fe1dZ62vOXMMKHkZjVihh+IceYpJYHuyfKoYJyahLOQXZykG
K5iPAEEtq3HPfMVF43RKHOwfhrAH5KwelUA/0EkcR4Gzth1MKEqojdnYNemkkSy7
aNPPT4LEm5R7sV6vG1CjwbgvQrWCgc4nMb8ngdfnVF7Ydqjqi9SAqUzIk4+Uf0ZY
+6RY5IcHdCaiPaWIE1xURQ8B0DRUURsQwXdjZhgLN/DKJpCl5aCCxg==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFZDCCA0ygAwIBAgIIYsLLTehAXpYwDQYJKoZIhvcNAQELBQAwUDELMAkGA1UE
BhMCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UECwwKSHVhd2VpIENCRzEbMBkG
A1UEAwwSSHVhd2VpIENCRyBSb290IENBMB4XDTE3MDgyMTEwNTYyN1oXDTQyMDgx
NTEwNTYyN1owUDELMAkGA1UEBhMCQ04xDzANBgNVBAoMBkh1YXdlaTETMBEGA1UE
CwwKSHVhd2VpIENCRzEbMBkGA1UEAwwSSHVhd2VpIENCRyBSb290IENBMIICIjAN
BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1OyKm3Ig/6eibB7Uz2o93UqGk2M7
84WdfF8mvffvu218d61G5M3Px54E3kefUTk5Ky1ywHvw7Rp9KDuYv7ktaHkk+yr5
9Ihseu3a7iM/C6SnMSGt+LfB/Bcob9Abw95EigXQ4yQddX9hbNrin3AwZw8wMjEI
SYYDo5GuYDL0NbAiYg2Y5GpfYIqRzoi6GqDz+evLrsl20kJeCEPgJZN4Jg00Iq9k
++EKOZ5Jc/Zx22ZUgKpdwKABkvzshEgG6WWUPB+gosOiLv++inu/9blDpEzQZhjZ
9WVHpURHDK1YlCvubVAMhDpnbqNHZ0AxlPletdoyugrH/OLKl5inhMXNj3Re7Hl8
WsBWLUKp6sXFf0dvSFzqnr2jkhicS+K2IYZnjghC9cOBRO8fnkonh0EBt0evjUIK
r5ClbCKioBX8JU+d4ldtWOpp2FlxeFTLreDJ5ZBU4//bQpTwYMt7gwMK+MO5Wtok
Ux3UF98Z6GdUgbl6nBjBe82c7oIQXhHGHPnURQO7DDPgyVnNOnTPIkmiHJh/e3vk
VhiZNHFCCLTip6GoJVrLxwb9i4q+d0thw4doxVJ5NB9OfDMV64/ybJgpf7m3Ld2y
E0gsf1prrRlDFDXjlYyqqpf1l9Y0u3ctXo7UpXMgbyDEpUQhq3a7txZQO/17luTD
oA6Tz1ADavvBwHkCAwEAAaNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFKrE03lH6G4ja+/wqWwicz16GWmhMA0GCSqGSIb3DQEB
CwUAA4ICAQC1d3TMB+VHZdGrWJbfaBShFNiCTN/MceSHOpzBn6JumQP4N7mxCOwd
RSsGKQxV2NPH7LTXWNhUvUw5Sek96FWx/+Oa7jsj3WNAVtmS3zKpCQ5iGb08WIRO
cFnx3oUQ5rcO8r/lUk7Q2cN0E+rF4xsdQrH9k2cd3kAXZXBjfxfKPJTdPy1XnZR/
h8H5EwEK5DWjSzK1wKd3G/Fxdm3E23pcr4FZgdYdOlFSiqW2TJ3Qe6lF4GOKOOyd
WHkpu54ieTsqoYcuMKnKMjT2SLNNgv9Gu5ipaG8Olz6g9C7Htp943lmK/1Vtnhgg
pL3rDTsFX/+ehk7OtxuNzRMD9lXUtEfok7f8XB0dcL4ZjnEhDmp5QZqC1kMubHQt
QnTauEiv0YkSGOwJAUZpK1PIff5GgxXYfaHfBC6Op4q02ppl5Q3URl7XIjYLjvs9
t4S9xPe8tb6416V2fe1dZ62vOXMMKHkZjVihh+IceYpJYHuyfKoYJyahLOQXZykG
K5iPAEEtq3HPfMVF43RKHOwfhrAH5KwelUA/0EkcR4Gzth1MKEqojdnYNemkkSy7
aNPPT4LEm5R7sV6vG1CjwbgvQrWCgc4nMb8ngdfnVF7Ydqjqi9SAqUzIk4+Uf0ZY
+6RY5IcHdCaiPaWIE1xURQ8B0DRUURsQwXdjZhgLN/DKJpCl5aCCxg==
-----END CERTIFICATE-----

View File

@ -1,12 +1,12 @@
A document without any HTML open/closing tags.
<hr>
We try and use the representation given by common browsers of the
HTML document, so that it looks similar when converted to plain text.
<a href="http://foo.com">visit foo.com</a> - or <a href="http://www.foo.com">http://www.foo.com</a>
<a href="http://foo.com" title="a link with a title">link</a>
<h2><a name="anchor">An anchor which will not appear</a></h2>
A document without any HTML open/closing tags.
<hr>
We try and use the representation given by common browsers of the
HTML document, so that it looks similar when converted to plain text.
<a href="http://foo.com">visit foo.com</a> - or <a href="http://www.foo.com">http://www.foo.com</a>
<a href="http://foo.com" title="a link with a title">link</a>
<h2><a name="anchor">An anchor which will not appear</a></h2>

View File

@ -1,21 +1,21 @@
<html>
<title>Ignored Title</title>
<body>
<h1>Hello, World!</h1>
<p>This is some e-mail content.
Even though it has whitespace and newlines, the e-mail converter
will handle it correctly.
<p>Even mismatched tags.</p>
<div>A div</div>
<div>Another div</div>
<div>A div<div>within a div</div></div>
<p>Another line<br />Yet another line</p>
<a href="http://foo.com">A link</a>
</body>
<html>
<title>Ignored Title</title>
<body>
<h1>Hello, World!</h1>
<p>This is some e-mail content.
Even though it has whitespace and newlines, the e-mail converter
will handle it correctly.
<p>Even mismatched tags.</p>
<div>A div</div>
<div>Another div</div>
<div>A div<div>within a div</div></div>
<p>Another line<br />Yet another line</p>
<a href="http://foo.com">A link</a>
</body>
</html>

View File

@ -1,53 +1,53 @@
<html>
<title>Ignored Title</title>
<body>
<h1>Hello, World!</h1>
<table>
<thead>
<tr>
<th>Col A</th>
<th>Col B</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Data A1
</td>
<td>
Data B1
</td>
</tr>
<tr>
<td>
Data A2
</td>
<td>
Data B2
</td>
</tr>
<tr>
<td>
Data A3
</td>
<td>
Data B4
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>
Total A
</td>
<td>
Total B
</td>
</tr>
</tfoot>
</table>
</body>
<html>
<title>Ignored Title</title>
<body>
<h1>Hello, World!</h1>
<table>
<thead>
<tr>
<th>Col A</th>
<th>Col B</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Data A1
</td>
<td>
Data B1
</td>
</tr>
<tr>
<td>
Data A2
</td>
<td>
Data B2
</td>
</tr>
<tr>
<td>
Data A3
</td>
<td>
Data B4
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>
Total A
</td>
<td>
Total B
</td>
</tr>
</tfoot>
</table>
</body>
</html>

View File

@ -1,2 +1,2 @@
test one
test one
test two

View File

@ -1,5 +1,5 @@
1
2
3
4
1
2
3
4
5 6

View File

@ -1,10 +1,10 @@
/*!
* Form Cache v@VERSION
* https://github.com/fengyuanchen/formcache
*
* Copyright 2014 Fengyuan Chen
* Released under the MIT license
*
* Date: @DATE
*/
/*!
* 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

@ -1 +1 @@
!function(e){function t(t,n){var a=e('<div class="numberedtextarea-wrapper"></div>').insertAfter(t);e(t).detach().appendTo(a)}function n(t,n){(t=e(t)).parents(".numberedtextarea-wrapper");var i=parseFloat(t.css("padding-left")),o=parseFloat(t.css("padding-top")),s=(parseFloat(t.css("padding-bottom")),e('<div class="numberedtextarea-line-numbers"></div>').insertAfter(t));t.css({paddingLeft:i+s.width()+"px"}).on("input propertychange change keyup paste",function(){a(t,n)}).on("scroll",function(){r(t,n)}),s.css({paddingLeft:i+"px",paddingTop:o+"px",lineHeight:t.css("line-height"),fontFamily:t.css("font-family"),width:s.width()-i+"px"}),t.trigger("change")}function a(t,n){var a=(t=e(t)).parent().find(".numberedtextarea-line-numbers"),r=t.val().split("\n").length,o=parseFloat(t.css("padding-bottom"));for(a.find(".numberedtextarea-number").remove(),i=1;i<=r;i++){var s=e('<div class="numberedtextarea-number numberedtextarea-number-'+i+'">'+i+"</div>").appendTo(a);i===r&&s.css("margin-bottom",o+"px")}}function r(t,n){(t=e(t)).parent().find(".numberedtextarea-line-numbers").scrollTop(t.scrollTop())}function o(e,t){if(e.focus(),"number"==typeof e.selectionStart){var n=e.value,a=e.selectionStart;e.value=n.slice(0,a)+t+n.slice(e.selectionEnd),e.selectionEnd=e.selectionStart=a+t.length}else if(void 0!==document.selection){var i=document.selection.createRange();i.text=t,i.collapse(!1),i.select()}}function s(t){e(t).keydown(function(e){if(9==e.which)return o(this,"\t"),!1}),e(t).keypress(function(e){if(9==e.which)return!1})}e.fn.numberedtextarea=function(a){var i=e.extend({color:null,borderColor:null,class:null,allowTabChar:!1},a);return this.each(function(){if("textarea"!==this.nodeName.toLowerCase())return console.log("This is not a <textarea>, so no way Jose..."),!1;t(this,i),n(this,i),i.allowTabChar&&e(this).allowTabChar()}),this},e.fn.allowTabChar=function(){return this.jquery&&this.each(function(){if(1==this.nodeType){var e=this.nodeName.toLowerCase();("textarea"==e||"input"==e&&"text"==this.type)&&s(this)}}),this}}(jQuery);
!function(e){function t(t,n){var a=e('<div class="numberedtextarea-wrapper"></div>').insertAfter(t);e(t).detach().appendTo(a)}function n(t,n){(t=e(t)).parents(".numberedtextarea-wrapper");var i=parseFloat(t.css("padding-left")),o=parseFloat(t.css("padding-top")),s=(parseFloat(t.css("padding-bottom")),e('<div class="numberedtextarea-line-numbers"></div>').insertAfter(t));t.css({paddingLeft:i+s.width()+"px"}).on("input propertychange change keyup paste",function(){a(t,n)}).on("scroll",function(){r(t,n)}),s.css({paddingLeft:i+"px",paddingTop:o+"px",lineHeight:t.css("line-height"),fontFamily:t.css("font-family"),width:s.width()-i+"px"}),t.trigger("change")}function a(t,n){var a=(t=e(t)).parent().find(".numberedtextarea-line-numbers"),r=t.val().split("\n").length,o=parseFloat(t.css("padding-bottom"));for(a.find(".numberedtextarea-number").remove(),i=1;i<=r;i++){var s=e('<div class="numberedtextarea-number numberedtextarea-number-'+i+'">'+i+"</div>").appendTo(a);i===r&&s.css("margin-bottom",o+"px")}}function r(t,n){(t=e(t)).parent().find(".numberedtextarea-line-numbers").scrollTop(t.scrollTop())}function o(e,t){if(e.focus(),"number"==typeof e.selectionStart){var n=e.value,a=e.selectionStart;e.value=n.slice(0,a)+t+n.slice(e.selectionEnd),e.selectionEnd=e.selectionStart=a+t.length}else if(void 0!==document.selection){var i=document.selection.createRange();i.text=t,i.collapse(!1),i.select()}}function s(t){e(t).keydown(function(e){if(9==e.which)return o(this,"\t"),!1}),e(t).keypress(function(e){if(9==e.which)return!1})}e.fn.numberedtextarea=function(a){var i=e.extend({color:null,borderColor:null,class:null,allowTabChar:!1},a);return this.each(function(){if("textarea"!==this.nodeName.toLowerCase())return console.log("This is not a <textarea>, so no way Jose..."),!1;t(this,i),n(this,i),i.allowTabChar&&e(this).allowTabChar()}),this},e.fn.allowTabChar=function(){return this.jquery&&this.each(function(){if(1==this.nodeType){var e=this.nodeName.toLowerCase();("textarea"==e||"input"==e&&"text"==this.type)&&s(this)}}),this}}(jQuery);

View File

@ -1,397 +1,397 @@
$(document).ready(function() {
mass_action = false;
function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
function validateRegex(e){var t=e.split("/"),n=e,r="";t.length>1&&(n=t[1],r=t[2]);try{return new RegExp(n,r),!0}catch(e){return!1}}
function is_active(elem) {
if ($(elem).data('submitted') == '1') {
return true;
} else {
var parent_btn_grp = $(elem).parentsUntil(".btn-group").parent();
if (parent_btn_grp.hasClass('btn-group')) {
parent_btn_grp.replaceWith('<button class="btn btn-secondary btn-sm" disabled>' + lang_footer.loading + '</a>');
}
$(elem).text(lang_footer.loading);
$(elem).attr('data-submitted', '1');
function disableF5(e) { if ((e.which || e.keyCode) == 116 || (e.which || e.keyCode) == 82) e.preventDefault(); };
$(document).on("keydown", disableF5);
return false;
}
}
$.fn.serializeObject = function() {
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
// Collect values of input fields with name "multi_select" and same data-id to js array multi_data[data-id]
var multi_data = [];
$(document).on('change', 'input[name=multi_select]:checkbox', function(e) {
if(mass_action === true) {
multi_data = [];
mass_action = false;
}
if ($(this).is(':checked') && $(this).data('id')) {
var id = $(this).data('id');
if (typeof multi_data[id] == "undefined") {
multi_data[id] = [];
}
multi_data[id].push($(this).val());
}
else {
var id = $(this).data('id');
if (typeof multi_data[id] !== "undefined") {
multi_data[id].splice($.inArray($(this).val(), multi_data[id]),1);
}
}
});
// Select checkbox by click on parent tr
$(document).on('click', 'tbody>tr', function(e) {
if(e.target.tagName.toLowerCase() === 'button') {
e.stopPropagation();
}
else if(e.target.tagName.toLowerCase() === 'a') {
e.stopPropagation();
}
else if (e.target.type == "checkbox") {
e.stopPropagation();
}
else {
var checkbox = $(this).find(':checkbox');
checkbox.trigger('click');
}
});
// Select or deselect all checkboxes with same data-id
$(document).on('click', '#toggle_multi_select_all', function(e) {
mass_action = true
e.preventDefault();
id = $(this).data("id");
var all_checkboxes = $("input[data-id=" + id + "]:enabled");
all_checkboxes.prop("checked", !all_checkboxes.prop("checked")).change();
});
// General API edit actions
$(document).on('click', "[data-action='edit_selected']", function(e) {
e.preventDefault();
var id = $(this).data('id');
var api_url = $(this).data('api-url');
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 (typeof $(this).data('api-reload-location') !== 'undefined') {
api_reload_location = $(this).data('api-reload-location');
} else {
api_reload_location = '#';
}
// If clicked element #edit_selected is in a form with the same data-id as the button,
// we merge all input fields by {"name":"value"} into api-attr
if ($(this).closest("form").data('id') == id) {
var invalid = false;
$(this).closest("form").find('select, textarea, input').each(function() {
if ($(this).prop('required')) {
if (!$(this).val() && $(this).prop('disabled') === false) {
invalid = true;
if ($(this).is("select")) {
$(this).selectpicker('setStyle', 'btn-input-missing', 'add');
}
$(this).addClass('inputMissingAttr');
} else {
if ($(this).is("select")) {
$(this).selectpicker('setStyle', 'btn-input-missing', 'remove');
}
$(this).removeClass('inputMissingAttr');
}
}
if ($(this).val() && $(this).attr("type") == 'email') {
if (!validateEmail($(this).val())) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
$(this).removeClass('inputMissingAttr');
}
}
if ($(this).attr("max")) {
if (Number($(this).val()) > Number($(this).attr("max"))) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
if ($(this).attr("min")) {
if (Number($(this).val()) < Number($(this).attr("min"))) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
$(this).removeClass('inputMissingAttr');
}
}
}
}
if ($(this).val() && $(this).attr("regex")) {
var regex_content = $(this).val();
$(this).removeClass('inputMissingAttr');
if(!validateRegex(regex_content)) {
invalid = true;
$(this).addClass('inputMissingAttr');
}
if(!regex_content.startsWith('/') || !/\/[ims]?$/.test(regex_content)){
invalid = true;
$(this).addClass('inputMissingAttr');
}
}
});
if (!invalid) {
var attr_to_merge = $(this).closest("form").serializeObject();
// parse possible JSON Strings
for (var [key, value] of Object.entries(attr_to_merge)) {
try {
attr_to_merge[key] = JSON.parse(attr_to_merge[key]);
} catch {}
}
var api_attr = $.extend(api_attr, attr_to_merge)
} else {
return false;
}
}
// alert(JSON.stringify(api_attr));
// If clicked element #edit_selected has data-item attribute, it is added to "items"
if (typeof $(this).data('item') !== 'undefined') {
var id = $(this).data('id');
if (typeof multi_data[id] == "undefined") {
multi_data[id] = [];
}
multi_data[id].splice($.inArray($(this).data('item'), multi_data[id]), 1);
multi_data[id].push($(this).data('item'));
}
if (typeof multi_data[id] == "undefined") return;
api_items = multi_data[id];
for (var i in api_items) {
api_items[i] = decodeURIComponent(api_items[i]);
}
// alert(JSON.stringify(api_attr));
if (Object.keys(api_items).length !== 0) {
if (is_active($(this))) { return false; }
$.ajax({
type: "POST",
dataType: "json",
data: {
"items": JSON.stringify(api_items),
"attr": JSON.stringify(api_attr),
"csrf_token": csrf_token
},
url: '/api/v1/' + api_url,
jsonp: false,
complete: function(data) {
var response = (data.responseText);
if (typeof response !== 'undefined' && response.length !== 0) {
response_obj = JSON.parse(response);
}
if (api_reload_window === true) {
if (api_reload_location != '#') {
window.location.replace(api_reload_location)
} else {
window.location = window.location.href.split("#")[0];
}
}
}
});
}
});
// General API add actions
$(document).on('click', "[data-action='add_item']", function(e) {
e.preventDefault();
var id = $(this).data('id');
var api_url = $(this).data('api-url');
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,
// we merge all input fields by {"name":"value"} into api-attr
if ($(this).closest("form").data('id') == id) {
var invalid = false;
$(this).closest("form").find('select, textarea, input').each(function() {
if ($(this).prop('required')) {
if (!$(this).val() && $(this).prop('disabled') === false) {
invalid = true;
if ($(this).is("select")) {
$(this).selectpicker('setStyle', 'btn-input-missing', 'add');
}
$(this).addClass('inputMissingAttr');
} else {
if ($(this).is("select")) {
$(this).selectpicker('setStyle', 'btn-input-missing', 'remove');
}
$(this).removeClass('inputMissingAttr');
}
}
if ($(this).attr("type") == 'email') {
var emailReg = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
if (!emailReg.test($(this).val())) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
$(this).removeClass('inputMissingAttr');
}
}
if ($(this).attr("max")) {
if (Number($(this).val()) > Number($(this).attr("max"))) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
if ($(this).attr("min")) {
if (Number($(this).val()) < Number($(this).attr("min"))) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
$(this).removeClass('inputMissingAttr');
}
}
}
}
});
if (!invalid) {
var attr_to_merge = $(this).closest("form").serializeObject();
// parse possible JSON Strings
for (var [key, value] of Object.entries(attr_to_merge)) {
try {
attr_to_merge[key] = JSON.parse(attr_to_merge[key]);
} catch {}
}
var api_attr = $.extend(api_attr, attr_to_merge)
} else {
return false;
}
}
if (is_active($(this))) { return false; }
// alert(JSON.stringify(api_attr));
$.ajax({
type: "POST",
dataType: "json",
data: {
"attr": JSON.stringify(api_attr),
"csrf_token": csrf_token
},
url: '/api/v1/' + api_url,
jsonp: false,
complete: function(data) {
var response = (data.responseText);
if (typeof response !== 'undefined' && response.length !== 0) {
response_obj = JSON.parse(response);
unset = true;
$.each(response_obj, function(i, v) {
if (v.type == "danger") {
unset = false;
}
});
if (unset === true) {
unset = null;
// Keep form data for sync jobs
if (id != "add_syncjob") {
$('form').formcache('clear');
$('form').formcache('destroy');
var i = localStorage.length;
while(i--) {
var key = localStorage.key(i);
if(/formcache/.test(key)) {
localStorage.removeItem(key);
}
}
}
}
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];
}
}
});
});
// General API delete actions
$(document).on('click', "[data-action='delete_selected']", function(e) {
e.preventDefault();
var id = $(this).data('id');
// If clicked element #delete_selected has data-item attribute, it is added to "items"
if (typeof $(this).data('item') !== 'undefined') {
var id = $(this).data('id');
if (typeof multi_data[id] == "undefined") {
multi_data[id] = [];
}
multi_data[id].splice($.inArray($(this).data('item'), multi_data[id]), 1);
multi_data[id].push($(this).data('item'));
}
if (typeof $(this).data('text') !== 'undefined') {
$("#DeleteText").empty();
$("#DeleteText").text($(this).data('text'));
}
if (typeof multi_data[id] == "undefined" || multi_data[id] == "") return;
data_array = multi_data[id];
api_url = $(this).data('api-url');
$(document).on('show.bs.modal', '#ConfirmDeleteModal', function() {
$("#ItemsToDelete").empty();
for (var i in data_array) {
data_array[i] = decodeURIComponent(data_array[i]);
$("#ItemsToDelete").append("<li>" + escapeHtml(data_array[i]) + "</li>");
}
})
$('#ConfirmDeleteModal').modal('show')
.one('click', '#IsConfirmed', function(e) {
if (is_active($('#IsConfirmed'))) { return false; }
$.ajax({
type: "POST",
dataType: "json",
cache: false,
data: {
"items": JSON.stringify(data_array),
"csrf_token": csrf_token
},
url: '/api/v1/' + api_url,
jsonp: false,
complete: function(data) {
window.location = window.location.href.split("#")[0];
}
});
})
.one('click', '#isCanceled', function(e) {
// Remove event handler to allow to close modal and restart dialog without multiple submits
$('#ConfirmDeleteModal').off();
$('#ConfirmDeleteModal').modal('hide');
});
});
// toggle jquery datatables child rows
$('button[data-datatables-expand], a[data-datatables-expand]').on('click', function (e) {
e.preventDefault();
var tableId = e.target.getAttribute("data-datatables-expand");
var table = $("#" + tableId).DataTable();
table.rows(':not(.parent)').nodes().to$().find('td:first-child').trigger('click');
});
$('button[data-datatables-collapse], a[data-datatables-collapse]').on('click', function (e) {
e.preventDefault();
var tableId = e.target.getAttribute("data-datatables-collapse");
var table = $("#" + tableId).DataTable();
table.rows('.parent').nodes().to$().find('td:first-child').trigger('click');
});
});
$(document).ready(function() {
mass_action = false;
function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
function validateRegex(e){var t=e.split("/"),n=e,r="";t.length>1&&(n=t[1],r=t[2]);try{return new RegExp(n,r),!0}catch(e){return!1}}
function is_active(elem) {
if ($(elem).data('submitted') == '1') {
return true;
} else {
var parent_btn_grp = $(elem).parentsUntil(".btn-group").parent();
if (parent_btn_grp.hasClass('btn-group')) {
parent_btn_grp.replaceWith('<button class="btn btn-secondary btn-sm" disabled>' + lang_footer.loading + '</a>');
}
$(elem).text(lang_footer.loading);
$(elem).attr('data-submitted', '1');
function disableF5(e) { if ((e.which || e.keyCode) == 116 || (e.which || e.keyCode) == 82) e.preventDefault(); };
$(document).on("keydown", disableF5);
return false;
}
}
$.fn.serializeObject = function() {
var o = {};
var a = this.serializeArray();
$.each(a, function() {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
o[this.name].push(this.value || '');
} else {
o[this.name] = this.value || '';
}
});
return o;
};
// Collect values of input fields with name "multi_select" and same data-id to js array multi_data[data-id]
var multi_data = [];
$(document).on('change', 'input[name=multi_select]:checkbox', function(e) {
if(mass_action === true) {
multi_data = [];
mass_action = false;
}
if ($(this).is(':checked') && $(this).data('id')) {
var id = $(this).data('id');
if (typeof multi_data[id] == "undefined") {
multi_data[id] = [];
}
multi_data[id].push($(this).val());
}
else {
var id = $(this).data('id');
if (typeof multi_data[id] !== "undefined") {
multi_data[id].splice($.inArray($(this).val(), multi_data[id]),1);
}
}
});
// Select checkbox by click on parent tr
$(document).on('click', 'tbody>tr', function(e) {
if(e.target.tagName.toLowerCase() === 'button') {
e.stopPropagation();
}
else if(e.target.tagName.toLowerCase() === 'a') {
e.stopPropagation();
}
else if (e.target.type == "checkbox") {
e.stopPropagation();
}
else {
var checkbox = $(this).find(':checkbox');
checkbox.trigger('click');
}
});
// Select or deselect all checkboxes with same data-id
$(document).on('click', '#toggle_multi_select_all', function(e) {
mass_action = true
e.preventDefault();
id = $(this).data("id");
var all_checkboxes = $("input[data-id=" + id + "]:enabled");
all_checkboxes.prop("checked", !all_checkboxes.prop("checked")).change();
});
// General API edit actions
$(document).on('click', "[data-action='edit_selected']", function(e) {
e.preventDefault();
var id = $(this).data('id');
var api_url = $(this).data('api-url');
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 (typeof $(this).data('api-reload-location') !== 'undefined') {
api_reload_location = $(this).data('api-reload-location');
} else {
api_reload_location = '#';
}
// If clicked element #edit_selected is in a form with the same data-id as the button,
// we merge all input fields by {"name":"value"} into api-attr
if ($(this).closest("form").data('id') == id) {
var invalid = false;
$(this).closest("form").find('select, textarea, input').each(function() {
if ($(this).prop('required')) {
if (!$(this).val() && $(this).prop('disabled') === false) {
invalid = true;
if ($(this).is("select")) {
$(this).selectpicker('setStyle', 'btn-input-missing', 'add');
}
$(this).addClass('inputMissingAttr');
} else {
if ($(this).is("select")) {
$(this).selectpicker('setStyle', 'btn-input-missing', 'remove');
}
$(this).removeClass('inputMissingAttr');
}
}
if ($(this).val() && $(this).attr("type") == 'email') {
if (!validateEmail($(this).val())) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
$(this).removeClass('inputMissingAttr');
}
}
if ($(this).attr("max")) {
if (Number($(this).val()) > Number($(this).attr("max"))) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
if ($(this).attr("min")) {
if (Number($(this).val()) < Number($(this).attr("min"))) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
$(this).removeClass('inputMissingAttr');
}
}
}
}
if ($(this).val() && $(this).attr("regex")) {
var regex_content = $(this).val();
$(this).removeClass('inputMissingAttr');
if(!validateRegex(regex_content)) {
invalid = true;
$(this).addClass('inputMissingAttr');
}
if(!regex_content.startsWith('/') || !/\/[ims]?$/.test(regex_content)){
invalid = true;
$(this).addClass('inputMissingAttr');
}
}
});
if (!invalid) {
var attr_to_merge = $(this).closest("form").serializeObject();
// parse possible JSON Strings
for (var [key, value] of Object.entries(attr_to_merge)) {
try {
attr_to_merge[key] = JSON.parse(attr_to_merge[key]);
} catch {}
}
var api_attr = $.extend(api_attr, attr_to_merge)
} else {
return false;
}
}
// alert(JSON.stringify(api_attr));
// If clicked element #edit_selected has data-item attribute, it is added to "items"
if (typeof $(this).data('item') !== 'undefined') {
var id = $(this).data('id');
if (typeof multi_data[id] == "undefined") {
multi_data[id] = [];
}
multi_data[id].splice($.inArray($(this).data('item'), multi_data[id]), 1);
multi_data[id].push($(this).data('item'));
}
if (typeof multi_data[id] == "undefined") return;
api_items = multi_data[id];
for (var i in api_items) {
api_items[i] = decodeURIComponent(api_items[i]);
}
// alert(JSON.stringify(api_attr));
if (Object.keys(api_items).length !== 0) {
if (is_active($(this))) { return false; }
$.ajax({
type: "POST",
dataType: "json",
data: {
"items": JSON.stringify(api_items),
"attr": JSON.stringify(api_attr),
"csrf_token": csrf_token
},
url: '/api/v1/' + api_url,
jsonp: false,
complete: function(data) {
var response = (data.responseText);
if (typeof response !== 'undefined' && response.length !== 0) {
response_obj = JSON.parse(response);
}
if (api_reload_window === true) {
if (api_reload_location != '#') {
window.location.replace(api_reload_location)
} else {
window.location = window.location.href.split("#")[0];
}
}
}
});
}
});
// General API add actions
$(document).on('click', "[data-action='add_item']", function(e) {
e.preventDefault();
var id = $(this).data('id');
var api_url = $(this).data('api-url');
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,
// we merge all input fields by {"name":"value"} into api-attr
if ($(this).closest("form").data('id') == id) {
var invalid = false;
$(this).closest("form").find('select, textarea, input').each(function() {
if ($(this).prop('required')) {
if (!$(this).val() && $(this).prop('disabled') === false) {
invalid = true;
if ($(this).is("select")) {
$(this).selectpicker('setStyle', 'btn-input-missing', 'add');
}
$(this).addClass('inputMissingAttr');
} else {
if ($(this).is("select")) {
$(this).selectpicker('setStyle', 'btn-input-missing', 'remove');
}
$(this).removeClass('inputMissingAttr');
}
}
if ($(this).attr("type") == 'email') {
var emailReg = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
if (!emailReg.test($(this).val())) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
$(this).removeClass('inputMissingAttr');
}
}
if ($(this).attr("max")) {
if (Number($(this).val()) > Number($(this).attr("max"))) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
if ($(this).attr("min")) {
if (Number($(this).val()) < Number($(this).attr("min"))) {
invalid = true;
$(this).addClass('inputMissingAttr');
} else {
$(this).removeClass('inputMissingAttr');
}
}
}
}
});
if (!invalid) {
var attr_to_merge = $(this).closest("form").serializeObject();
// parse possible JSON Strings
for (var [key, value] of Object.entries(attr_to_merge)) {
try {
attr_to_merge[key] = JSON.parse(attr_to_merge[key]);
} catch {}
}
var api_attr = $.extend(api_attr, attr_to_merge)
} else {
return false;
}
}
if (is_active($(this))) { return false; }
// alert(JSON.stringify(api_attr));
$.ajax({
type: "POST",
dataType: "json",
data: {
"attr": JSON.stringify(api_attr),
"csrf_token": csrf_token
},
url: '/api/v1/' + api_url,
jsonp: false,
complete: function(data) {
var response = (data.responseText);
if (typeof response !== 'undefined' && response.length !== 0) {
response_obj = JSON.parse(response);
unset = true;
$.each(response_obj, function(i, v) {
if (v.type == "danger") {
unset = false;
}
});
if (unset === true) {
unset = null;
// Keep form data for sync jobs
if (id != "add_syncjob") {
$('form').formcache('clear');
$('form').formcache('destroy');
var i = localStorage.length;
while(i--) {
var key = localStorage.key(i);
if(/formcache/.test(key)) {
localStorage.removeItem(key);
}
}
}
}
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];
}
}
});
});
// General API delete actions
$(document).on('click', "[data-action='delete_selected']", function(e) {
e.preventDefault();
var id = $(this).data('id');
// If clicked element #delete_selected has data-item attribute, it is added to "items"
if (typeof $(this).data('item') !== 'undefined') {
var id = $(this).data('id');
if (typeof multi_data[id] == "undefined") {
multi_data[id] = [];
}
multi_data[id].splice($.inArray($(this).data('item'), multi_data[id]), 1);
multi_data[id].push($(this).data('item'));
}
if (typeof $(this).data('text') !== 'undefined') {
$("#DeleteText").empty();
$("#DeleteText").text($(this).data('text'));
}
if (typeof multi_data[id] == "undefined" || multi_data[id] == "") return;
data_array = multi_data[id];
api_url = $(this).data('api-url');
$(document).on('show.bs.modal', '#ConfirmDeleteModal', function() {
$("#ItemsToDelete").empty();
for (var i in data_array) {
data_array[i] = decodeURIComponent(data_array[i]);
$("#ItemsToDelete").append("<li>" + escapeHtml(data_array[i]) + "</li>");
}
})
$('#ConfirmDeleteModal').modal('show')
.one('click', '#IsConfirmed', function(e) {
if (is_active($('#IsConfirmed'))) { return false; }
$.ajax({
type: "POST",
dataType: "json",
cache: false,
data: {
"items": JSON.stringify(data_array),
"csrf_token": csrf_token
},
url: '/api/v1/' + api_url,
jsonp: false,
complete: function(data) {
window.location = window.location.href.split("#")[0];
}
});
})
.one('click', '#isCanceled', function(e) {
// Remove event handler to allow to close modal and restart dialog without multiple submits
$('#ConfirmDeleteModal').off();
$('#ConfirmDeleteModal').modal('hide');
});
});
// toggle jquery datatables child rows
$('button[data-datatables-expand], a[data-datatables-expand]').on('click', function (e) {
e.preventDefault();
var tableId = e.target.getAttribute("data-datatables-expand");
var table = $("#" + tableId).DataTable();
table.rows(':not(.parent)').nodes().to$().find('td:first-child').trigger('click');
});
$('button[data-datatables-collapse], a[data-datatables-collapse]').on('click', function (e) {
e.preventDefault();
var tableId = e.target.getAttribute("data-datatables-collapse");
var table = $("#" + tableId).DataTable();
table.rows('.parent').nodes().to$().find('td:first-child').trigger('click');
});
});

File diff suppressed because it is too large Load Diff

View File

@ -1,32 +1,32 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
if (!$oauth2_server->verifyResourceRequest(OAuth2\Request::createFromGlobals())) {
$oauth2_server->getResponse()->send();
die;
}
$token = $oauth2_server->getAccessTokenData(OAuth2\Request::createFromGlobals());
$stmt = $pdo->prepare("SELECT * FROM `mailbox` WHERE `username` = :username AND `active` = '1'");
$stmt->execute(array(':username' => $token['user_id']));
$mailbox = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($mailbox)) {
if ($token['scope'] == 'profile') {
header('Content-Type: application/json');
echo json_encode(array(
'success' => true,
'username' => $token['user_id'],
'id' => $token['user_id'],
'identifier' => $token['user_id'],
'email' => (!empty($mailbox['username']) ? $mailbox['username'] : ''),
'full_name' => (!empty($mailbox['name']) ? $mailbox['name'] : 'mailcow administrative user'),
'displayName' => (!empty($mailbox['name']) ? $mailbox['name'] : 'mailcow administrative user'),
'created' => (!empty($mailbox['created']) ? $mailbox['created'] : ''),
'modified' => (!empty($mailbox['modified']) ? $mailbox['modified'] : ''),
'active' => (!empty($mailbox['active']) ? $mailbox['active'] : ''),
));
exit;
}
}
echo json_encode(array(
'success' => false
));
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
if (!$oauth2_server->verifyResourceRequest(OAuth2\Request::createFromGlobals())) {
$oauth2_server->getResponse()->send();
die;
}
$token = $oauth2_server->getAccessTokenData(OAuth2\Request::createFromGlobals());
$stmt = $pdo->prepare("SELECT * FROM `mailbox` WHERE `username` = :username AND `active` = '1'");
$stmt->execute(array(':username' => $token['user_id']));
$mailbox = $stmt->fetch(PDO::FETCH_ASSOC);
if (!empty($mailbox)) {
if ($token['scope'] == 'profile') {
header('Content-Type: application/json');
echo json_encode(array(
'success' => true,
'username' => $token['user_id'],
'id' => $token['user_id'],
'identifier' => $token['user_id'],
'email' => (!empty($mailbox['username']) ? $mailbox['username'] : ''),
'full_name' => (!empty($mailbox['name']) ? $mailbox['name'] : 'mailcow administrative user'),
'displayName' => (!empty($mailbox['name']) ? $mailbox['name'] : 'mailcow administrative user'),
'created' => (!empty($mailbox['created']) ? $mailbox['created'] : ''),
'modified' => (!empty($mailbox['modified']) ? $mailbox['modified'] : ''),
'active' => (!empty($mailbox['active']) ? $mailbox['active'] : ''),
));
exit;
}
}
echo json_encode(array(
'success' => false
));

View File

@ -1,4 +1,4 @@
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
$request = OAuth2\Request::createFromGlobals();
$oauth2_server->handleTokenRequest($request)->send();
<?php
require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php';
$request = OAuth2\Request::createFromGlobals();
$oauth2_server->handleTokenRequest($request)->send();