generated from smedev/Template-for-SMEServer-Contribs-Package
Add in software files and templates
This commit is contained in:
110
root/opt/dmarc-srg/classes/Mail/MailAttachment.php
Normal file
110
root/opt/dmarc-srg/classes/Mail/MailAttachment.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
|
||||
* Copyright (C) 2020 Aleksey Andreev (liuch)
|
||||
*
|
||||
* Available at:
|
||||
* https://github.com/liuch/dmarc-srg
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Liuch\DmarcSrg\Mail;
|
||||
|
||||
use Liuch\DmarcSrg\ReportFile\ReportFile;
|
||||
use Liuch\DmarcSrg\Exception\SoftException;
|
||||
|
||||
class MailAttachment
|
||||
{
|
||||
private $conn;
|
||||
private $filename;
|
||||
private $bytes;
|
||||
private $number;
|
||||
private $mnumber;
|
||||
private $encoding;
|
||||
private $stream;
|
||||
private $mime_type;
|
||||
|
||||
public function __construct($conn, $params)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
$this->filename = $params['filename'];
|
||||
$this->bytes = $params['bytes'];
|
||||
$this->number = $params['number'];
|
||||
$this->mnumber = $params['mnumber'];
|
||||
$this->encoding = $params['encoding'];
|
||||
$this->stream = null;
|
||||
$this->mime_type = null;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (!is_null($this->stream) && get_resource_type($this->stream) == 'stream') {
|
||||
fclose($this->stream);
|
||||
}
|
||||
}
|
||||
|
||||
public function mimeType()
|
||||
{
|
||||
if (is_null($this->mime_type)) {
|
||||
$this->mime_type = ReportFile::getMimeType($this->filename, $this->datastream());
|
||||
}
|
||||
return $this->mime_type;
|
||||
}
|
||||
|
||||
public function size()
|
||||
{
|
||||
return $this->bytes;
|
||||
}
|
||||
|
||||
public function filename()
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
public function extension()
|
||||
{
|
||||
return pathinfo($this->filename, PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
public function datastream()
|
||||
{
|
||||
if (is_null($this->stream)) {
|
||||
$this->stream = fopen('php://temp', 'r+');
|
||||
fwrite($this->stream, $this->toString());
|
||||
}
|
||||
rewind($this->stream);
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
private function fetchBody()
|
||||
{
|
||||
return imap_fetchbody($this->conn, $this->mnumber, strval($this->number), FT_PEEK);
|
||||
}
|
||||
|
||||
private function toString()
|
||||
{
|
||||
switch ($this->encoding) {
|
||||
case ENC7BIT:
|
||||
case ENC8BIT:
|
||||
case ENCBINARY:
|
||||
return $this->fetchBody();
|
||||
case ENCBASE64:
|
||||
return base64_decode($this->fetchBody());
|
||||
case ENCQUOTEDPRINTABLE:
|
||||
return imap_qprint($this->fetchBody());
|
||||
}
|
||||
throw new SoftException('Encoding failed: Unknown encoding');
|
||||
}
|
||||
}
|
142
root/opt/dmarc-srg/classes/Mail/MailBody.php
Normal file
142
root/opt/dmarc-srg/classes/Mail/MailBody.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
|
||||
* Copyright (C) 2020 Aleksey Andreev (liuch)
|
||||
*
|
||||
* Available at:
|
||||
* https://github.com/liuch/dmarc-srg
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Liuch\DmarcSrg\Mail;
|
||||
|
||||
/**
|
||||
* The class is designed to easily create multipart/alternative message bodies.
|
||||
*/
|
||||
class MailBody
|
||||
{
|
||||
private $text = null;
|
||||
private $html = null;
|
||||
private $boundary = null;
|
||||
|
||||
/**
|
||||
* Sets text content as a part of the message body
|
||||
*
|
||||
* @param array $text Text part of the message as an array of strings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setText(array &$text): void
|
||||
{
|
||||
$this->text = $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets html content as a part of the message body
|
||||
*
|
||||
* @param array $html Html part of the message as and array of strings
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setHtml(array $html): void
|
||||
{
|
||||
$this->html = $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Content-Type header value for the whole message
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function contentType(): string
|
||||
{
|
||||
if ($this->boundary()) {
|
||||
$ctype = 'multipart/alternative; boundary="' . $this->boundary() . '"';
|
||||
} else {
|
||||
if (!is_null($this->html)) {
|
||||
$ctype = 'text/html';
|
||||
} else {
|
||||
$ctype = 'text/plain';
|
||||
}
|
||||
$ctype .= '; charset=utf-8';
|
||||
}
|
||||
return $ctype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the message parts with required headers as an array of strings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function content(): array
|
||||
{
|
||||
$content = [];
|
||||
if ($this->text) {
|
||||
$this->addBodyPart('text', $this->text, $content);
|
||||
}
|
||||
if ($this->html) {
|
||||
$this->addBodyPart('html', $this->html, $content);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a boundary string of the message. If the body has only one part of the content
|
||||
* it returns null
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private function boundary()
|
||||
{
|
||||
if (!$this->boundary) {
|
||||
if ($this->text && $this->html) {
|
||||
$this->boundary = '==========' . sha1(uniqid()) . '=====';
|
||||
}
|
||||
}
|
||||
return $this->boundary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified part of the content to the array passed as the third parameter
|
||||
* with the required headers.
|
||||
*
|
||||
* @param string $type Type of the content to add
|
||||
* @param array $part Part of the content to add
|
||||
* @param array $content Where the data with headers should be added
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function addBodyPart(string $type, array &$part, array &$content): void
|
||||
{
|
||||
if ($this->boundary()) {
|
||||
$content[] = '--' . $this->boundary();
|
||||
switch ($type) {
|
||||
case 'text':
|
||||
$ctype = 'text/plain';
|
||||
break;
|
||||
case 'html':
|
||||
$ctype = 'text/html';
|
||||
break;
|
||||
}
|
||||
$content[] = 'Content-Type: ' . $ctype . '; charset=utf-8';
|
||||
$content[] = 'Content-Transfer-Encoding: 7bit';
|
||||
$content[] = '';
|
||||
}
|
||||
foreach ($part as $row) {
|
||||
$content[] = $row;
|
||||
}
|
||||
unset($part);
|
||||
}
|
||||
}
|
468
root/opt/dmarc-srg/classes/Mail/MailBox.php
Normal file
468
root/opt/dmarc-srg/classes/Mail/MailBox.php
Normal file
@@ -0,0 +1,468 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
|
||||
* Copyright (C) 2020 Aleksey Andreev (liuch)
|
||||
*
|
||||
* Available at:
|
||||
* https://github.com/liuch/dmarc-srg
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Liuch\DmarcSrg\Mail;
|
||||
|
||||
use Liuch\DmarcSrg\Core;
|
||||
use Liuch\DmarcSrg\ErrorHandler;
|
||||
use Liuch\DmarcSrg\Exception\SoftException;
|
||||
use Liuch\DmarcSrg\Exception\LogicException;
|
||||
use Liuch\DmarcSrg\Exception\MailboxException;
|
||||
|
||||
class MailBox
|
||||
{
|
||||
private $conn;
|
||||
private $server;
|
||||
private $host;
|
||||
private $mbox;
|
||||
private $name;
|
||||
private $uname;
|
||||
private $passw;
|
||||
private $delim;
|
||||
private $attrs;
|
||||
private $expunge;
|
||||
private $options;
|
||||
|
||||
public function __construct($params)
|
||||
{
|
||||
if (!is_array($params)) {
|
||||
throw new LogicException('Incorrect mailbox params');
|
||||
}
|
||||
|
||||
$this->conn = null;
|
||||
$this->uname = $params['username'];
|
||||
$this->passw = $params['password'];
|
||||
if (isset($params['name']) && is_string($params['name']) && strlen($params['name']) > 0) {
|
||||
$this->name = $params['name'];
|
||||
} else {
|
||||
$name = $this->uname;
|
||||
$pos = strpos($name, '@');
|
||||
if ($pos !== false && $pos !== 0) {
|
||||
$name = substr($name, 0, $pos);
|
||||
}
|
||||
$this->name = $name;
|
||||
}
|
||||
$this->mbox = $params['mailbox'];
|
||||
$this->host = $params['host'];
|
||||
|
||||
$flags = $params['encryption'] ?? '';
|
||||
switch ($flags) {
|
||||
case 'ssl':
|
||||
default:
|
||||
$flags = '/ssl';
|
||||
break;
|
||||
case 'none':
|
||||
$flags = '/notls';
|
||||
break;
|
||||
case 'starttls':
|
||||
$flags = '/tls';
|
||||
break;
|
||||
}
|
||||
if (isset($params['novalidate-cert']) && $params['novalidate-cert'] === true) {
|
||||
$flags .= '/novalidate-cert';
|
||||
}
|
||||
|
||||
$this->server = sprintf('{%s/imap%s}', $this->host, $flags);
|
||||
$this->expunge = false;
|
||||
$this->options = [];
|
||||
|
||||
if (isset($params['auth_exclude'])) {
|
||||
$auth_exclude = $params['auth_exclude'];
|
||||
switch (gettype($auth_exclude)) {
|
||||
case 'string':
|
||||
$auth_exclude = [ $auth_exclude ];
|
||||
break;
|
||||
case 'array':
|
||||
break;
|
||||
default:
|
||||
$auth_exclude = null;
|
||||
break;
|
||||
}
|
||||
if ($auth_exclude) {
|
||||
$this->options['DISABLE_AUTHENTICATOR'] = $auth_exclude;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if (extension_loaded('imap')) {
|
||||
$this->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
public function childMailbox(string $mailbox_name)
|
||||
{
|
||||
$this->ensureConnection();
|
||||
try {
|
||||
$mb_list = imap_list(
|
||||
$this->conn,
|
||||
self::utf8ToMutf7($this->server),
|
||||
self::utf8ToMutf7($this->mbox) . $this->delim . self::utf8ToMutf7($mailbox_name)
|
||||
);
|
||||
} catch (\ErrorException $e) {
|
||||
$mb_list = false;
|
||||
}
|
||||
$this->ensureErrorLog('imap_list');
|
||||
if (!$mb_list) {
|
||||
return null;
|
||||
}
|
||||
$child = clone $this;
|
||||
$child->mbox .= $this->delim . $mailbox_name;
|
||||
$child->conn = null;
|
||||
$child->expunge = false;
|
||||
return $child;
|
||||
}
|
||||
|
||||
public function name()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function host()
|
||||
{
|
||||
return $this->host;
|
||||
}
|
||||
|
||||
public function mailbox()
|
||||
{
|
||||
return $this->mbox;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
try {
|
||||
$this->ensureConnection();
|
||||
try {
|
||||
$res = imap_status(
|
||||
$this->conn,
|
||||
self::utf8ToMutf7($this->server . $this->mbox),
|
||||
SA_MESSAGES | SA_UNSEEN
|
||||
);
|
||||
} catch (\ErrorException $e) {
|
||||
$res = false;
|
||||
}
|
||||
$error_message = $this->ensureErrorLog();
|
||||
if (!$res) {
|
||||
throw new MailboxException($error_message ?? 'Failed to get the mail box status');
|
||||
}
|
||||
|
||||
if ($this->attrs & \LATT_NOSELECT) {
|
||||
throw new MailboxException('The resource is not a mailbox');
|
||||
}
|
||||
|
||||
$this->checkRights();
|
||||
} catch (MailboxException $e) {
|
||||
return ErrorHandler::exceptionResult($e);
|
||||
}
|
||||
return [
|
||||
'error_code' => 0,
|
||||
'message' => 'Successfully',
|
||||
'status' => [
|
||||
'messages' => $res->messages,
|
||||
'unseen' => $res->unseen
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function search($criteria)
|
||||
{
|
||||
$this->ensureConnection();
|
||||
try {
|
||||
$res = imap_search($this->conn, $criteria);
|
||||
} catch (\ErrorException $e) {
|
||||
$res = false;
|
||||
}
|
||||
$error_message = $this->ensureErrorLog('imap_search');
|
||||
if ($res === false) {
|
||||
if (!$error_message) {
|
||||
return [];
|
||||
}
|
||||
throw new MailboxException(
|
||||
'Failed to search email messages',
|
||||
-1,
|
||||
new \ErrorException($error_message)
|
||||
);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function sort($criteria, $search_criteria, $reverse)
|
||||
{
|
||||
$this->ensureConnection();
|
||||
try {
|
||||
$res = imap_sort($this->conn, $criteria, $reverse ? 1 : 0, SE_NOPREFETCH, $search_criteria);
|
||||
} catch (\ErrorException $e) {
|
||||
$res = false;
|
||||
}
|
||||
$error_message = $this->ensureErrorLog('imap_sort');
|
||||
if ($res === false) {
|
||||
if (!$error_message) {
|
||||
return [];
|
||||
}
|
||||
throw new MailboxException(
|
||||
'Failed to sort email messages',
|
||||
-1,
|
||||
new \ErrorException($error_message)
|
||||
);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function message($number)
|
||||
{
|
||||
return new MailMessage($this->conn, $number);
|
||||
}
|
||||
|
||||
public function ensureMailbox($mailbox_name)
|
||||
{
|
||||
$mbn = self::utf8ToMutf7($mailbox_name);
|
||||
$srv = self::utf8ToMutf7($this->server);
|
||||
$mbo = self::utf8ToMutf7($this->mbox);
|
||||
$this->ensureConnection();
|
||||
try {
|
||||
$mb_list = imap_list($this->conn, $srv, $mbo . $this->delim . $mbn);
|
||||
} catch (\ErrorException $e) {
|
||||
$mb_list = false;
|
||||
}
|
||||
$error_message = $this->ensureErrorLog('imap_list');
|
||||
if (empty($mb_list)) {
|
||||
if ($error_message) {
|
||||
throw new MailboxException(
|
||||
'Failed to get the list of mailboxes',
|
||||
-1,
|
||||
new \ErrorException($error_message)
|
||||
);
|
||||
}
|
||||
|
||||
$new_mailbox = "{$srv}{$mbo}{$this->delim}{$mbn}";
|
||||
try {
|
||||
$res = imap_createmailbox($this->conn, $new_mailbox);
|
||||
} catch (\ErrorException $e) {
|
||||
$res = false;
|
||||
}
|
||||
$error_message = $this->ensureErrorLog('imap_createmailbox');
|
||||
if (!$res) {
|
||||
throw new MailboxException(
|
||||
'Failed to create a new mailbox',
|
||||
-1,
|
||||
new \ErrorException($error_message ?? 'Unknown')
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
imap_subscribe($this->conn, $new_mailbox);
|
||||
} catch (\ErrorException $e) {
|
||||
}
|
||||
$this->ensureErrorLog('imap_subscribe');
|
||||
}
|
||||
}
|
||||
|
||||
public function moveMessage($number, $mailbox_name)
|
||||
{
|
||||
$this->ensureConnection();
|
||||
$target = self::utf8ToMutf7($this->mbox) . $this->delim . self::utf8ToMutf7($mailbox_name);
|
||||
try {
|
||||
$res = imap_mail_move($this->conn, strval($number), $target);
|
||||
} catch (\ErrorException $e) {
|
||||
$res = false;
|
||||
}
|
||||
$error_message = $this->ensureErrorLog('imap_mail_move');
|
||||
if (!$res) {
|
||||
throw new MailboxException(
|
||||
'Failed to move a message',
|
||||
-1,
|
||||
new \ErrorException($error_message ?? 'Unknown')
|
||||
);
|
||||
}
|
||||
$this->expunge = true;
|
||||
}
|
||||
|
||||
public function deleteMessage($number)
|
||||
{
|
||||
$this->ensureConnection();
|
||||
try {
|
||||
imap_delete($this->conn, strval($number));
|
||||
} catch (\ErrorException $e) {
|
||||
}
|
||||
$this->ensureErrorLog('imap_delete');
|
||||
$this->expunge = true;
|
||||
}
|
||||
|
||||
public static function resetErrorStack()
|
||||
{
|
||||
imap_errors();
|
||||
imap_alerts();
|
||||
}
|
||||
|
||||
private function ensureConnection()
|
||||
{
|
||||
if (is_null($this->conn)) {
|
||||
$error_message = null;
|
||||
$srv = self::utf8ToMutf7($this->server);
|
||||
try {
|
||||
$this->conn = imap_open(
|
||||
$srv,
|
||||
$this->uname,
|
||||
$this->passw,
|
||||
OP_HALFOPEN,
|
||||
0,
|
||||
$this->options
|
||||
);
|
||||
} catch (\ErrorException $e) {
|
||||
$this->conn = null;
|
||||
}
|
||||
if ($this->conn) {
|
||||
$mbx = self::utf8ToMutf7($this->mbox);
|
||||
try {
|
||||
$mb_list = imap_getmailboxes($this->conn, $srv, $mbx);
|
||||
} catch (\ErrorException $e) {
|
||||
$mb_list = null;
|
||||
}
|
||||
if ($mb_list && count($mb_list) === 1) {
|
||||
$this->delim = $mb_list[0]->delimiter ?? '/';
|
||||
$this->attrs = $mb_list[0]->attributes ?? 0;
|
||||
try {
|
||||
if (imap_reopen($this->conn, $srv . $mbx)) {
|
||||
return;
|
||||
}
|
||||
} catch (\ErrorException $e) {
|
||||
}
|
||||
} else {
|
||||
$error_message = "Mailbox `{$this->mbox}` not found";
|
||||
}
|
||||
}
|
||||
if (!$error_message) {
|
||||
$error_message = imap_last_error();
|
||||
if (!$error_message) {
|
||||
$error_message = 'Cannot connect to the mail server';
|
||||
}
|
||||
}
|
||||
Core::instance()->logger()->error("IMAP error: {$error_message}");
|
||||
self::resetErrorStack();
|
||||
if ($this->conn) {
|
||||
try {
|
||||
imap_close($this->conn);
|
||||
} catch (\ErrorException $e) {
|
||||
}
|
||||
$this->ensureErrorLog('imap_close');
|
||||
}
|
||||
$this->conn = null;
|
||||
throw new MailboxException($error_message);
|
||||
}
|
||||
}
|
||||
|
||||
private function ensureErrorLog(string $prefix = 'IMAP error')
|
||||
{
|
||||
if ($error_message = imap_last_error()) {
|
||||
self::resetErrorStack();
|
||||
$error_message = "{$prefix}: {$error_message}";
|
||||
Core::instance()->logger()->error($error_message);
|
||||
return $error_message;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private function checkRights(): void
|
||||
{
|
||||
if ($this->attrs & \LATT_NOINFERIORS) {
|
||||
throw new SoftException('The mailbox may not have any children mailboxes');
|
||||
}
|
||||
|
||||
if (!function_exists('imap_getacl')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mbox = self::utf8ToMutf7($this->mbox);
|
||||
try {
|
||||
$acls = imap_getacl($this->conn, $mbox);
|
||||
} catch (\ErrorException $e) {
|
||||
// It's not possible to get the ACLs information
|
||||
$acls = false;
|
||||
}
|
||||
$this->ensureErrorLog('imap_getacl');
|
||||
if ($acls !== false) {
|
||||
$needed_rights_map = [
|
||||
'l' => 'LOOKUP',
|
||||
'r' => 'READ',
|
||||
's' => 'WRITE-SEEN',
|
||||
't' => 'WRITE-DELETE',
|
||||
'k' => 'CREATE'
|
||||
];
|
||||
$result = [];
|
||||
$needed_rights = array_keys($needed_rights_map);
|
||||
foreach ([ "#{$this->uname}", '#authenticated', '#anyone' ] as $identifier) {
|
||||
if (isset($acls[$identifier])) {
|
||||
$rights = $acls[$identifier];
|
||||
foreach ($needed_rights as $r) {
|
||||
if (!str_contains($rights, $r)) {
|
||||
$result[] = $needed_rights_map[$r];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (count($result) > 0) {
|
||||
throw new SoftException(
|
||||
'Not enough rights. Additionally, these rights are required: ' . implode(', ', $result)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes messages marked for deletion, if any, and closes the connection
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function cleanup(): void
|
||||
{
|
||||
self::resetErrorStack();
|
||||
if (!is_null($this->conn)) {
|
||||
try {
|
||||
if ($this->expunge) {
|
||||
imap_expunge($this->conn);
|
||||
}
|
||||
} catch (\ErrorException $e) {
|
||||
}
|
||||
$this->ensureErrorLog('imap_expunge');
|
||||
|
||||
try {
|
||||
imap_close($this->conn);
|
||||
} catch (\ErrorException $e) {
|
||||
}
|
||||
$this->ensureErrorLog('imap_close');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It's a replacement for the standard function imap_utf8_to_mutf7
|
||||
*
|
||||
* @param string $s A UTF-8 encoded string
|
||||
*
|
||||
* @return string|false
|
||||
*/
|
||||
private static function utf8ToMutf7(string $s)
|
||||
{
|
||||
return mb_convert_encoding($s, 'UTF7-IMAP', 'UTF-8');
|
||||
}
|
||||
}
|
137
root/opt/dmarc-srg/classes/Mail/MailBoxes.php
Normal file
137
root/opt/dmarc-srg/classes/Mail/MailBoxes.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
|
||||
* Copyright (C) 2020 Aleksey Andreev (liuch)
|
||||
*
|
||||
* Available at:
|
||||
* https://github.com/liuch/dmarc-srg
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Liuch\DmarcSrg\Mail;
|
||||
|
||||
use Liuch\DmarcSrg\Core;
|
||||
use Liuch\DmarcSrg\ErrorHandler;
|
||||
use Liuch\DmarcSrg\Exception\LogicException;
|
||||
use Liuch\DmarcSrg\Exception\RuntimeException;
|
||||
use Liuch\DmarcSrg\Exception\MailboxException;
|
||||
|
||||
class MailBoxes implements \Iterator
|
||||
{
|
||||
private $box_list;
|
||||
private $index = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$mailboxes = Core::instance()->config('mailboxes');
|
||||
|
||||
$this->box_list = [];
|
||||
if (is_array($mailboxes)) {
|
||||
$cnt = count($mailboxes);
|
||||
if ($cnt > 0) {
|
||||
if (isset($mailboxes[0])) {
|
||||
for ($i = 0; $i < $cnt; ++$i) {
|
||||
$this->box_list[] = new MailBox($mailboxes[$i]);
|
||||
}
|
||||
} else {
|
||||
$this->box_list[] = new MailBox($mailboxes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->box_list);
|
||||
}
|
||||
|
||||
public function list()
|
||||
{
|
||||
$id = 0;
|
||||
$res = [];
|
||||
foreach ($this->box_list as &$mbox) {
|
||||
$id += 1;
|
||||
$res[] = [
|
||||
'id' => $id,
|
||||
'name' => $mbox->name(),
|
||||
'host' => $mbox->host(),
|
||||
'mailbox' => $mbox->mailbox()
|
||||
];
|
||||
}
|
||||
unset($mbox);
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function mailbox($id)
|
||||
{
|
||||
if (!is_int($id) || $id <= 0 || $id > count($this->box_list)) {
|
||||
throw new LogicException("Incorrect mailbox Id: {$i}");
|
||||
}
|
||||
return $this->box_list[$id - 1];
|
||||
}
|
||||
|
||||
public function check($id)
|
||||
{
|
||||
if ($id !== 0) {
|
||||
return $this->mailbox($id)->check();
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$err_cnt = 0;
|
||||
$box_cnt = count($this->box_list);
|
||||
for ($i = 0; $i < $box_cnt; ++$i) {
|
||||
$r = $this->box_list[$i]->check();
|
||||
if ($r['error_code'] !== 0) {
|
||||
++$err_cnt;
|
||||
}
|
||||
$results[] = $r;
|
||||
}
|
||||
$res = [];
|
||||
if ($err_cnt == 0) {
|
||||
$res['error_code'] = 0;
|
||||
$res['message'] = 'Success';
|
||||
} else {
|
||||
$res['error_code'] = -1;
|
||||
$res['message'] = sprintf('%d of the %d mailboxes failed the check', $err_cnt, $box_cnt);
|
||||
}
|
||||
$res['results'] = $results;
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function current(): object
|
||||
{
|
||||
return $this->box_list[$this->index];
|
||||
}
|
||||
|
||||
public function key(): int
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
public function next(): void
|
||||
{
|
||||
++$this->index;
|
||||
}
|
||||
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->index = 0;
|
||||
}
|
||||
|
||||
public function valid(): bool
|
||||
{
|
||||
return isset($this->box_list[$this->index]);
|
||||
}
|
||||
}
|
154
root/opt/dmarc-srg/classes/Mail/MailMessage.php
Normal file
154
root/opt/dmarc-srg/classes/Mail/MailMessage.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* dmarc-srg - A php parser, viewer and summary report generator for incoming DMARC reports.
|
||||
* Copyright (C) 2020 Aleksey Andreev (liuch)
|
||||
*
|
||||
* Available at:
|
||||
* https://github.com/liuch/dmarc-srg
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace Liuch\DmarcSrg\Mail;
|
||||
|
||||
use Liuch\DmarcSrg\Core;
|
||||
use Liuch\DmarcSrg\Exception\SoftException;
|
||||
use Liuch\DmarcSrg\Exception\MailboxException;
|
||||
|
||||
class MailMessage
|
||||
{
|
||||
private $conn;
|
||||
private $number;
|
||||
private $attachment;
|
||||
private $attachments_cnt;
|
||||
|
||||
public function __construct($conn, $number)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
$this->number = $number;
|
||||
$this->attachment = null;
|
||||
$this->attachments_cnt = -1;
|
||||
}
|
||||
|
||||
public function overview()
|
||||
{
|
||||
$res = @imap_fetch_overview($this->conn, strval($this->number));
|
||||
if (!isset($res[0])) {
|
||||
if ($error_message = imap_last_error()) {
|
||||
Core::instance()->logger()->error("imap_fetch_overview failed: {$error_message}");
|
||||
}
|
||||
MailBox::resetErrorStack();
|
||||
return false;
|
||||
}
|
||||
return $res[0];
|
||||
}
|
||||
|
||||
public function setSeen()
|
||||
{
|
||||
if (!@imap_setflag_full($this->conn, strval($this->number), '\\Seen')) {
|
||||
if ($error_message = imap_last_error()) {
|
||||
$error_message = '?';
|
||||
}
|
||||
MailBox::resetErrorStack();
|
||||
Core::instance()->logger()->error("imap_setflag_full failed: {$error_message}");
|
||||
throw new MailboxException("Failed to make a message seen: {$error_message}");
|
||||
}
|
||||
}
|
||||
|
||||
public function validate()
|
||||
{
|
||||
$this->ensureAttachment();
|
||||
if ($this->attachments_cnt !== 1) {
|
||||
throw new SoftException("Attachment count is not valid ({$this->attachments_cnt})");
|
||||
}
|
||||
|
||||
$bytes = $this->attachment->size();
|
||||
if ($bytes === -1) {
|
||||
throw new SoftException("Failed to get attached file size. Wrong message format?");
|
||||
}
|
||||
if ($bytes < 50 || $bytes > 1 * 1024 * 1024) {
|
||||
throw new SoftException("Attachment file size is not valid ({$bytes} bytes)");
|
||||
}
|
||||
|
||||
$mime_type = $this->attachment->mimeType();
|
||||
if (!in_array($mime_type, [ 'application/zip', 'application/gzip', 'application/x-gzip', 'text/xml' ])) {
|
||||
throw new SoftException("Attachment file type is not valid ({$mime_type})");
|
||||
}
|
||||
}
|
||||
|
||||
public function attachment()
|
||||
{
|
||||
return $this->attachment;
|
||||
}
|
||||
|
||||
private function ensureAttachment()
|
||||
{
|
||||
if ($this->attachments_cnt === -1) {
|
||||
$structure = imap_fetchstructure($this->conn, $this->number);
|
||||
if ($structure === false) {
|
||||
throw new MailboxException('FetchStructure failed: ' . imap_last_error());
|
||||
}
|
||||
$this->attachments_cnt = 0;
|
||||
$parts = isset($structure->parts) ? $structure->parts : [ $structure ];
|
||||
foreach ($parts as $index => &$part) {
|
||||
$att_part = $this->scanAttachmentPart($part, $index + 1);
|
||||
if ($att_part) {
|
||||
++$this->attachments_cnt;
|
||||
if (!$this->attachment) {
|
||||
$this->attachment = new MailAttachment($this->conn, $att_part);
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($part);
|
||||
}
|
||||
}
|
||||
|
||||
private function scanAttachmentPart(&$part, $number)
|
||||
{
|
||||
$filename = null;
|
||||
if ($part->ifdparameters) {
|
||||
$filename = $this->getAttribute($part->dparameters, 'filename');
|
||||
}
|
||||
|
||||
if (empty($filename) && $part->ifparameters) {
|
||||
$filename = $this->getAttribute($part->parameters, 'name');
|
||||
}
|
||||
|
||||
if (empty($filename)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'filename' => imap_utf8($filename),
|
||||
'bytes' => isset($part->bytes) ? $part->bytes : -1,
|
||||
'number' => $number,
|
||||
'mnumber' => $this->number,
|
||||
'encoding' => $part->encoding
|
||||
];
|
||||
}
|
||||
|
||||
private function getAttribute(&$params, $name)
|
||||
{
|
||||
// need to check all objects as imap_fetchstructure
|
||||
// returns multiple objects with the same attribute name,
|
||||
// but first entry contains a truncated value
|
||||
$value = null;
|
||||
foreach ($params as &$obj) {
|
||||
if (strcasecmp($obj->attribute, $name) === 0) {
|
||||
$value = $obj->value;
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user