%PDF- %PDF-
Direktori : /www/varak.net/mail2.varak.net_old/libraries/MailSo/Smtp/ |
Current File : //www/varak.net/mail2.varak.net_old/libraries/MailSo/Smtp/SmtpClient.php |
<?php /* * Copyright 2004-2014, AfterLogic Corp. * Licensed under AGPLv3 license or AfterLogic license * if commercial version of the product was purchased. * See the LICENSE file for a full license statement. */ namespace MailSo\Smtp; /** * @category MailSo * @package Smtp */ class SmtpClient extends \MailSo\Net\NetClient { /** * @var bool */ private $bHelo; /** * @var bool */ private $bRcpt; /** * @var bool */ private $bMail; /** * @var bool */ private $bData; /** * @var array */ private $aAuthTypes; /** * @var array */ private $aCapa; /** * @var int */ private $iSizeCapaValue; /** * @var int */ private $iRequestTime; /** * @var array */ private $aResults; /** * @var bool */ public $__USE_SINGLE_LINE_AUTH_PLAIN_COMMAND = false; /** * @access protected */ protected function __construct() { parent::__construct(); $this->aAuthTypes = array(); $this->iRequestTime = 0; $this->iSizeCapaValue = 0; $this->aResults = array(); $this->aCapa = array(); $this->bHelo = false; $this->bRcpt = false; $this->bMail = false; $this->bData = false; } /** * @return \MailSo\Smtp\SmtpClient */ public static function NewInstance() { return new self(); } /** * @return bool */ public function IsSupported($sCapa) { return in_array(strtoupper($sCapa), $this->aCapa); } /** * @return bool */ public function IsAuthSupported($sAuth) { return in_array(strtoupper($sAuth), $this->aAuthTypes); } /** * @return bool */ public function HasSupportedAuth() { return $this->IsAuthSupported('PLAIN') || $this->IsAuthSupported('LOGIN'); } /** * @return string */ public static function EhloHelper() { $sEhloHost = empty($_SERVER['SERVER_NAME']) ? '' : \trim($_SERVER['SERVER_NAME']); if (empty($sEhloHost)) { $sEhloHost = empty($_SERVER['HTTP_HOST']) ? '' : \trim($_SERVER['HTTP_HOST']); } if (empty($sEhloHost)) { $sEhloHost = \function_exists('gethostname') ? \gethostname() : 'localhost'; } $sEhloHost = \trim(\preg_replace('/:\d+$/', '', \trim($sEhloHost))); if (\preg_match('/^\d+\.\d+\.\d+\.\d+$/', $sEhloHost)) { $sEhloHost = '['.$sEhloHost.']'; } return empty($sEhloHost) ? 'localhost' : $sEhloHost; } /** * @param string $sServerName * @param int $iPort = 25 * @param string $sEhloHost = '[127.0.0.1]' * @param int $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT * @param bool $bVerifySsl = false * * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\ResponseException */ public function Connect($sServerName, $iPort = 25, $sEhloHost = '[127.0.0.1]', $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT, $bVerifySsl = false) { $this->iRequestTime = microtime(true); parent::Connect($sServerName, $iPort, $iSecurityType, $bVerifySsl); $this->validateResponse(220); $this->preLoginStartTLSAndEhloProcess($sEhloHost); return $this; } /** * @param string $sLogin * @param string $sPassword * * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function Login($sLogin, $sPassword) { $sLogin = \MailSo\Base\Utils::IdnToAscii($sLogin); if ($this->IsAuthSupported('LOGIN')) { try { $this->sendRequestWithCheck('AUTH', 334, 'LOGIN'); } catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) { $this->writeLogException( new \MailSo\Smtp\Exceptions\LoginBadMethodException( $oException->GetResponses(), $oException->getMessage(), 0, $oException), \MailSo\Log\Enumerations\Type::NOTICE, true); } try { $this->sendRequestWithCheck(\base64_encode($sLogin), 334, ''); $this->sendRequestWithCheck(\base64_encode($sPassword), 235, '', true); } catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) { $this->writeLogException( new \MailSo\Smtp\Exceptions\LoginBadCredentialsException( $oException->GetResponses(), $oException->getMessage(), 0, $oException), \MailSo\Log\Enumerations\Type::NOTICE, true); } } else if ($this->IsAuthSupported('PLAIN')) { if ($this->__USE_SINGLE_LINE_AUTH_PLAIN_COMMAND) { try { $this->sendRequestWithCheck('AUTH', 235, 'PLAIN '.\base64_encode("\0".$sLogin."\0".$sPassword), true); } catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) { $this->writeLogException( new \MailSo\Smtp\Exceptions\LoginBadCredentialsException( $oException->GetResponses(), $oException->getMessage(), 0, $oException), \MailSo\Log\Enumerations\Type::NOTICE, true); } } else { try { $this->sendRequestWithCheck('AUTH', 334, 'PLAIN'); } catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) { $this->writeLogException( new \MailSo\Smtp\Exceptions\LoginBadMethodException( $oException->GetResponses(), $oException->getMessage(), 0, $oException), \MailSo\Log\Enumerations\Type::NOTICE, true); } try { $this->sendRequestWithCheck(\base64_encode("\0".$sLogin."\0".$sPassword), 235, '', true); } catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) { $this->writeLogException( new \MailSo\Smtp\Exceptions\LoginBadCredentialsException( $oException->GetResponses(), $oException->getMessage(), 0, $oException), \MailSo\Log\Enumerations\Type::NOTICE, true); } } } else { $this->writeLogException( new \MailSo\Smtp\Exceptions\LoginBadMethodException(), \MailSo\Log\Enumerations\Type::NOTICE, true); } return $this; } /** * @param string $sXOAuth2Token * * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function LoginWithXOauth2($sXOAuth2Token) { if ($this->IsAuthSupported('XOAUTH2')) { try { $this->sendRequestWithCheck('AUTH', 235, 'XOAUTH2 '.trim($sXOAuth2Token)); } catch (\MailSo\Smtp\Exceptions\NegativeResponseException $oException) { $this->writeLogException( new \MailSo\Smtp\Exceptions\LoginBadCredentialsException( $oException->GetResponses(), $oException->getMessage(), 0, $oException), \MailSo\Log\Enumerations\Type::NOTICE, true); } } else { $this->writeLogException( new \MailSo\Smtp\Exceptions\LoginBadMethodException(), \MailSo\Log\Enumerations\Type::NOTICE, true); } return $this; } /** * @param string $sFrom * @param string $sSizeIfSupported = '' * * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function MailFrom($sFrom, $sSizeIfSupported = '') { $sFrom = \MailSo\Base\Utils::IdnToAscii($sFrom, true); $sCmd = 'FROM:<'.$sFrom.'>'; $sSizeIfSupported = (string) $sSizeIfSupported; if (0 < \strlen($sSizeIfSupported) && \is_numeric($sSizeIfSupported) && $this->IsSupported('SIZE')) { $sCmd .= ' SIZE='.$sSizeIfSupported; } $this->sendRequestWithCheck('MAIL', 250, $sCmd); $this->bMail = true; $this->bRcpt = false; $this->bData = false; return $this; } /** * @param string $sTo * * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function Rcpt($sTo) { if (!$this->bMail) { $this->writeLogException( new Exceptions\RuntimeException('No sender reverse path has been supplied'), \MailSo\Log\Enumerations\Type::ERROR, true); } $sTo = \MailSo\Base\Utils::IdnToAscii($sTo, true); $this->sendRequestWithCheck('RCPT', array(250, 251), 'TO:<'.$sTo.'>'); $this->bRcpt = true; return $this; } /** * @param string $sTo * * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function MailTo($sTo) { return $this->Rcpt($sTo); } /** * @param string $sData * * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function Data($sData) { if (!\MailSo\Base\Validator::NotEmptyString($sData, true)) { throw new \MailSo\Base\Exceptions\InvalidArgumentException(); } $rDataStream = \MailSo\Base\ResourceRegistry::CreateMemoryResourceFromString($sData); unset($sData); $this->DataWithStream($rDataStream); \MailSo\Base\ResourceRegistry::CloseMemoryResource($rDataStream); return $this; } /** * @param resource $rDataStream * * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function DataWithStream($rDataStream) { if (!\is_resource($rDataStream)) { throw new \MailSo\Base\Exceptions\InvalidArgumentException(); } if (!$this->bRcpt) { $this->writeLogException( new Exceptions\RuntimeException('No recipient forward path has been supplied'), \MailSo\Log\Enumerations\Type::ERROR, true); } $this->sendRequestWithCheck('DATA', 354); $this->writeLog('Message data.', \MailSo\Log\Enumerations\Type::NOTE); $this->bRunningCallback = true; while (!\feof($rDataStream)) { $sBuffer = \fgets($rDataStream); if (false !== $sBuffer) { if (0 === \strpos($sBuffer, '.')) { $sBuffer = '.'.$sBuffer; } $this->sendRaw(\rtrim($sBuffer, "\r\n"), false); \MailSo\Base\Utils::ResetTimeLimit(); continue; } else if (!\feof($rDataStream)) { $this->writeLogException( new Exceptions\RuntimeException('Cannot read input resource'), \MailSo\Log\Enumerations\Type::ERROR, true); } break; } $this->sendRequestWithCheck('.', 250); $this->bRunningCallback = false; $this->bData = true; return $this; } /** * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function Rset() { $this->sendRequestWithCheck('RSET', array(250, 220)); $this->bMail = false; $this->bRcpt = false; $this->bData = false; return $this; } /** * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function Vrfy($sUser) { $this->sendRequestWithCheck('VRFY', array(250, 251, 252), $sUser); return $this; } /** * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function Noop() { $this->sendRequestWithCheck('NOOP', 250); return $this; } /** * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ public function Logout() { if ($this->IsConnected()) { $this->sendRequestWithCheck('QUIT', 221); } $this->bHelo = false; $this->bMail = false; $this->bRcpt = false; $this->bData = false; return $this; } /** * @param string $sEhloHost * * @return void */ private function preLoginStartTLSAndEhloProcess($sEhloHost) { if ($this->bHelo) { $this->writeLogException( new Exceptions\RuntimeException('Cannot issue EHLO/HELO to existing session'), \MailSo\Log\Enumerations\Type::ERROR, true); } $this->ehloOrHelo($sEhloHost); if (\MailSo\Net\Enumerations\ConnectionSecurityType::UseStartTLS( $this->IsSupported('STARTTLS'), $this->iSecurityType, $this->HasSupportedAuth())) { $this->sendRequestWithCheck('STARTTLS', 220); $this->EnableCrypto(); $this->ehloOrHelo($sEhloHost); } else if (\MailSo\Net\Enumerations\ConnectionSecurityType::STARTTLS === $this->iSecurityType) { $this->writeLogException( new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('STARTTLS is not supported'), \MailSo\Log\Enumerations\Type::ERROR, true); } $this->bHelo = true; } /** * @param string $sCommand * @param string $sAddToCommand = '' * @param bool $bSecureLog = false * * @return void * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception */ private function sendRequest($sCommand, $sAddToCommand = '', $bSecureLog = false) { if (!\MailSo\Base\Validator::NotEmptyString($sCommand, true)) { $this->writeLogException( new \MailSo\Base\Exceptions\InvalidArgumentException(), \MailSo\Log\Enumerations\Type::ERROR, true); } $this->IsConnected(true); $sCommand = \trim($sCommand); $sRealCommand = $sCommand.(0 === \strlen($sAddToCommand) ? '' : ' '.$sAddToCommand); $sFakeCommand = ($bSecureLog) ? '********' : ''; $this->iRequestTime = \microtime(true); $this->sendRaw($sRealCommand, true, $sFakeCommand); return $this; } /** * @param string $sCommand * @param int|array $mExpectCode * @param string $sAddToCommand = '' * @param bool $bSecureLog = false * * @return void * * @throws \MailSo\Base\Exceptions\InvalidArgumentException * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ private function sendRequestWithCheck($sCommand, $mExpectCode, $sAddToCommand = '', $bSecureLog = false) { $this->sendRequest($sCommand, $sAddToCommand, $bSecureLog); $this->validateResponse($mExpectCode); } /** * @param string $sHost * * @return void */ private function ehloOrHelo($sHost) { try { $this->ehlo($sHost); } catch (\Exception $oException) { try { $this->helo($sHost); } catch (\Exception $oException) { throw $oException; } } return $this; } /** * @param string $sHost * * @return void * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ private function ehlo($sHost) { $this->sendRequestWithCheck('EHLO', 250, $sHost); foreach ($this->aResults as $sLine) { $aMatch = array(); if (\preg_match('/[\d]+[ \-](.+)$/', $sLine, $aMatch) && isset($aMatch[1]) && 0 < \strlen($aMatch[1])) { $sLine = \trim($aMatch[1]); $aLine = \preg_split('/[ =]/', $sLine, 2); if (\is_array($aLine) && 0 < \count($aLine) && !empty($aLine[0])) { $sCapa = \strtoupper($aLine[0]); if (('AUTH' === $sCapa || 'SIZE' === $sCapa) && !empty($aLine[1])) { $sSubLine = \trim(\strtoupper($aLine[1])); if (0 < \strlen($sSubLine)) { if ('AUTH' === $sCapa) { $this->aAuthTypes = \explode(' ', $sSubLine); } else if ('SIZE' === $sCapa && \is_numeric($sSubLine)) { $this->iSizeCapaValue = (int) $sSubLine; } } } $this->aCapa[] = $sCapa; } } } } /** * @param string $sHost * * @return void * * @throws \MailSo\Net\Exceptions\Exception * @throws \MailSo\Smtp\Exceptions\Exception */ private function helo($sHost) { $this->sendRequestWithCheck('HELO', 250, $sHost); $this->aAuthTypes = array(); $this->iSizeCapaValue = 0; $this->aCapa = array(); } /** * @param int|array $mExpectCode * * @return void * * @throws \MailSo\Smtp\Exceptions\ResponseException */ private function validateResponse($mExpectCode) { if (!\is_array($mExpectCode)) { $mExpectCode = array((int) $mExpectCode); } else { $mExpectCode = \array_map('intval', $mExpectCode); } $aParts = array('', '', ''); $this->aResults = array(); do { $this->getNextBuffer(); $aParts = \preg_split('/([\s-]+)/', $this->sResponseBuffer, 2, PREG_SPLIT_DELIM_CAPTURE); if (\is_array($aParts) && 3 === \count($aParts) && \is_numeric($aParts[0])) { if ('-' !== trim($aParts[1]) && !\in_array((int) $aParts[0], $mExpectCode)) { if ((int)$aParts[0] === 550) { $this->writeLogException( new Exceptions\MailboxUnavailableException($this->aResults, \trim( (0 < \count($this->aResults) ? \implode("\r\n", $this->aResults)."\r\n" : ''). $this->sResponseBuffer)), \MailSo\Log\Enumerations\Type::ERROR, true); } else { $this->writeLogException( new Exceptions\NegativeResponseException($this->aResults, \trim( (0 < \count($this->aResults) ? \implode("\r\n", $this->aResults)."\r\n" : ''). $this->sResponseBuffer)), \MailSo\Log\Enumerations\Type::ERROR, true); } } } else { $this->writeLogException( new Exceptions\ResponseException($this->aResults, \trim( (0 < \count($this->aResults) ? \implode("\r\n", $this->aResults)."\r\n" : ''). $this->sResponseBuffer)), \MailSo\Log\Enumerations\Type::ERROR, true); } $this->aResults[] = $this->sResponseBuffer; } while ('-' === \trim($aParts[1])); $this->writeLog((microtime(true) - $this->iRequestTime), \MailSo\Log\Enumerations\Type::TIME); } /** * @return string */ protected function getLogName() { return 'SMTP'; } /** * @param \MailSo\Log\Logger $oLogger * * @return \MailSo\Smtp\SmtpClient * * @throws \MailSo\Base\Exceptions\InvalidArgumentException */ public function SetLogger($oLogger) { parent::SetLogger($oLogger); return $this; } }