mirror of
https://github.com/Karaka-Management/phpOMS.git
synced 2026-01-11 01:38:41 +00:00
draft mail handler
This commit is contained in:
parent
0cfe3a45bd
commit
77b26ad788
38
Message/Mail/DsnNotificationType.php
Normal file
38
Message/Mail/DsnNotificationType.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Message\Mail;
|
||||
|
||||
use phpOMS\Stdlib\Base\Enum;
|
||||
|
||||
/**
|
||||
* Dsn notification types enum.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract class DsnNotificationType extends Enum
|
||||
{
|
||||
public const NONE = '';
|
||||
|
||||
public const NEVER = 'NEVER';
|
||||
|
||||
public const SUCCESS = 'SUCCESS';
|
||||
|
||||
public const FAILURE = 'FAILURE';
|
||||
|
||||
public const DELAY = 'DELAY';
|
||||
}
|
||||
2401
Message/Mail/Email.php
Normal file
2401
Message/Mail/Email.php
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -1,669 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*
|
||||
* Extended based on:
|
||||
* GLGPL 2.1 License
|
||||
* © 2012 - 2015 Marcus Bointon, 2010 - 2012 Jim Jagielski, 2004 - 2009 Andy Prevost
|
||||
* © PHPMailer
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Message\Mail;
|
||||
|
||||
/**
|
||||
* Mail class.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @todo Orange-Management/phpOMS#34
|
||||
* Implement!!!
|
||||
*/
|
||||
abstract class EmailAbstract
|
||||
{
|
||||
/**
|
||||
* Host.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected string $host = '';
|
||||
|
||||
/**
|
||||
* Port.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected int $port = 25;
|
||||
|
||||
/**
|
||||
* Use ssl.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected bool $ssl = false;
|
||||
|
||||
/**
|
||||
* Mailbox base.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected string $mailbox = '';
|
||||
|
||||
/**
|
||||
* Timeout.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected int $timeout = 30;
|
||||
|
||||
/**
|
||||
* Connection.
|
||||
*
|
||||
* @var mixed
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $con = null;
|
||||
|
||||
/**
|
||||
* Submit type/software
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected string $submitType = SubmitType::SMTP;
|
||||
|
||||
/**
|
||||
* Sendmail path if submit type is not smtp or mail
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected string $sendmailPath = '';
|
||||
|
||||
/**
|
||||
* End of line
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected string $endOfLine = "\r\n";
|
||||
|
||||
/**
|
||||
* OAuth
|
||||
*
|
||||
* @var OAuth
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $oauth = null;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param string $host Host
|
||||
* @param int $port Host port
|
||||
* @param int $timeout Timeout
|
||||
* @param bool $ssl Use ssl
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __construct(string $host = 'localhost', int $port = 25, int $timeout = 30, bool $ssl = false)
|
||||
{
|
||||
$this->host = $host;
|
||||
$this->port = $port;
|
||||
$this->timeout = $timeout;
|
||||
$this->ssl = $ssl;
|
||||
|
||||
\imap_timeout(\IMAP_OPENTIMEOUT, $timeout);
|
||||
\imap_timeout(\IMAP_READTIMEOUT, $timeout);
|
||||
\imap_timeout(\IMAP_WRITETIMEOUT, $timeout);
|
||||
\imap_timeout(\IMAP_CLOSETIMEOUT, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode
|
||||
*
|
||||
* @param string $content Content to decode
|
||||
* @param int $encoding Encoding type
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function decode(string $content, string $encoding)
|
||||
{
|
||||
if ($encoding === EncodingType::E_BASE64) {
|
||||
return \imap_base64($content);
|
||||
} elseif ($encoding === EncodingType::E_8BIT) {
|
||||
return \imap_8bit($content);
|
||||
}
|
||||
|
||||
return \imap_qprint($content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Descrutor
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect server
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function disconnect() : void
|
||||
{
|
||||
if ($this->con !== null) {
|
||||
\imap_close($this->con);
|
||||
$this->con = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*
|
||||
* @param string $user Username
|
||||
* @param string $pass Password
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function connect(string $user = '', string $pass = '') : bool
|
||||
{
|
||||
$this->mailbox = \substr($this->mailbox, 0, -1) . ($this->ssl ? '/ssl/validate-cert' : '/novalidate-cert') . '}';
|
||||
|
||||
try {
|
||||
$this->con = \imap_open($this->mailbox . 'INBOX', $user, $pass);
|
||||
|
||||
return true;
|
||||
} catch (\Throwable $t) {
|
||||
$this->con = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test connection
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function isConnected() : bool
|
||||
{
|
||||
return $this->con === null ? false : \imap_ping($this->con);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set submit type/software
|
||||
*
|
||||
* @param string $submitType Submit type/software
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function setSubmitType(string $submitType) : void
|
||||
{
|
||||
$this->submitType = $submitType;
|
||||
|
||||
if ($this->submitType === SubmitType::SMTP
|
||||
|| $this->submitType === SubmitType::MAIL
|
||||
) {
|
||||
$this->endOfLine = $this->submitType === SubmitType::SMTP || !\stripos(\PHP_OS, 'WIN') === 0 ? \PHP_EOL : "\r\n";
|
||||
} elseif ($this->submitType === SubmitType::SENDMAIL) {
|
||||
$this->endOfLine = \PHP_EOL;
|
||||
$path = \ini_get('sendmail_path');
|
||||
|
||||
$this->sendmailPath = \stripos($path, 'sendmail') === false ? '/usr/sbin/sendmail' : $path;
|
||||
} elseif ($this->submitType === SubmitType::QMAIL) {
|
||||
$this->endOfLine = \PHP_EOL;
|
||||
$path = \ini_get('sendmail_path');
|
||||
|
||||
$this->sendmailPath = \stripos($path, 'qmail') === false ? '/var/qmail/bin/qmail-inject' : $path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Mail
|
||||
*
|
||||
* @param Mail $mail Mail to send
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function send(Mail $mail) : bool
|
||||
{
|
||||
if (empty($mail->getTo())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->preSend($mail);
|
||||
$this->postSend($mail);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function preSend(Mail $mail) : bool
|
||||
{
|
||||
$this->endOfLine = $this->submitType === SubmitType::SMTP
|
||||
|| ($this->submitType === SubmitType::MAIL && \stripos(\PHP_OS, 'WIN') === 0)
|
||||
? "\r\n" : \PHP_EOL;
|
||||
|
||||
$this->setMessageType();
|
||||
|
||||
if ($this->submitType === SubmitType::MAIL) {
|
||||
$this->header .= 'to: ' . $this->addrAppend('to', $this->to);
|
||||
$this->header .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->subject)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function postSend(Mail $mail) : bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get boxes.
|
||||
*
|
||||
* @param string $pattern Pattern for boxes
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getBoxes(string $pattern = '*') : array
|
||||
{
|
||||
$list = \imap_list($this->con, $this->host, $pattern);
|
||||
|
||||
return $list === false ? [] : $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get inbox quota.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getQuota() : array
|
||||
{
|
||||
$quota = [];
|
||||
|
||||
try {
|
||||
$quota = \imap_get_quotaroot($this->con, "INBOX");
|
||||
} finally {
|
||||
return $quota === false ? [] : $quota;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get email.
|
||||
*
|
||||
* @param string $id mail id
|
||||
*
|
||||
* @return Mail
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getEmail(string $id) : Mail
|
||||
{
|
||||
$mail = new Mail($id);
|
||||
|
||||
if ((int) $id > $this->countMessages()) {
|
||||
return $mail;
|
||||
}
|
||||
|
||||
$mail->setOverview(\imap_fetch_overview($this->con, $id));
|
||||
$mail->setBody(\imap_fetchbody($this->con, (int) $id, '1.1'));
|
||||
//$mail->setEncoding(\imap_fetchstructure($this->con, (int) $id));
|
||||
|
||||
return $mail;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all inbox messages.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxAll() : array
|
||||
{
|
||||
return $this->getInboxOverview('ALL');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get inbox overview.
|
||||
*
|
||||
* @param string $option Inbox option (imap_search creterias)
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxOverview(string $option = 'ALL') : array
|
||||
{
|
||||
$ids = \imap_search($this->con, $option, \SE_FREE, 'UTF-8');
|
||||
|
||||
return \is_array($ids) ? \imap_fetch_overview($this->con, \implode(',', $ids)) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all new inbox messages.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxNew() : array
|
||||
{
|
||||
return $this->getInboxOverview('NEW');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all inbox messages from a person.
|
||||
*
|
||||
* @param string $from Messages from
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxFrom(string $from) : array
|
||||
{
|
||||
return $this->getInboxOverview('FROM "' . $from . '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all inbox messages to a person.
|
||||
*
|
||||
* @param string $to Messages to
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxTo(string $to) : array
|
||||
{
|
||||
return $this->getInboxOverview('TO "' . $to . '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all inbox messages cc a person.
|
||||
*
|
||||
* @param string $cc Messages cc
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxCc(string $cc) : array
|
||||
{
|
||||
return $this->getInboxOverview('CC "' . $cc . '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all inbox messages bcc a person.
|
||||
*
|
||||
* @param string $bcc Messages bcc
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxBcc(string $bcc) : array
|
||||
{
|
||||
return $this->getInboxOverview('BCC "' . $bcc . '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all answered inbox messages.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxAnswered() : array
|
||||
{
|
||||
return $this->getInboxOverview('ANSWERED');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all inbox messages with a certain subject.
|
||||
*
|
||||
* @param string $subject Subject
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxSubject(string $subject) : array
|
||||
{
|
||||
return $this->getInboxOverview('SUBJECT "' . $subject . '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all inbox messages from a certain date onwards.
|
||||
*
|
||||
* @param \DateTime $since Messages since
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxSince(\DateTime $since) : array
|
||||
{
|
||||
return $this->getInboxOverview('SINCE "' . $since->format('d-M-Y') . '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all unseen inbox messages.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxUnseen() : array
|
||||
{
|
||||
return $this->getInboxOverview('UNSEEN');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all seen inbox messages.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxSeen() : array
|
||||
{
|
||||
return $this->getInboxOverview('SEEN');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all deleted inbox messages.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxDeleted() : array
|
||||
{
|
||||
return $this->getInboxOverview('DELETED');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all inbox messages with text.
|
||||
*
|
||||
* @param string $text Text in message body
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getInboxText(string $text) : array
|
||||
{
|
||||
return $this->getInboxOverview('TEXT "' . $text . '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create mailbox
|
||||
*
|
||||
* @param string $mailbox Mailbox to create
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function createMailbox(string $mailbox) : bool
|
||||
{
|
||||
return \imap_createmailbox($this->con, $mailbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename mailbox
|
||||
*
|
||||
* @param string $old Old mailbox name
|
||||
* @param string $new New mailbox name
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function renameMailbox(string $old, string $new) : bool
|
||||
{
|
||||
return \imap_renamemailbox($this->con, $old, $new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete mailbox
|
||||
*
|
||||
* @param string $mailbox Mailbox to delete
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function deleteMailbox(string $mailbox) : bool
|
||||
{
|
||||
return \imap_deletemailbox($this->con, $mailbox);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check message to delete
|
||||
*
|
||||
* @param int $id Message id
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function deleteMessage(int $id) : bool
|
||||
{
|
||||
return \imap_delete($this->con, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all marked messages
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function deleteMarkedMessages() : bool
|
||||
{
|
||||
return \imap_expunge($this->con);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check message to delete
|
||||
*
|
||||
* @param int $length Amount of message overview
|
||||
* @param int $start Start index of the overview for pagination
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getMessageOverview(int $length = 0, int $start = 1) : array
|
||||
{
|
||||
if ($length === 0) {
|
||||
$info = \imap_check($this->con);
|
||||
$length = $info->Nmsgs;
|
||||
}
|
||||
|
||||
return \imap_fetch_overview($this->con, $start . ':' . ($length - 1 + $start), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count messages
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function countMessages() : int
|
||||
{
|
||||
return \imap_num_msg($this->con);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get message header
|
||||
*
|
||||
* @param int $id Message id
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getMessageHeader(int $id) : string
|
||||
{
|
||||
if ($id > $this->countMessages()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return \imap_fetchheader($this->con, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the OAuth connection
|
||||
*
|
||||
* @param OAuth $oauth OAuth
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function setOAuth($oauth) : void
|
||||
{
|
||||
$this->oauth = $oauth;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ use phpOMS\Stdlib\Base\Enum;
|
|||
/**
|
||||
* Encoding enum.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
|
|
|
|||
|
|
@ -19,14 +19,16 @@ use phpOMS\Stdlib\Base\Enum;
|
|||
/**
|
||||
* Encryption enum.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract class EncryptionType extends Enum
|
||||
{
|
||||
public const NONE = '';
|
||||
|
||||
public const TLS = 'tls';
|
||||
|
||||
public const SSL = 'ssl';
|
||||
public const SMTPS = 'ssl';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use phpOMS\Stdlib\Base\Enum;
|
|||
/**
|
||||
* Submit enum.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use phpOMS\Stdlib\Base\Enum;
|
|||
/**
|
||||
* Calendar message types enum.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
|
|
|
|||
|
|
@ -22,22 +22,142 @@ namespace phpOMS\Message\Mail;
|
|||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Imap extends EmailAbstract
|
||||
class Imap extends MailHandler implements MailBoxInterface
|
||||
{
|
||||
/**
|
||||
* Connect to server
|
||||
* Destructor.
|
||||
*
|
||||
* @param string $user Username
|
||||
* @param string $pass Password
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->inboxClose();
|
||||
parent::__destruct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function connect(string $user = '', string $pass = '') : bool
|
||||
public function connectInbox() : bool
|
||||
{
|
||||
$this->mailbox = '{' . $this->host . ':' . $this->port . '/imap}';
|
||||
$this->mailbox = ($tmp = \imap_open(
|
||||
'{'
|
||||
. $this->host . ':' . $this->port . '/imap'
|
||||
. ($this->encryption !== EncryptionType::NONE ? '/ssl' : '')
|
||||
. '}',
|
||||
$this->username, $this->password
|
||||
) === false) ? null : $tmp;
|
||||
}
|
||||
|
||||
return parent::connect($user, $pass);
|
||||
public function getBoxes() : array
|
||||
{
|
||||
$list = \imap_list($this->mailbox, '{' . $this->host . ':' . $this->port . '}');
|
||||
if (!\is_array($list)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($list as $key => $value) {
|
||||
$list[$key] = \imap_utf7_decode($value);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function renameBox(string $old, string $new) : bool
|
||||
{
|
||||
return \imap_renamemailbox($this->mailbox, $old, \imap_utf7_encode($new));
|
||||
}
|
||||
|
||||
public function deleteBox(string $box) : bool
|
||||
{
|
||||
return \imap_deletemailbox($this->mailbox, $box);
|
||||
}
|
||||
|
||||
public function createBox(string $box) : bool
|
||||
{
|
||||
return \imap_createmailbox($this->mailbox, \imap_utf7_encode($box));
|
||||
}
|
||||
|
||||
public function countMail(string $box) : int
|
||||
{
|
||||
if ($this->box !== $box) {
|
||||
\imap_reopen($this->box, $box);
|
||||
$this->box = $box;
|
||||
}
|
||||
|
||||
return \imap_num_msg($this->mailbox);
|
||||
}
|
||||
|
||||
public function getMailboxInfo(string $box) : object
|
||||
{
|
||||
if ($this->box !== $box) {
|
||||
\imap_reopen($this->box, $box);
|
||||
$this->box = $box;
|
||||
}
|
||||
|
||||
return \imap_status($this->mailbox);
|
||||
}
|
||||
|
||||
public function getRecentCount(string $box) : int
|
||||
{
|
||||
if ($this->box !== $box) {
|
||||
\imap_reopen($this->box, $box);
|
||||
$this->box = $box;
|
||||
}
|
||||
|
||||
return \imap_num_recent($this->mailbox);
|
||||
}
|
||||
|
||||
public function copyMail(string|array $messages, string $box) : bool
|
||||
{
|
||||
return \imap_mail_copy($this->mailbox, !\is_string($messages) ? \implode(',', $messages) : $messages, $box);
|
||||
}
|
||||
|
||||
public function moveMail(string|array $messages, string $box) : bool
|
||||
{
|
||||
return \imap_mail_copy($this->mailbox, !\is_string($messages) ? \implode(',', $messages) : $messages, $box);
|
||||
}
|
||||
|
||||
public function deleteMail(int $msg) : bool
|
||||
{
|
||||
return \imap_delete($this->mailbox, $msg);
|
||||
}
|
||||
|
||||
public function getHeaders(string $box) : array
|
||||
{
|
||||
if ($this->box !== $box) {
|
||||
\imap_reopen($this->box, $box);
|
||||
$this->box = $box;
|
||||
}
|
||||
|
||||
return \imap_headers($this->mailbox);
|
||||
}
|
||||
|
||||
public function getHeaderInfo(int $msg) : object
|
||||
{
|
||||
return \imap_headerinfo($this->mailbox, $msg);
|
||||
}
|
||||
|
||||
public function getMail(int $msg) : Email
|
||||
{
|
||||
return new Email();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close mailbox
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function inboxClose() : void
|
||||
{
|
||||
if ($this->mailbox !== null) {
|
||||
\imap_close($this->mailbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
29
Message/Mail/MailBoxInterface.php
Normal file
29
Message/Mail/MailBoxInterface.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package phpOMS\Uri
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Message\Mail;
|
||||
|
||||
/**
|
||||
* Message interface.
|
||||
*
|
||||
* @property string $subject Subject
|
||||
*
|
||||
* @package phpOMS\Uri
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*/
|
||||
interface MailBoxInterface
|
||||
{
|
||||
}
|
||||
615
Message/Mail/MailHandler.php
Normal file
615
Message/Mail/MailHandler.php
Normal file
|
|
@ -0,0 +1,615 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license GLGPL 2.1 License
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*
|
||||
* Extended based on:
|
||||
* GLGPL 2.1 License
|
||||
* © 2012 - 2015 Marcus Bointon, 2010 - 2012 Jim Jagielski, 2004 - 2009 Andy Prevost
|
||||
* © PHPMailer
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Message\Mail;
|
||||
|
||||
use phpOMS\Validation\Network\Email as EmailValidator;
|
||||
use phpOMS\Utils\StringUtils;
|
||||
use phpOMS\Validation\Network\Hostname;
|
||||
use phpOMS\System\SystemUtils;
|
||||
|
||||
/**
|
||||
* Mail class.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license GLGPL 2.1 License
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class MailHandler
|
||||
{
|
||||
/**
|
||||
* The maximum line length allowed by RFC 2822 section 2.1.1.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.0.0
|
||||
*/
|
||||
const MAX_LINE_LENGTH = 998;
|
||||
|
||||
/**
|
||||
* Mailer for sending message
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $mailer = SubmitType::MAIL;
|
||||
|
||||
/**
|
||||
* The path to the sendmail program.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $mailerTool = '';
|
||||
|
||||
/**
|
||||
* Use sendmail MTA
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public bool $useMailOptions = true;
|
||||
|
||||
/**
|
||||
* Hostname for Message-ID and HELO string.
|
||||
*
|
||||
* If empty this is automatically generated.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $hostname = '';
|
||||
|
||||
/**
|
||||
* SMTP hosts.
|
||||
* (e.g. "smtp1.example.com:25;smtp2.example.com").
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $host = 'localhost';
|
||||
|
||||
/**
|
||||
* The default port.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public int $port = 25;
|
||||
|
||||
/**
|
||||
* The SMTP HELO/EHLO name
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $helo = '';
|
||||
|
||||
/**
|
||||
* SMTP encryption
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $encryption = EncryptionType::NONE;
|
||||
|
||||
/**
|
||||
* Use TLS automatically if the server supports it.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public bool $useAutoTLS = true;
|
||||
|
||||
/**
|
||||
* Use smtp auth
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public bool $useSMTPAuth = false;
|
||||
|
||||
/**
|
||||
* Options passed when connecting via SMTP.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public array $smtpOptions = [];
|
||||
|
||||
/**
|
||||
* SMTP username.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $username = '';
|
||||
|
||||
/**
|
||||
* SMTP password.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $password = '';
|
||||
|
||||
/**
|
||||
* SMTP auth type.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $authType = SMTPAuthType::NONE;
|
||||
|
||||
/**
|
||||
* OAuth class.
|
||||
*
|
||||
* @var OAuth
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public mixed $oauth;
|
||||
|
||||
/**
|
||||
* Server timeout
|
||||
*
|
||||
* @var int
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public int $timeout = 300;
|
||||
|
||||
/**
|
||||
* Comma separated list of DSN notifications
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public string $dsn = DsnNotificationType::NONE;
|
||||
|
||||
/**
|
||||
* Keep connection alive.
|
||||
*
|
||||
* This requires a close call.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public bool $keepAlive = false;
|
||||
|
||||
/**
|
||||
* Use VERP
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public bool $useVerp = false;
|
||||
|
||||
/**
|
||||
* An instance of the SMTP sender class.
|
||||
*
|
||||
* @var null|Smtp
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public ?Smtp $smtp = null;
|
||||
|
||||
/**
|
||||
* An instance of the SMTP sender class.
|
||||
*
|
||||
* @var resource
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public mixed $mailbox = null;
|
||||
|
||||
/**
|
||||
* SMTP RFC standard line ending
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected static string $LE = "\r\n";
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $user Username
|
||||
* @param string $pass Password
|
||||
* @param int $port Port
|
||||
* @param string $encryption Encryption type
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __construct(string $user = '', string $pass = '', int $port = 25, string $encryption = EncryptionType::NONE)
|
||||
{
|
||||
$this->username = $user;
|
||||
$this->password = $pass;
|
||||
$this->port = $port;
|
||||
$this->encryption = $encryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->smtpClose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mailer and the mailer tool
|
||||
*
|
||||
* @param string $mailer Mailer
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function setMailer(string $mailer) : void
|
||||
{
|
||||
$this->mailer = $mailer;
|
||||
|
||||
switch ($mailer) {
|
||||
case SubmitType::MAIL:
|
||||
case SubmitType::SMTP:
|
||||
return;
|
||||
case SubmitType::SENDMAIL:
|
||||
$this->mailerTool = \stripos($sendmailPath = \ini_get('sendmail_path'), 'sendmail') === false
|
||||
? '/usr/sbin/sendmail'
|
||||
: $sendmailPath;
|
||||
return;
|
||||
case SubmitType::QMAIL:
|
||||
$this->mailerTool = \stripos($sendmailPath = \ini_get('sendmail_path'), 'qmail') === false
|
||||
? '/var/qmail/bin/qmail-inject'
|
||||
: $sendmailPath;
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail
|
||||
*
|
||||
* @param $mail Mail
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function send(Email $mail) : bool
|
||||
{
|
||||
if (!$mail->preSend($this->mailer)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->postSend($mail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the mail
|
||||
*
|
||||
* @param Email $mail Mail
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function postSend(Email $mail) : bool
|
||||
{
|
||||
switch ($this->mailer) {
|
||||
case SubmitType::SENDMAIL:
|
||||
case SubmitType::QMAIL:
|
||||
return $this->sendmailSend($mail);
|
||||
case SubmitType::SMTP:
|
||||
return $this->smtpSend($mail);
|
||||
case SubmitType::MAIL:
|
||||
return $this->mailSend($mail);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail
|
||||
*
|
||||
* @param Email $mail Mail
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function sendmailSend(Email $mail) : bool
|
||||
{
|
||||
$header = \rtrim($mail->headerMime, " \r\n\t") . self::$LE . self::$LE;
|
||||
|
||||
// CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
|
||||
if (!empty($mail->sender) && StringUtils::isShellSafe($mail->sender)) {
|
||||
$mailerToolFmt = $this->mailer === SubmitType::QMAIL
|
||||
? '%s -f%s'
|
||||
: '%s -oi -f%s -t';
|
||||
} elseif ($this->mailer === SubmitType::QMAIL) {
|
||||
$mailerToolFmt = '%s';
|
||||
} else {
|
||||
$mailerToolFmt = '%s -oi -t';
|
||||
}
|
||||
|
||||
$mailerTool = \sprintf($mailerToolFmt, \escapeshellcmd($this->sendmail), $mail->sender);
|
||||
|
||||
$con = \popen($mailerTool, 'w');
|
||||
if ($con === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
\fwrite($con, $header);
|
||||
\fwrite($con, $mail->bodyMime);
|
||||
|
||||
$result = \pclose($con);
|
||||
if ($result !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail
|
||||
*
|
||||
* @param Email $mail Mail
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function mailSend(Email $mail) : bool
|
||||
{
|
||||
$header = \rtrim($mail->headerMime, " \r\n\t") . self::$LE . self::$LE;
|
||||
|
||||
$toArr = [];
|
||||
foreach ($mail->to as $toaddr) {
|
||||
$toArr[] = $mail->addrFormat($toaddr);
|
||||
}
|
||||
|
||||
$to = \implode(', ', $toArr);
|
||||
|
||||
//This sets the SMTP envelope sender which gets turned into a return-path header by the receiver
|
||||
// CVE-2016-10033, CVE-2016-10045: Don't pass -f if characters will be escaped.
|
||||
$params = null;
|
||||
if (!empty($mail->sender) && EmailValidator::isValid($mail->sender) && StringUtils::isShellSafe($mail->sender)) {
|
||||
$params = \sprintf('-f%s', $mail->sender);
|
||||
}
|
||||
|
||||
if (!empty($mail->sender) && EmailValidator::isValid($mail->sender)) {
|
||||
$oldFrom = \ini_get('sendmail_from');
|
||||
\ini_set('sendmail_from', $mail->sender);
|
||||
}
|
||||
|
||||
$result = false;
|
||||
$result = $this->mailPassthru($to, $mail, $header, $params);
|
||||
|
||||
if (isset($oldFrom)) {
|
||||
\ini_set('sendmail_from', $oldFrom);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call mail() in a safe_mode-aware fashion.
|
||||
*
|
||||
* @param string $to To
|
||||
* @param Email $mail Mail
|
||||
* @param string $body Message Body
|
||||
* @param string $header Additional Header(s)
|
||||
* @param null|string $params Params
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
private function mailPassthru(string $to, Email $mail, string $header, string $params = null) : bool
|
||||
{
|
||||
$subject = $mail->encodeHeader(\trim(\str_replace(["\r", "\n"], '', $mail->subject)));
|
||||
|
||||
return !$this->useMailOptions || $params === null
|
||||
? \mail($to, $subject, $mail->body, $header)
|
||||
: \mail($to, $subject, $mail->body, $header, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send mail
|
||||
*
|
||||
* @param Email $mail Mail
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function smtpSend(Email $mail) : bool
|
||||
{
|
||||
$header = \rtrim($mail->headerMime, " \r\n\t") . self::$LE . self::$LE;
|
||||
|
||||
if (!$this->smtpConnect($this->smtpOptions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$smtpFrom = $mail->sender === '' ? $mail->from : $mail->sender;
|
||||
|
||||
if (!$this->smtp->mail($smtpFrom)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$badRcpt = [];
|
||||
foreach ([$mail->to, $mail->cc, $mail->bcc] as $togroup) {
|
||||
foreach ($togroup as $to) {
|
||||
$badRcpt = $this->smtp->recipient($to[0], $this->dsn) ? $badRcpt + 1 : $badRcpt;
|
||||
}
|
||||
}
|
||||
|
||||
// Only send the DATA command if we have viable recipients
|
||||
if ((\count($this->to) + \count($this->cc) + \count($this->bcc) > $badRcpt)
|
||||
&& !$this->smtp->data($header . $mail->body, self::MAX_LINE_LENGTH)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//$transactinoId = $this->smtp->getLastTransactionId();
|
||||
|
||||
if ($this->keepAlive) {
|
||||
$this->smtp->reset();
|
||||
} else {
|
||||
$this->smtp->quit();
|
||||
$this->smtp->close();
|
||||
}
|
||||
|
||||
if (!empty($badRcpt)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a connection to an SMTP server.
|
||||
*
|
||||
* @param array $options An array of options compatible with stream_context_create()
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function smtpConnect(array $options = null) : bool
|
||||
{
|
||||
if ($this->smtp === null) {
|
||||
$this->smtp = new Smtp();
|
||||
}
|
||||
|
||||
if ($this->smtp->isConnected()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($options === null) {
|
||||
$options = $this->smtpOptions;
|
||||
}
|
||||
|
||||
$this->smtp->timeout = $this->timeout;
|
||||
$this->smtp->doVerp = $this->useVerp;
|
||||
|
||||
$hosts = \explode(';', $this->host);
|
||||
foreach ($hosts as $hostentry) {
|
||||
$hostinfo = [];
|
||||
if (!\preg_match(
|
||||
'/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/',
|
||||
\trim($hostentry),
|
||||
$hostinfo
|
||||
)
|
||||
) {
|
||||
// Not a valid host entry
|
||||
continue;
|
||||
}
|
||||
|
||||
// $hostinfo[1]: optional ssl or tls prefix
|
||||
// $hostinfo[2]: the hostname
|
||||
// $hostinfo[3]: optional port number
|
||||
|
||||
//Check the host name is a valid name or IP address
|
||||
if (!Hostname::isValid($hostinfo[2])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$prefix = '';
|
||||
$secure = $this->encryption;
|
||||
$tls = ($this->encryption === EncryptionType::TLS);
|
||||
|
||||
if ($hostinfo[1] === 'ssl' || ($hostinfo[1] === '' && $this->encryption === EncryptionType::SMTPS)) {
|
||||
$prefix = 'ssl://';
|
||||
$tls = false;
|
||||
$secure = EncryptionType::SMTPS;
|
||||
} elseif ('tls' === $hostinfo[1]) {
|
||||
$tls = true;
|
||||
$secure = EncryptionType::TLS;
|
||||
}
|
||||
|
||||
//Do we need the OpenSSL extension?
|
||||
$sslExt = defined('OPENSSL_ALGO_SHA256');
|
||||
if (($secure === EncryptionType::TLS || $secure === EncryptionType::SMTPS)
|
||||
&& !$sslExt
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$host = $hostinfo[2];
|
||||
$port = $this->port;
|
||||
|
||||
if (isset($hostinfo[3])
|
||||
&& \is_numeric($hostinfo[3])
|
||||
&& $hostinfo[3] > 0 && $hostinfo[3] < 65536
|
||||
) {
|
||||
$port = (int) $hostinfo[3];
|
||||
}
|
||||
|
||||
if ($this->smtp->connect($prefix . $host, $port, $this->timeout, $options)) {
|
||||
$hello = !empty($this->helo) ? $this->helo : SystemUtils::getHostname();
|
||||
|
||||
$this->smtp->hello($hello);
|
||||
$tls = $this->useAutoTLS && $sslExt && 'ssl' !== $secure && $this->smtp->getServerExt('STARTTLS')
|
||||
? true : $tls;
|
||||
|
||||
$this->smtp->hello($hello);
|
||||
|
||||
//Automatically enable TLS encryption
|
||||
$tls = $this->useAutoTLS && $sslExt && $secure !== EncryptionType::SMTPS && $this->smtp->getServerExt('STARTTLS')
|
||||
? true : $tls;
|
||||
|
||||
if ($tls) {
|
||||
if (!$this->smtp->startTLS()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Resend EHLO
|
||||
$this->smtp->hello($hello);
|
||||
}
|
||||
|
||||
return !($this->useSMTPAuth
|
||||
&& !$this->smtp->authenticate($this->username, $this->password, $this->authType, $this->oauth )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, all connection attempts have failed
|
||||
$this->smtp->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close SMTP
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function smtpClose() : void
|
||||
{
|
||||
if ($this->smtp !== null && $this->smtp->isConnected()) {
|
||||
$this->smtp->quit();
|
||||
$this->smtp->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Message/Mail/MessageInterface.php
Normal file
40
Message/Mail/MessageInterface.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package phpOMS\Uri
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Message\Mail;
|
||||
|
||||
/**
|
||||
* Message interface.
|
||||
*
|
||||
* @property string $subject Subject
|
||||
*
|
||||
* @package phpOMS\Uri
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*/
|
||||
interface MessageInterface
|
||||
{
|
||||
public function preSend(string $mailer) : bool;
|
||||
|
||||
public function addTo(string $address, string $name = '') : bool;
|
||||
|
||||
public function addCC(string $address, string $name = '') : bool;
|
||||
|
||||
public function addBCC(string $address, string $name = '') : bool;
|
||||
|
||||
public function addReplyTo(string $address, string $name = '') : bool;
|
||||
|
||||
public function setFrom(string $address, string $name = '') : bool;
|
||||
}
|
||||
|
|
@ -22,29 +22,150 @@ namespace phpOMS\Message\Mail;
|
|||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Pop3 extends EmailAbstract
|
||||
class Pop3 extends MailHandler implements MailBoxInterface
|
||||
{
|
||||
/**
|
||||
* Connect to server
|
||||
* Current mailbox
|
||||
*
|
||||
* @param string $user Username
|
||||
* @param string $pass Password
|
||||
* @var string
|
||||
* @sicne 1.0.0
|
||||
*/
|
||||
private string $box = '';
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->inboxClose();
|
||||
parent::__destruct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function connect(string $user = '', string $pass = '') : bool
|
||||
public function connectInbox() : bool
|
||||
{
|
||||
$this->mailbox = '{' . $this->host . ':' . $this->port . '/pop3}';
|
||||
$this->mailbox = ($tmp = \imap_open(
|
||||
'{'
|
||||
. $this->host . ':' . $this->port . '/pop3'
|
||||
. ($this->encryption !== EncryptionType::NONE ? '/ssl' : '')
|
||||
. '}',
|
||||
$this->username, $this->password
|
||||
) === false) ? null : $tmp;
|
||||
}
|
||||
|
||||
return parent::connect();
|
||||
public function getBoxes() : array
|
||||
{
|
||||
$list = \imap_list($this->mailbox, '{' . $this->host . ':' . $this->port . '}');
|
||||
if (!\is_array($list)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($list as $key => $value) {
|
||||
$list[$key] = \imap_utf7_decode($value);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function renameBox(string $old, string $new) : bool
|
||||
{
|
||||
return \imap_renamemailbox($this->mailbox, $old, \imap_utf7_encode($new));
|
||||
}
|
||||
|
||||
public function deleteBox(string $box) : bool
|
||||
{
|
||||
return \imap_deletemailbox($this->mailbox, $box);
|
||||
}
|
||||
|
||||
public function createBox(string $box) : bool
|
||||
{
|
||||
return \imap_createmailbox($this->mailbox, \imap_utf7_encode($box));
|
||||
}
|
||||
|
||||
public function countMail(string $box) : int
|
||||
{
|
||||
if ($this->box !== $box) {
|
||||
\imap_reopen($this->box, $box);
|
||||
$this->box = $box;
|
||||
}
|
||||
|
||||
return \imap_num_msg($this->mailbox);
|
||||
}
|
||||
|
||||
public function getMailboxInfo(string $box) : object
|
||||
{
|
||||
if ($this->box !== $box) {
|
||||
\imap_reopen($this->box, $box);
|
||||
$this->box = $box;
|
||||
}
|
||||
|
||||
return \imap_status($this->mailbox);
|
||||
}
|
||||
|
||||
public function getRecentCount(string $box) : int
|
||||
{
|
||||
if ($this->box !== $box) {
|
||||
\imap_reopen($this->box, $box);
|
||||
$this->box = $box;
|
||||
}
|
||||
|
||||
return \imap_num_recent($this->mailbox);
|
||||
}
|
||||
|
||||
public function copyMail(string|array $messages, string $box) : bool
|
||||
{
|
||||
return \imap_mail_copy($this->mailbox, !\is_string($messages) ? \implode(',', $messages) : $messages, $box);
|
||||
}
|
||||
|
||||
public function moveMail(string|array $messages, string $box) : bool
|
||||
{
|
||||
return \imap_mail_copy($this->mailbox, !\is_string($messages) ? \implode(',', $messages) : $messages, $box);
|
||||
}
|
||||
|
||||
public function deleteMail(int $msg) : bool
|
||||
{
|
||||
return \imap_delete($this->mailbox, $msg);
|
||||
}
|
||||
|
||||
public function getHeaders(string $box) : array
|
||||
{
|
||||
if ($this->box !== $box) {
|
||||
\imap_reopen($this->box, $box);
|
||||
$this->box = $box;
|
||||
}
|
||||
|
||||
return \imap_headers($this->mailbox);
|
||||
}
|
||||
|
||||
public function getHeaderInfo(int $msg) : object
|
||||
{
|
||||
return \imap_headerinfo($this->mailbox, $msg);
|
||||
}
|
||||
|
||||
public function getMail(int $msg) : Email
|
||||
{
|
||||
return new Email();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* Close mailbox
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function send() : bool
|
||||
public function inboxClose() : void
|
||||
{
|
||||
if ($this->mailbox !== null) {
|
||||
\imap_close($this->mailbox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
38
Message/Mail/SMTPAuthType.php
Normal file
38
Message/Mail/SMTPAuthType.php
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Message\Mail;
|
||||
|
||||
use phpOMS\Stdlib\Base\Enum;
|
||||
|
||||
/**
|
||||
* SMTP auth types enum.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract class SMTPAuthType extends Enum
|
||||
{
|
||||
public const NONE = '';
|
||||
|
||||
public const CRAM = 'CRAM-MD5';
|
||||
|
||||
public const LOGIN = 'LOGIN';
|
||||
|
||||
public const PLAIN = 'PLAIN';
|
||||
|
||||
public const XOAUTH2 = 'XOAUTH2';
|
||||
}
|
||||
905
Message/Mail/Smtp.php
Normal file
905
Message/Mail/Smtp.php
Normal file
|
|
@ -0,0 +1,905 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license GLGPL 2.1 License
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*
|
||||
* Extended based on:
|
||||
* GLGPL 2.1 License
|
||||
* © 2012 - 2015 Marcus Bointon, 2010 - 2012 Jim Jagielski, 2004 - 2009 Andy Prevost
|
||||
* © PHPMailer
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Message\Mail;
|
||||
|
||||
/**
|
||||
* Smtp mail class.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license GLGPL 2.1 License
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class Smtp
|
||||
{
|
||||
/**
|
||||
* The maximum line length allowed
|
||||
*
|
||||
* @var int
|
||||
* @since 1.0.0
|
||||
*/
|
||||
const MAX_REPLY_LENGTH = 512;
|
||||
|
||||
/**
|
||||
* SMTP RFC standard line ending
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected static string $LE = "\r\n";
|
||||
|
||||
/**
|
||||
* Whether to use VERP.
|
||||
*
|
||||
* @var bool
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public bool $doVerp = false;
|
||||
|
||||
/**
|
||||
* The timeout value for connection, in seconds.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public int $timeout = 300;
|
||||
|
||||
/**
|
||||
* How long to wait for commands to complete, in seconds.
|
||||
*
|
||||
* @var int
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public int $timeLimit = 300;
|
||||
|
||||
/**
|
||||
* The last transaction ID issued in response to a DATA command,
|
||||
* if one was detected.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected string $lastSmtpTransactionId = '';
|
||||
|
||||
/**
|
||||
* The socket for the server connection.
|
||||
*
|
||||
* @var ?resource
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected $con;
|
||||
|
||||
/**
|
||||
* The reply the server sent to us for HELO.
|
||||
* If empty no HELO string has yet been received.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected string $heloRply = '';
|
||||
|
||||
/**
|
||||
* The set of SMTP extensions sent in reply to EHLO command.
|
||||
*
|
||||
* @var array
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected array $serverCaps = [];
|
||||
|
||||
/**
|
||||
* The most recent reply received from the server.
|
||||
*
|
||||
* @var string
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected string $lastReply = '';
|
||||
|
||||
/**
|
||||
* Connect to an SMTP server.
|
||||
*
|
||||
* @param string $host SMTP server IP or host name
|
||||
* @param int $port The port number to connect to
|
||||
* @param int $timeout How long to wait for the connection to open
|
||||
* @param array $options An array of options for stream_context_create()
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function connect(string $host, int $port = 25, int $timeout = 30, array $options = []) : bool
|
||||
{
|
||||
if ($this->isConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->con = $this->getSMTPConnection($host, $port, $timeout, $options);
|
||||
if ($this->con === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->lastReply = $this->getLines();
|
||||
$responseCode = (int) \substr($this->lastReply, 0, 3);
|
||||
if ($responseCode === 220) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($responseCode === 554) {
|
||||
$this->quit();
|
||||
}
|
||||
|
||||
$this->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create connection to the SMTP server.
|
||||
*
|
||||
* @param string $host SMTP server IP or host name
|
||||
* @param int $port The port number to connect to
|
||||
* @param int $timeout How long to wait for the connection to open
|
||||
* @param array $options An array of options for stream_context_create()
|
||||
*
|
||||
* @return null|resource
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function getSMTPConnection(string $host, int $port = 25, int $timeout = 30, array $options = []) : mixed
|
||||
{
|
||||
static $streamok;
|
||||
if ($streamok === null) {
|
||||
$streamok = \function_exists('stream_socket_client');
|
||||
}
|
||||
|
||||
$errno = 0;
|
||||
$errstr = '';
|
||||
|
||||
if ($streamok) {
|
||||
$socketContext = \stream_context_create($options);
|
||||
$connection = \stream_socket_client($host . ':' . $port, $errno, $errstr, $timeout, \STREAM_CLIENT_CONNECT, $socketContext);
|
||||
} else {
|
||||
//Fall back to fsockopen which should work in more places, but is missing some features
|
||||
$connection = \fsockopen($host, $port, $errno, $errstr, $timeout);
|
||||
}
|
||||
|
||||
if (!\is_resource($connection)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// SMTP server can take longer to respond, give longer timeout for first read
|
||||
// Windows does not have support for this timeout function
|
||||
if (\strpos(\PHP_OS, 'WIN') !== 0) {
|
||||
$max = (int) \ini_get('max_execution_time');
|
||||
if ($max !== 0 && $timeout > $max && \strpos(\ini_get('disable_functions'), 'set_time_limit') === false) {
|
||||
\set_time_limit($timeout);
|
||||
}
|
||||
|
||||
\stream_set_timeout($connection, $timeout, 0);
|
||||
}
|
||||
|
||||
return $connection === false ? null : $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a TLS (encrypted) session.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function startTLS() : bool
|
||||
{
|
||||
if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$crypto_method = \STREAM_CRYPTO_METHOD_TLS_CLIENT;
|
||||
if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
|
||||
$crypto_method |= \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
|
||||
$crypto_method |= \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
|
||||
}
|
||||
|
||||
return (bool) \stream_socket_enable_crypto($this->con, true, $crypto_method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform SMTP authentication.
|
||||
* Must be run after hello().
|
||||
*
|
||||
* @param string $username The user name
|
||||
* @param string $password The password
|
||||
* @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2)
|
||||
* @param OAuth $oauth An optional OAuth instance for XOAUTH2 authentication
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function authenticate(
|
||||
string $username,
|
||||
string $password,
|
||||
string $authtype = '',
|
||||
mixed $oauth = null
|
||||
) : bool {
|
||||
if (empty($this->serverCaps)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isset($this->serverCaps['EHLO'])) {
|
||||
// SMTP extensions are available; try to find a proper authentication method
|
||||
if (!isset($this->serverCaps['AUTH'])) {
|
||||
// 'at this stage' means that auth may be allowed after the stage changes
|
||||
// e.g. after STARTTLS
|
||||
return false;
|
||||
}
|
||||
|
||||
//If we have requested a specific auth type, check the server supports it before trying others
|
||||
if ($authtype !== '' && !\in_array($authtype, $this->serverCaps['AUTH'], true)) {
|
||||
$authtype = '';
|
||||
}
|
||||
|
||||
if ($authtype !== '') {
|
||||
//If no auth mechanism is specified, attempt to use these, in this order
|
||||
//Try CRAM-MD5 first as it's more secure than the others
|
||||
foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) {
|
||||
if (\in_array($method, $this->serverCaps['AUTH'], true)) {
|
||||
$authtype = $method;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($authtype === '') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!\in_array($authtype, $this->serverCaps['AUTH'], true)) {
|
||||
return false;
|
||||
}
|
||||
} elseif ($authtype === '') {
|
||||
$authtype = 'LOGIN';
|
||||
}
|
||||
|
||||
switch ($authtype) {
|
||||
case 'PLAIN':
|
||||
// Start authentication
|
||||
if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)
|
||||
|| !$this->sendCommand('User & Password',
|
||||
\base64_encode("\0" . $username . "\0" . $password),
|
||||
235
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'LOGIN':
|
||||
// Start authentication
|
||||
if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)
|
||||
|| !$this->sendCommand('Username', \base64_encode($username), 334)
|
||||
|| !$this->sendCommand('Password', \base64_encode($password), 235)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'CRAM-MD5':
|
||||
// Start authentication
|
||||
if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$challenge = \base64_decode(\substr($this->lastReply, 4));
|
||||
$response = $username . ' ' . $this->hmac($challenge, $password);
|
||||
|
||||
// send encoded credentials
|
||||
return $this->sendCommand('Username', \base64_encode($response), 235);
|
||||
case 'XOAUTH2':
|
||||
//The OAuth instance must be set up prior to requesting auth.
|
||||
if ($OAuth === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$oauth = $OAuth->getOauth64();
|
||||
if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate an MD5 HMAC hash.
|
||||
*
|
||||
* @param string $data The data to hash
|
||||
* @param string $key The key to hash with
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function hmac(string $data, string $key) : string
|
||||
{
|
||||
// RFC 2104 HMAC implementation for php.
|
||||
// Creates an md5 HMAC.
|
||||
// by Lance Rushing
|
||||
$byteLen = 64;
|
||||
if (strlen($key) > $byteLen) {
|
||||
$key = \pack('H*', \md5($key));
|
||||
}
|
||||
|
||||
$key = \str_pad($key, $byteLen, \chr(0x00));
|
||||
$ipad = \str_pad('', $byteLen, \chr(0x36));
|
||||
$opad = \str_pad('', $byteLen, \chr(0x5c));
|
||||
$k_ipad = $key ^ $ipad;
|
||||
$k_opad = $key ^ $opad;
|
||||
|
||||
return \md5($k_opad . \pack('H*', \md5($k_ipad . $data)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check connection state.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function isConnected() : bool
|
||||
{
|
||||
if (!\is_resource($this->con)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$status = \stream_get_meta_data($this->con);
|
||||
if ($status['eof']) {
|
||||
$this->close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the socket and clean up the state of the class.
|
||||
* Try to QUIT first!
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function close() : void
|
||||
{
|
||||
$this->serverCaps = [];
|
||||
$this->heloRply = '';
|
||||
|
||||
if (\is_resource($this->con)) {
|
||||
fclose($this->con);
|
||||
$this->con = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMTP DATA command.
|
||||
*
|
||||
* @param string $msg_data Message data to send
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function data($msg_data, int $maxLineLength = 998) : bool
|
||||
{
|
||||
if (!$this->sendCommand('DATA', 'DATA', 354)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The server is ready to accept data!
|
||||
* According to rfc821 we should not send more than 1000 characters on a single line (including the LE)
|
||||
*/
|
||||
$lines = \explode("\n", \str_replace(["\r\n", "\r"], "\n", $msg_data));
|
||||
|
||||
/* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
|
||||
* of the first line (':' separated) does not contain a space then it _should_ be a header and we will
|
||||
* process all lines before a blank line as headers.
|
||||
*/
|
||||
$field = \substr($lines[0], 0, \strpos($lines[0], ':'));
|
||||
$inHeaders = (!empty($field) && \strpos($field, ' ') === false);
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$linesOut = [];
|
||||
if ($inHeaders && $line === '') {
|
||||
$inHeaders = false;
|
||||
}
|
||||
|
||||
while (isset($line[$maxLineLength])) {
|
||||
$pos = \strrpos(\substr($line, 0, $maxLineLength), ' ');
|
||||
if (!$pos) {
|
||||
$pos = $maxLineLength - 1;
|
||||
$linesOut[] = \substr($line, 0, $pos);
|
||||
$line = \substr($line, $pos);
|
||||
} else {
|
||||
$linesOut[] = \substr($line, 0, $pos);
|
||||
$line = \substr($line, $pos + 1);
|
||||
}
|
||||
|
||||
if ($inHeaders) {
|
||||
$line = "\t" . $line;
|
||||
}
|
||||
}
|
||||
|
||||
$linesOut[] = $line;
|
||||
|
||||
foreach ($linesOut as $lineOut) {
|
||||
if (!empty($lineOut) && $lineOut[0] === '.') {
|
||||
$lineOut = '.' . $lineOut;
|
||||
}
|
||||
|
||||
$this->clientSend($lineOut . self::$LE, 'DATA');
|
||||
}
|
||||
}
|
||||
|
||||
$tmpTimeLimit = $this->timeLimit;
|
||||
$this->timeLimit *= 2;
|
||||
$result = $this->sendCommand('DATA END', '.', 250);
|
||||
|
||||
$this->recordLastTransactionId();
|
||||
|
||||
$this->timeLimit = $tmpTimeLimit;
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMTP HELO or EHLO command.
|
||||
*
|
||||
* @param string $host The host name or IP to connect to
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function hello(string $host = '') : bool
|
||||
{
|
||||
if ($this->sendHello('EHLO', $host)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (\substr($this->heloRply, 0, 3) == '421') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->sendHello('HELO', $host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMTP HELO or EHLO command.
|
||||
*
|
||||
* @param string $hello The HELO string
|
||||
* @param string $host The hostname to say we are
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function sendHello(string $hello, string $host) : bool
|
||||
{
|
||||
$status = $this->sendCommand($hello, $hello . ' ' . $host, 250);
|
||||
$this->heloRply = $this->lastReply;
|
||||
|
||||
if ($status) {
|
||||
$this->parseHelloFields($hello);
|
||||
} else {
|
||||
$this->serverCaps = [];
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a reply to HELO/EHLO command to discover server extensions.
|
||||
*
|
||||
* @param string $type `HELO` or `EHLO`
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function parseHelloFields(string $type) : void
|
||||
{
|
||||
$this->serverCaps = [];
|
||||
$lines = \explode("\n", $this->heloRply);
|
||||
|
||||
foreach ($lines as $n => $s) {
|
||||
//First 4 chars contain response code followed by - or space
|
||||
$s = \trim(\substr($s, 4));
|
||||
if (empty($s)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fields = \explode(' ', $s);
|
||||
if (!empty($fields)) {
|
||||
if (!$n) {
|
||||
$name = $type;
|
||||
$fields = $fields[0];
|
||||
} else {
|
||||
$name = \array_shift($fields);
|
||||
switch ($name) {
|
||||
case 'SIZE':
|
||||
$fields = ($fields ? $fields[0] : 0);
|
||||
break;
|
||||
case 'AUTH':
|
||||
if (!\is_array($fields)) {
|
||||
$fields = [];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$fields = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->serverCaps[$name] = $fields;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMTP MAIL command.
|
||||
*
|
||||
* @param string $from Source address of this message
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function mail(string $from) : bool
|
||||
{
|
||||
$useVerp = ($this->doVerp ? ' XVERP' : '');
|
||||
|
||||
return $this->sendCommand('MAIL FROM', 'MAIL FROM:<' . $from . '>' . $useVerp, 250);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMTP QUIT command.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function quit() : bool
|
||||
{
|
||||
$status = $this->sendCommand('QUIT', 'QUIT', 221);
|
||||
if ($status) {
|
||||
$this->close();
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMTP RCPT command.
|
||||
*
|
||||
* @param string $address The address the message is being sent to
|
||||
* @param string $dsn Comma separated list of DSN notifications
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function recipient(string $address, string $dsn = DsnNotificationType::NONE) : bool
|
||||
{
|
||||
if ($dsn === '') {
|
||||
$rcpt = 'RCPT TO:<' . $address . '>';
|
||||
} else {
|
||||
$notify = [];
|
||||
|
||||
if (\strpos($dsn, 'NEVER') !== false) {
|
||||
$notify[] = 'NEVER';
|
||||
} else {
|
||||
foreach (['SUCCESS', 'FAILURE', 'DELAY'] as $value) {
|
||||
if (\strpos($dsn, $value) !== false) {
|
||||
$notify[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rcpt = 'RCPT TO:<' . $address . '> NOTIFY=' . \implode(',', $notify);
|
||||
}
|
||||
|
||||
return $this->sendCommand('RCPT TO', $rcpt, [250, 251]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMTP RSET command.
|
||||
*
|
||||
* @return bool s
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function reset() : bool
|
||||
{
|
||||
return $this->sendCommand('RSET', 'RSET', 250);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a command to an SMTP server and check its return code.
|
||||
*
|
||||
* @param string $command The command name - not sent to the server
|
||||
* @param string $commandstring The actual command to send
|
||||
* @param int|array $expect One or more expected integer success codes
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function sendCommand(string $command, string $commandstring, int|array $expect) : bool
|
||||
{
|
||||
if (!$this->isConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((\strpos($commandstring, "\n") !== false)
|
||||
|| (\strpos($commandstring, "\r") !== false)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->clientSend($commandstring . self::$LE, $command);
|
||||
|
||||
$this->lastReply = $this->getLines();
|
||||
|
||||
$matches = [];
|
||||
if (\preg_match('/^([\d]{3})[ -](?:([\d]\\.[\d]\\.[\d]{1,2}) )?/', $this->lastReply, $matches)) {
|
||||
$code = (int) $matches[1];
|
||||
$codeEx = \count($matches) > 2 ? $matches[2] : null;
|
||||
|
||||
// Cut off error code from each response line
|
||||
$detail = \preg_replace(
|
||||
"/{$code}[ -]" .
|
||||
($codeEx ? \str_replace('.', '\\.', $codeEx) . ' ' : '') . '/m',
|
||||
'',
|
||||
$this->lastReply
|
||||
);
|
||||
} else {
|
||||
// Fall back to simple parsing if regex fails
|
||||
$code = (int) \substr($this->lastReply, 0, 3);
|
||||
$detail = \substr($this->lastReply, 4);
|
||||
}
|
||||
|
||||
if (!\in_array($code, (array) $expect, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMTP SAML command.
|
||||
* Starts a mail transaction from the email address specified in $from.
|
||||
*
|
||||
* @param string $from The address the message is from
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function sendAndMail(string $from) : bool
|
||||
{
|
||||
return $this->sendCommand('SAML', "SAML FROM:$from", 250);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMTP VRFY command.
|
||||
*
|
||||
* @param string $name The name to verify
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function verify(string $name) : bool
|
||||
{
|
||||
return $this->sendCommand('VRFY', "VRFY $name", [250, 251]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an SMTP NOOP command.
|
||||
* Used to keep keep-alives alive.
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function noop() : bool
|
||||
{
|
||||
return $this->sendCommand('NOOP', 'NOOP', 250);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send raw data to the server.
|
||||
*
|
||||
* @param string $data The data to send
|
||||
* @param string $command Optionally, the command this is part of, used only for controlling debug output
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function clientSend(string $data, string $command = '') : int
|
||||
{
|
||||
$result = \fwrite($this->con, $data);
|
||||
|
||||
return $result === false ? -1 : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get SMTP extensions available on the server.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getServerExtList() : array
|
||||
{
|
||||
return $this->serverCaps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get metadata about the SMTP server from its HELO/EHLO response.
|
||||
*
|
||||
* @param string $name Name of SMTP extension
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getServerExt(string $name) : bool
|
||||
{
|
||||
if (empty($this->serverCaps)) {
|
||||
// HELO/EHLO has not been sent
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset($this->serverCaps[$name])) {
|
||||
if ($name === 'HELO') {
|
||||
// Server name
|
||||
//return $this->serverCaps['EHLO'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return isset($this->serverCaps[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last reply from the server.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getLastReply() : string
|
||||
{
|
||||
return $this->lastReply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the SMTP server's response.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function getLines() : string
|
||||
{
|
||||
if (!\is_resource($this->con)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$data = '';
|
||||
$endTime = 0;
|
||||
|
||||
\stream_set_timeout($this->con, $this->timeout);
|
||||
if ($this->timeLimit > 0) {
|
||||
$endTime = \time() + $this->timeLimit;
|
||||
}
|
||||
|
||||
$selR = [$this->con];
|
||||
$selW = null;
|
||||
$tries = 0;
|
||||
|
||||
while (\is_resource($this->con) && !\feof($this->con)) {
|
||||
$n = \stream_select($selR, $selW, $selW, $this->timeLimit);
|
||||
if ($n === false) {
|
||||
if ($tries < 3) {
|
||||
++$tries;
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$str = \fgets($this->con, self::MAX_REPLY_LENGTH);
|
||||
$data .= $str;
|
||||
|
||||
// If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
|
||||
// or 4th character is a space or a line break char, we are done reading, break the loop.
|
||||
// String array access is a significant micro-optimisation over strlen
|
||||
if (!isset($str[3]) || $str[3] === ' ' || $str[3] === "\r" || $str[3] === "\n") {
|
||||
break;
|
||||
}
|
||||
|
||||
$info = \stream_get_meta_data($this->con);
|
||||
if ($info['timed_out']) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Now check if reads took too long
|
||||
if ($endTime && \time() > $endTime) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and return the ID of the last SMTP transaction
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
protected function recordLastTransactionId() : string
|
||||
{
|
||||
$reply = $this->getLastReply();
|
||||
|
||||
if ($reply === '') {
|
||||
$this->lastSmtpTransactionId = '';
|
||||
} else {
|
||||
$this->lastSmtpTransactionId = '';
|
||||
$patterns = SmtpTransactionPattern::getConstants();
|
||||
|
||||
foreach ($patterns as $pattern) {
|
||||
$matches = [];
|
||||
if (\preg_match($pattern, $reply, $matches)) {
|
||||
$this->lastSmtpTransactionId = \trim($matches[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->lastSmtpTransactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the queue/transaction ID of the last SMTP transaction
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getLastTransactionId() : string
|
||||
{
|
||||
return $this->lastSmtpTransactionId;
|
||||
}
|
||||
}
|
||||
42
Message/Mail/SmtpTransactionPattern.php
Normal file
42
Message/Mail/SmtpTransactionPattern.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\Message\Mail;
|
||||
|
||||
use phpOMS\Stdlib\Base\Enum;
|
||||
|
||||
/**
|
||||
* Transaction types enum.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract class SmtpTransactionPattern extends Enum
|
||||
{
|
||||
public const EXIM = '/[\d]{3} OK id=(.*)/';
|
||||
|
||||
public const SENDMAIL = '/[\d]{3} 2.0.0 (.*) Message/';
|
||||
|
||||
public const POSTFIX = '/[\d]{3} 2.0.0 Ok: queued as (.*)/';
|
||||
|
||||
public const MICROSOFT_ESMTP = '/[0-9]{3} 2.[\d].0 (.*)@(?:.*) Queued mail for delivery/';
|
||||
|
||||
public const AMAZON_SES = '/[\d]{3} Ok (.*)/';
|
||||
|
||||
public const SENDGRID = '/[\d]{3} Ok: queued as (.*)/';
|
||||
|
||||
public const CAMPAIGNMONITOR = '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/';
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ use phpOMS\Stdlib\Base\Enum;
|
|||
/**
|
||||
* Submit enum.
|
||||
*
|
||||
* @package phpOMS\Message\Mail
|
||||
* @package phpOMS\Message\Mail
|
||||
* @license OMS License 1.0
|
||||
* @link https://orange-management.org
|
||||
* @since 1.0.0
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ namespace phpOMS\Message;
|
|||
interface MessageInterface
|
||||
{
|
||||
/**
|
||||
* Gets the body of the message.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
* Gets the body of the message.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public function getBody() : string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,4 +191,85 @@ final class FileUtils
|
|||
|
||||
return \intval($perm, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-byte-safe pathinfo.
|
||||
*
|
||||
* @param string $path Path
|
||||
* @param null|int|string $options PATHINFO_* or specifier for the component
|
||||
*
|
||||
* @return string|array
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function mb_pathinfo(string $path, int|string $options = null) : string|array
|
||||
{
|
||||
$ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''];
|
||||
$pathinfo = [];
|
||||
|
||||
if (\preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^.\\\\/]+?)|))[\\\\/.]*$#m', $path, $pathinfo)) {
|
||||
if (isset($pathinfo[1])) {
|
||||
$ret['dirname'] = $pathinfo[1];
|
||||
}
|
||||
if (isset($pathinfo[2])) {
|
||||
$ret['basename'] = $pathinfo[2];
|
||||
}
|
||||
if (isset($pathinfo[5])) {
|
||||
$ret['extension'] = $pathinfo[5];
|
||||
}
|
||||
if (isset($pathinfo[3])) {
|
||||
$ret['filename'] = $pathinfo[3];
|
||||
}
|
||||
}
|
||||
|
||||
switch ($options) {
|
||||
case \PATHINFO_DIRNAME:
|
||||
case 'dirname':
|
||||
return $ret['dirname'];
|
||||
case \PATHINFO_BASENAME:
|
||||
case 'basename':
|
||||
return $ret['basename'];
|
||||
case \PATHINFO_EXTENSION:
|
||||
case 'extension':
|
||||
return $ret['extension'];
|
||||
case \PATHINFO_FILENAME:
|
||||
case 'filename':
|
||||
return $ret['filename'];
|
||||
default:
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a file path is safe, accessible, and readable.
|
||||
*
|
||||
* @param string $path A relative or absolute path
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function isAccessible(string $path) : bool
|
||||
{
|
||||
$readable = \is_file($path);
|
||||
if (\strpos($path, '\\\\') !== 0) {
|
||||
$readable = $readable && \is_readable($path);
|
||||
}
|
||||
|
||||
return self::isPermittedPath($path) && $readable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a file path is of a permitted type.
|
||||
*
|
||||
* @param string $path Path
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function isPermittedPath(string $path) : bool
|
||||
{
|
||||
return !\preg_match('#^[a-z]+://#i', $path);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2009,4 +2009,22 @@ abstract class MimeType extends Enum
|
|||
public const M_ZMM = 'application/vnd.handheld-entertainment+xml';
|
||||
|
||||
public const M_123 = 'application/vnd.lotus-1-2-3';
|
||||
|
||||
/**
|
||||
* Get mime from file extension
|
||||
*
|
||||
* @param string $extension Extension
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function extensionToMime(string $extension) : string
|
||||
{
|
||||
try {
|
||||
return self::getByName('M_' . \strtoupper($extension)) ?? 'application/octet-stream';
|
||||
} catch (\Throwable $t) {
|
||||
return 'application/octet-stream';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -126,4 +126,24 @@ final class SystemUtils
|
|||
|
||||
return (int) $cpuUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server hostname.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function getHostname() : string
|
||||
{
|
||||
if (isset($_SERVER['SERVER_NAME'])) {
|
||||
return $_SERVER['SERVER_NAME'];
|
||||
} elseif (($result = gethostname()) !== false) {
|
||||
return $result;
|
||||
} elseif (\php_uname('n') !== false) {
|
||||
return \php_uname('n');
|
||||
}
|
||||
|
||||
return 'localhost.localdomain';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -399,4 +399,34 @@ final class StringUtils
|
|||
|
||||
return (int) $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix CVE-2016-10033 and CVE-2016-10045 by disallowing potentially unsafe shell characters.
|
||||
*
|
||||
* @param string $string String to check
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public static function isShellSafe(string $string)
|
||||
{
|
||||
if (\escapeshellcmd($string) !== $string
|
||||
|| !\in_array(\escapeshellarg($string), ["'$string'", "\"$string\""])
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$length = \strlen($string);
|
||||
|
||||
for ($i = 0; $i < $length; ++$i) {
|
||||
$c = $string[$i];
|
||||
|
||||
if (!\ctype_alnum($c) && \strpos('@_-.', $c) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,21 @@ abstract class Hostname extends ValidatorAbstract
|
|||
*/
|
||||
public static function isValid(mixed $value, array $constraints = null) : bool
|
||||
{
|
||||
return \filter_var(\gethostbyname($value), \FILTER_VALIDATE_IP) !== false;
|
||||
//return \filter_var(\gethostbyname($value), \FILTER_VALIDATE_IP) !== false;
|
||||
|
||||
if (empty($value)
|
||||
|| \strlen($value) > 256
|
||||
|| !\preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+])$/', $value)
|
||||
) {
|
||||
return false;
|
||||
} elseif (\strlen($value) > 2 && \substr($value, 0, 1) === '[' && \substr($value, -1, 1) === ']') {
|
||||
return \filter_var(\substr($value, 1, -1), \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV6) !== false;
|
||||
} elseif (\is_numeric(str_replace('.', '', $value))) {
|
||||
return \filter_var($value, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4) !== false;
|
||||
} elseif (\filter_var('http://' . $value, FILTER_VALIDATE_URL) !== false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,67 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package tests
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\tests\Message\Mail;
|
||||
|
||||
use phpOMS\Message\Mail\Imap;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ImapTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/**
|
||||
* @covers phpOMS\Message\Mail\Imap
|
||||
* @group framework
|
||||
*/
|
||||
public function testDefault() : void
|
||||
{
|
||||
self::markTestIncomplete();
|
||||
return;
|
||||
$email = new Imap(
|
||||
$GLOBALS['CONFIG']['mail']['imap']['host'],
|
||||
$GLOBALS['CONFIG']['mail']['imap']['port'],
|
||||
30,
|
||||
$GLOBALS['CONFIG']['mail']['imap']['ssl']
|
||||
);
|
||||
|
||||
self::assertFalse($email->isConnected());
|
||||
self::assertTrue($email->connect(
|
||||
$GLOBALS['CONFIG']['mail']['imap']['user'],
|
||||
$GLOBALS['CONFIG']['mail']['imap']['password']
|
||||
));
|
||||
|
||||
self::assertTrue($email->isConnected());
|
||||
self::assertEquals([], $email->getBoxes());
|
||||
self::assertEquals([], $email->getQuota());
|
||||
self::assertInstanceOf('\phpOMS\Message\Mail\Mail', $email->getEmail('1'));
|
||||
self::assertEquals([], $email->getInboxAll());
|
||||
self::assertEquals([], $email->getInboxOverview());
|
||||
self::assertEquals([], $email->getInboxNew());
|
||||
self::assertEquals([], $email->getInboxFrom(''));
|
||||
self::assertEquals([], $email->getInboxTo(''));
|
||||
self::assertEquals([], $email->getInboxCc(''));
|
||||
self::assertEquals([], $email->getInboxBcc(''));
|
||||
self::assertEquals([], $email->getInboxAnswered());
|
||||
self::assertEquals([], $email->getInboxSubject(''));
|
||||
self::assertEquals([], $email->getInboxSince(new \DateTime('now')));
|
||||
self::assertEquals([], $email->getInboxUnseen());
|
||||
self::assertEquals([], $email->getInboxSeen());
|
||||
self::assertEquals([], $email->getInboxDeleted());
|
||||
self::assertEquals([], $email->getInboxText(''));
|
||||
self::assertEquals([], $email->getMessageOverview(1, 1));
|
||||
self::assertEquals(0, $email->countMessages());
|
||||
self::assertEquals('', $email->getMessageHeader(1));
|
||||
}
|
||||
}
|
||||
53
tests/Message/Mail/MailHandlerTest.php
Normal file
53
tests/Message/Mail/MailHandlerTest.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package tests
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\tests\Message;
|
||||
|
||||
require_once __DIR__ . '/../../Autoloader.php';
|
||||
|
||||
use phpOMS\Message\Mail\Mail;
|
||||
use phpOMS\Message\Mail\MailHandler;
|
||||
use phpOMS\Message\Mail\SubmitType;
|
||||
use phpOMS\Message\Mail\Email;
|
||||
use phpOMS\Message\Mail\Imap;
|
||||
|
||||
/**
|
||||
* @testdox phpOMS\tests\Message\MailHandlerTest: Abstract mail handler
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class MailHandlerTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testSendTextWithMail() : void
|
||||
{
|
||||
$mailer = new MailHandler();
|
||||
$mailer->setMailer(SubmitType::MAIL);
|
||||
|
||||
$mail = new Email();
|
||||
$mail->setFrom('d.eichhorn@orange-management.org', 'Dennis Eichhorn');
|
||||
$mail->addTo('coyle.maguire@googlemail.com', 'Donald Duck');
|
||||
$mail->subject = 'Test email';
|
||||
$mail->body = 'This is some content';
|
||||
|
||||
self::assertTrue($mailer->send($mail));
|
||||
}
|
||||
|
||||
public function testReceiveMailWithImap() : void
|
||||
{
|
||||
$mailer = new Imap();
|
||||
$mailer->connectInbox();
|
||||
|
||||
var_dump($mailer->getBoxes());
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package tests
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\tests\Message\Mail;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class MailTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testPlaceholder() : void
|
||||
{
|
||||
self::markTestIncomplete();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package tests
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\tests\Message\Mail;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class NntpTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testPlaceholder() : void
|
||||
{
|
||||
self::markTestIncomplete();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Orange Management
|
||||
*
|
||||
* PHP Version 8.0
|
||||
*
|
||||
* @package tests
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 1.0
|
||||
* @version 1.0.0
|
||||
* @link https://orange-management.org
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace phpOMS\tests\Message\Mail;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class Pop3Test extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
public function testPlaceholder() : void
|
||||
{
|
||||
self::markTestIncomplete();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user