diff --git a/data/web/inc/ajax/qitem_details.php b/data/web/inc/ajax/qitem_details.php index 0a256703..8607388d 100644 --- a/data/web/inc/ajax/qitem_details.php +++ b/data/web/inc/ajax/qitem_details.php @@ -2,9 +2,6 @@ session_start(); header("Content-Type: application/json"); require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/prerequisites.inc.php'; -if (!isset($_SESSION['mailcow_cc_role'])) { - exit(); -} function rrmdir($src) { $dir = opendir($src); @@ -22,6 +19,7 @@ function rrmdir($src) { closedir($dir); rmdir($src); } + function addAddresses(&$list, $mail, $headerName) { $addresses = $mail->getAddresses($headerName); foreach ($addresses as $address) { @@ -29,7 +27,49 @@ function addAddresses(&$list, $mail, $headerName) { } } -if (!empty($_GET['id']) && ctype_alnum($_GET['id'])) { +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'); + $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 symbols + $data['symbols'] = json_decode($mailc['symbols']); + $data['subject'] = $mail_parser->getHeader('subject'); + (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 (strlen($mailc['msg']) > 10485760) { @@ -37,6 +77,16 @@ if (!empty($_GET['id']) && ctype_alnum($_GET['id'])) { 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 @@ -53,7 +103,9 @@ if (!empty($_GET['id']) && ctype_alnum($_GET['id'])) { addAddresses($recipientsList, $mail_parser, 'bcc'); $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 symbols diff --git a/data/web/inc/functions.quarantine.inc.php b/data/web/inc/functions.quarantine.inc.php index 69fd4e01..bf43851c 100644 --- a/data/web/inc/functions.quarantine.inc.php +++ b/data/web/inc/functions.quarantine.inc.php @@ -16,7 +16,7 @@ function quarantine($_action, $_data = null) { 'msg' => 'access_denied' ) ))); - return; + return false; } $stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt` WHERE SHA2(CONCAT(`id`, `qid`), 256) = :hash @@ -32,7 +32,7 @@ function quarantine($_action, $_data = null) { 'msg' => 'access_denied' ) ))); - return; + return false; } else { $stmt = $pdo->prepare("DELETE FROM `quarantine` WHERE id = :id"); @@ -59,7 +59,7 @@ function quarantine($_action, $_data = null) { 'msg' => 'access_denied' ) ))); - return; + return false; } $stmt = $pdo->prepare('SELECT `id` FROM `quarantine` LEFT OUTER JOIN `user_acl` ON `user_acl`.`username` = `rcpt` WHERE SHA2(CONCAT(`id`, `qid`), 256) = :hash @@ -75,7 +75,7 @@ function quarantine($_action, $_data = null) { 'msg' => 'access_denied' ) ))); - return; + return false; } else { $stmt = $pdo->prepare('SELECT `msg`, `qid`, `sender`, `rcpt` FROM `quarantine` WHERE `id` = :id'); @@ -96,7 +96,7 @@ function quarantine($_action, $_data = null) { 'msg' => array('release_send_failed', 'Cannot determine Postfix host') ) ))); - return; + return false; } try { $release_format = $redis->Get('Q_RELEASE_FORMAT'); @@ -109,7 +109,7 @@ function quarantine($_action, $_data = null) { 'msg' => array('redis_error', $e) ) ))); - return; + return false; } if ($release_format == 'attachment') { try { @@ -137,7 +137,7 @@ function quarantine($_action, $_data = null) { 'msg' => array('release_send_failed', 'Cannot determine Postfix host') ) ))); - return; + return false; } $mail->Host = $postfix; $mail->Port = 590; @@ -162,7 +162,7 @@ function quarantine($_action, $_data = null) { 'msg' => array('release_send_failed', $e->errorMessage()) ) ))); - return; + return false; } } elseif ($release_format == 'raw') { @@ -199,7 +199,7 @@ function quarantine($_action, $_data = null) { 'msg' => 'Postfix returned SMTP code ' . $smtp_resource . ', expected ' . $postfix_talk[$i][0] ) ))); - return; + return false; } if ($postfix_talk[$i][1] !== '') { fputs($smtp_connection, $postfix_talk[$i][1]); @@ -809,13 +809,36 @@ function quarantine($_action, $_data = null) { if (!is_numeric($_data) || empty($_data)) { return false; } - $stmt = $pdo->prepare('SELECT `rcpt`, `score`, `symbols`, `msg`, `domain` FROM `quarantine` WHERE `id`= :id'); + $stmt = $pdo->prepare('SELECT * FROM `quarantine` WHERE `id`= :id'); $stmt->execute(array(':id' => $_data)); $row = $stmt->fetch(PDO::FETCH_ASSOC); if (hasMailboxObjectAccess($_SESSION['mailcow_cc_username'], $_SESSION['mailcow_cc_role'], $row['rcpt'])) { return $row; } + logger(array('return' => array( + array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'access_denied' + ) + ))); return false; break; + case 'hash_details': + $hash = trim($_data); + if (preg_match("/^([a-f0-9]{64})$/", $hash) === false) { + logger(array('return' => array( + array( + 'type' => 'danger', + 'log' => array(__FUNCTION__, $_action, $_data_log), + 'msg' => 'access_denied' + ) + ))); + return false; + } + $stmt = $pdo->prepare('SELECT * FROM `quarantine` WHERE SHA2(CONCAT(`id`, `qid`), 256) = :hash'); + $stmt->execute(array(':hash' => $hash)); + return $stmt->fetch(PDO::FETCH_ASSOC); + break; } } diff --git a/data/web/inc/header.inc.php b/data/web/inc/header.inc.php index 79e76de7..d2add25e 100644 --- a/data/web/inc/header.inc.php +++ b/data/web/inc/header.inc.php @@ -20,7 +20,7 @@ if (preg_match("/edit/i", $_SERVER['REQUEST_URI'])) { $css_minifier->add('/web/css/site/edit.css'); } - if (preg_match("/quarantine/i", $_SERVER['REQUEST_URI'])) { + if (preg_match("/(quarantine|qhandler)/i", $_SERVER['REQUEST_URI'])) { $css_minifier->add('/web/css/site/quarantine.css'); } if (preg_match("/debug/i", $_SERVER['REQUEST_URI'])) { diff --git a/data/web/js/site/qhandler.js b/data/web/js/site/qhandler.js new file mode 100644 index 00000000..5253e6db --- /dev/null +++ b/data/web/js/site/qhandler.js @@ -0,0 +1,52 @@ +jQuery(function($){ + var qitem = $('legend').data('hash'); + var qError = $("#qid_error"); + $.ajax({ + url: '/inc/ajax/qitem_details.php', + data: { hash: qitem }, + dataType: 'json', + success: function(data){ + if (typeof data.error !== 'undefined') { + qError.text(data.error); + qError.show(); + } + $('[data-id="qitems_single"]').each(function(index) { + $(this).attr("data-item", qitem); + }); + $('#qid_detail_subj').text(data.subject); + $('#qid_detail_hfrom').text(data.header_from); + $('#qid_detail_efrom').text(data.env_from); + $('#qid_detail_score').text(data.score); + $('#qid_detail_symbols').html(''); + if (typeof data.symbols !== 'undefined') { + data.symbols.sort(function (a, b) { + if (a.score === 0) return 1 + if (b.score === 0) return -1 + if (b.score < 0 && a.score < 0) { + return a.score - b.score + } + if (b.score > 0 && a.score > 0) { + return b.score - a.score + } + return b.score - a.score + }) + $.each(data.symbols, function (index, value) { + var highlightClass = '' + if (value.score > 0) highlightClass = 'negative' + else if (value.score < 0) highlightClass = 'positive' + else highlightClass = 'neutral' + $('#qid_detail_symbols').append('' + value.name + ' (' + value.score + ')'); + }); + $('[data-toggle="tooltip"]').tooltip() + } + $('#qid_detail_recipients').html(''); + if (typeof data.recipients !== 'undefined') { + $.each(data.recipients, function(index, value) { + var elem = $(''); + elem.text(value.address + (value.type != 'to' ? (' (' + value.type.toUpperCase() + ')') : '')); + $('#qid_detail_recipients').append(elem); + }); + } + } + }); +}); \ No newline at end of file diff --git a/data/web/js/site/quarantine.js b/data/web/js/site/quarantine.js index d25c0de6..af871dec 100644 --- a/data/web/js/site/quarantine.js +++ b/data/web/js/site/quarantine.js @@ -144,7 +144,8 @@ jQuery(function($){ $('#qid_detail_subj').text(data.subject); $('#qid_detail_text').text(data.text_plain); $('#qid_detail_text_from_html').text(data.text_html); - + $('#qid_detail_hfrom').text(data.header_from); + $('#qid_detail_efrom').text(data.env_from); $('#qid_detail_score').text(data.score); $('#qid_detail_symbols').html(''); if (typeof data.symbols !== 'undefined') { diff --git a/data/web/lang/lang.ca.json b/data/web/lang/lang.ca.json index 1008ab58..6c85b9b0 100644 --- a/data/web/lang/lang.ca.json +++ b/data/web/lang/lang.ca.json @@ -387,7 +387,7 @@ "release_body": "Hem adjuntat el teu missatge com a eml en aquest missatge.", "release_subject": "Element potencialment perillós %s en quarantena", "remove": "Esborrar", - "sender": "Emissor", + "sender": "Emissor (SMTP)", "show_item": "Mostrar", "subj": "Assumpte", "text_from_html_content": "Contingut (a partir del HTML)", diff --git a/data/web/lang/lang.cs.json b/data/web/lang/lang.cs.json index f6a5877d..642a9ccb 100644 --- a/data/web/lang/lang.cs.json +++ b/data/web/lang/lang.cs.json @@ -676,7 +676,7 @@ "release_subject": "Potenciálně škodlivá položka v karanténě %s", "remove": "Smazat", "rspamd_result": "Skóre Rspamd", - "sender": "Odesílatel", + "sender": "Odesílatel (SMTP)", "show_item": "Zobrazit položku", "spam_score": "Skóre", "subj": "Předmět", diff --git a/data/web/lang/lang.de.json b/data/web/lang/lang.de.json index 27ff48a1..fade6109 100644 --- a/data/web/lang/lang.de.json +++ b/data/web/lang/lang.de.json @@ -758,7 +758,10 @@ "release_subject": "Potentiell schädliche Nachricht aus Quarantäne: %s", "remove": "Entfernen", "rspamd_result": "Rspamd Ergebnis", - "sender": "Sender", + "quick_release_link": "Quick-Release Link öffnen", + "quick_delete_link": "Quick-Delete Link öffnen", + "sender": "Sender (SMTP)", + "sender_header": "Sender (From-Header)", "show_item": "Details", "spam_score": "Bewertung", "subj": "Betreff", diff --git a/data/web/lang/lang.en.json b/data/web/lang/lang.en.json index fd788a28..8b9aaf83 100644 --- a/data/web/lang/lang.en.json +++ b/data/web/lang/lang.en.json @@ -757,7 +757,10 @@ "release_subject": "Potentially damaging quarantine item %s", "remove": "Remove", "rspamd_result": "Rspamd result", - "sender": "Sender", + "sender": "Sender (SMTP)", + "sender_header": "Sender (From header)", + "quick_release_link": "Open quick release link", + "quick_delete_link": "Open quick delete link", "show_item": "Show item", "spam_score": "Score", "subj": "Subject", diff --git a/data/web/lang/lang.es.json b/data/web/lang/lang.es.json index da69adb6..b98921d0 100644 --- a/data/web/lang/lang.es.json +++ b/data/web/lang/lang.es.json @@ -602,7 +602,7 @@ "release_body": "Adjuntamos el mensaje en formato eml a este correo.", "release_subject": "Mensaje de cuarentena potencialmente dañino %s", "remove": "Remover", - "sender": "Remitente", + "sender": "Remitente (SMTP)", "show_item": "Mostrar item", "spam_score": "Puntaje", "subj": "Asunto", diff --git a/data/web/lang/lang.fi.json b/data/web/lang/lang.fi.json index e9c8683a..12f70ee1 100644 --- a/data/web/lang/lang.fi.json +++ b/data/web/lang/lang.fi.json @@ -677,7 +677,7 @@ "release_subject": "Mahdollisesti vahingoittava karanteeni aihe %s", "remove": "Poista", "rspamd_result": "Rspamd-tulos", - "sender": "Lähettäjä", + "sender": "Lähettäjä (SMTP)", "show_item": "Näytä tuote", "spam_score": "Pisteet", "subj": "Aihe", diff --git a/data/web/lang/lang.lv.json b/data/web/lang/lang.lv.json index 28b93c6c..02e9b521 100644 --- a/data/web/lang/lang.lv.json +++ b/data/web/lang/lang.lv.json @@ -388,7 +388,7 @@ "release_body": "Šim ziņojumam mēs esam pievienojuši jūsu ziņojumu kā eml failu.", "release_subject": "Potenciāli kaitīgs karantīnas vienums %s", "remove": "Noņemt", - "sender": "Sūtītājs", + "sender": "Sūtītājs (SMTP)", "show_item": "Parādīt vienumus", "subj": "Priekšmets", "text_from_html_content": "Saturs (konvertēts html)", diff --git a/data/web/lang/lang.nl.json b/data/web/lang/lang.nl.json index edcd2fb9..e668eb6c 100644 --- a/data/web/lang/lang.nl.json +++ b/data/web/lang/lang.nl.json @@ -753,7 +753,7 @@ "release_subject": "Mogelijk schadelijk quarantaine-item %s", "remove": "Verwijder", "rspamd_result": "Rspamd-resultaat", - "sender": "Afzender", + "sender": "Afzender (SMTP)", "show_item": "Toon item", "spam_score": "Score", "subj": "Onderwerp", diff --git a/data/web/lang/lang.ro.json b/data/web/lang/lang.ro.json index f2eab8ca..06cf0de9 100644 --- a/data/web/lang/lang.ro.json +++ b/data/web/lang/lang.ro.json @@ -757,7 +757,7 @@ "release_subject": "Element în carantină potențial dăunător %s", "remove": "Elimină", "rspamd_result": "Rezultat Rspamd", - "sender": "Expeditor", + "sender": "Expeditor (SMTP)", "show_item": "Afișează elementul", "spam_score": "Scor", "subj": "Subiect", diff --git a/data/web/lang/lang.ru.json b/data/web/lang/lang.ru.json index 8b15ac58..d83efca2 100644 --- a/data/web/lang/lang.ru.json +++ b/data/web/lang/lang.ru.json @@ -759,7 +759,7 @@ "release_subject": "Потенциально опасное письмо %s", "remove": "Удалить", "rspamd_result": "Результат Rspamd", - "sender": "Отправитель", + "sender": "Отправитель (SMTP)", "show_item": "Показать", "spam_score": "Оценка", "subj": "Тема письма", diff --git a/data/web/lang/lang.sk.json b/data/web/lang/lang.sk.json index b023fe19..c74fa1ec 100644 --- a/data/web/lang/lang.sk.json +++ b/data/web/lang/lang.sk.json @@ -750,7 +750,7 @@ "release_subject": "Potenciálne nebezpečná položka v karanténne %s", "remove": "Odstrániť", "rspamd_result": "Rspamd výsledok", - "sender": "Odosielateľ", + "sender": "Odosielateľ (SMTP)", "show_item": "Ukázať súbor", "spam_score": "Výsledok", "subj": "Predmet", diff --git a/data/web/lang/lang.sv.json b/data/web/lang/lang.sv.json index bbe4015a..ac29ecee 100644 --- a/data/web/lang/lang.sv.json +++ b/data/web/lang/lang.sv.json @@ -757,7 +757,7 @@ "release_subject": "Potentiellt skadlig karantänsmeddelande %s", "remove": "Ta bort", "rspamd_result": "Rspamd resultat", - "sender": "Avsändare", + "sender": "Avsändare (SMTP)", "show_item": "Visa objekt", "spam_score": "Betyg", "subj": "Ämne", diff --git a/data/web/modals/quarantine.php b/data/web/modals/quarantine.php index d907aba7..2ec40b85 100644 --- a/data/web/modals/quarantine.php +++ b/data/web/modals/quarantine.php @@ -26,6 +26,14 @@ if (!isset($_SESSION['mailcow_cc_role'])) {

+
+ +

+
+
+ +

+

@@ -53,6 +61,9 @@ if (!isset($_SESSION['mailcow_cc_role'])) {
             
             
  • +
  • +
  • +
  • diff --git a/data/web/qhandler.php b/data/web/qhandler.php index fd4dc235..91fcd4af 100644 --- a/data/web/qhandler.php +++ b/data/web/qhandler.php @@ -1,6 +1,10 @@ @@ -29,7 +33,29 @@ elseif (in_array($_GET['action'], array('release', 'delete'))) {
    - + + +
    + +

    :

    +

    +
    +
    + +

    +
    +
    + +

    +
    +
    + +

    +
    +
    + +

    +
    @@ -49,7 +75,29 @@ elseif (in_array($_GET['action'], array('release', 'delete'))) {
    - + + +
    + +

    :

    +

    +
    +
    + +

    +
    +
    + +

    +
    +
    + +

    +
    +
    + +

    +
    @@ -64,5 +112,6 @@ elseif (in_array($_GET['action'], array('release', 'delete'))) { endif; } } +$js_minifier->add('/web/js/site/qhandler.js'); require_once $_SERVER['DOCUMENT_ROOT'] . '/inc/footer.inc.php'; ?>