Japanese English

PHP deobfuscation, decryption, reconstruction tool

De-obfuscate PHP malware/viruses and tampering code on Wordpress to original readable code.

*Please note that not all obfuscation codes can be decoded.

Decoded the code below.

<?php namespace PHPMailer\PHPMailer; class SMTP { const VERSION = '6.1.4'; const LE = "\r\n"; const DEFAULT_PORT = 25; const MAX_LINE_LENGTH = 998; const MAX_REPLY_LENGTH = 512; const DEBUG_OFF = 0; const DEBUG_CLIENT = 1; const DEBUG_SERVER = 2; const DEBUG_CONNECTION = 3; const DEBUG_LOWLEVEL =...



Obfuscated php code

<?php
 namespace PHPMailer\PHPMailer; class SMTP { const VERSION = '6.1.4'; const LE = "\r\n"; const DEFAULT_PORT = 25; const MAX_LINE_LENGTH = 998; const MAX_REPLY_LENGTH = 512; const DEBUG_OFF = 0; const DEBUG_CLIENT = 1; const DEBUG_SERVER = 2; const DEBUG_CONNECTION = 3; const DEBUG_LOWLEVEL = 4; public $do_debug = self::DEBUG_OFF; public $Debugoutput = 'echo'; public $do_verp = false; public $Timeout = 300; public $Timelimit = 300; protected $smtp_transaction_id_patterns = [ 'exim' => '/[\d]{3} OK id=(.*)/', 'sendmail' => '/[\d]{3} 2.0.0 (.*) Message/', 'postfix' => '/[\d]{3} 2.0.0 Ok: queued as (.*)/', 'Microsoft_ESMTP' => '/[0-9]{3} 2.[\d].0 (.*)@(?:.*) Queued mail for delivery/', 'Amazon_SES' => '/[\d]{3} Ok (.*)/', 'SendGrid' => '/[\d]{3} Ok: queued as (.*)/', 'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/', ]; protected $last_smtp_transaction_id; protected $smtp_conn; protected $error = [ 'error' => '', 'detail' => '', 'smtp_code' => '', 'smtp_code_ex' => '', ]; protected $helo_rply; protected $server_caps; protected $last_reply = ''; protected function edebug($str, $level = 0) { if ($level > $this->do_debug) { return; } if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) { $this->Debugoutput->debug($str); return; } if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) { call_user_func($this->Debugoutput, $str, $level); return; } switch ($this->Debugoutput) { case 'error_log': error_log($str); break; case 'html': echo gmdate('Y-m-d H:i:s'), ' ', htmlentities( preg_replace('/[\r\n]+/', '', $str), ENT_QUOTES, 'UTF-8' ), "<br>\n"; break; case 'echo': default: $str = preg_replace('/\r\n|\r/m', "\n", $str); echo gmdate('Y-m-d H:i:s'), "\t", trim( str_replace( "\n", "\n                   \t                  ", trim($str) ) ), "\n"; } } public function connect($host, $port = null, $timeout = 30, $options = []) { static $streamok; if (null === $streamok) { $streamok = function_exists('stream_socket_client'); } $this->setError(''); if ($this->connected()) { $this->setError('Already connected to a server'); return false; } if (empty($port)) { $port = self::DEFAULT_PORT; } $this->edebug( "Connection: opening to $host:$port, timeout=$timeout, options=" . (count($options) > 0 ? var_export($options, true) : 'array()'), self::DEBUG_CONNECTION ); $errno = 0; $errstr = ''; if ($streamok) { $socket_context = stream_context_create($options); set_error_handler([$this, 'errorHandler']); $this->smtp_conn = stream_socket_client( $host . ':' . $port, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $socket_context ); restore_error_handler(); } else { $this->edebug( 'Connection: stream_socket_client not available, falling back to fsockopen', self::DEBUG_CONNECTION ); set_error_handler([$this, 'errorHandler']); $this->smtp_conn = fsockopen( $host, $port, $errno, $errstr, $timeout ); restore_error_handler(); } if (!is_resource($this->smtp_conn)) { $this->setError( 'Failed to connect to server', '', (string) $errno, $errstr ); $this->edebug( 'SMTP ERROR: ' . $this->error['error'] . ": $errstr ($errno)", self::DEBUG_CLIENT ); return false; } $this->edebug('Connection: opened', self::DEBUG_CONNECTION); if (strpos(PHP_OS, 'WIN') !== 0) { $max = (int) ini_get('max_execution_time'); if (0 !== $max && $timeout > $max) { @set_time_limit($timeout); } stream_set_timeout($this->smtp_conn, $timeout, 0); } $announce = $this->get_lines(); $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); return true; } public function startTLS() { 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; } set_error_handler([$this, 'errorHandler']); $crypto_ok = stream_socket_enable_crypto( $this->smtp_conn, true, $crypto_method ); restore_error_handler(); return (bool) $crypto_ok; } public function authenticate( $username, $password, $authtype = null, $OAuth = null ) { if (!$this->server_caps) { $this->setError('Authentication is not allowed before HELO/EHLO'); return false; } if (array_key_exists('EHLO', $this->server_caps)) { if (!array_key_exists('AUTH', $this->server_caps)) { $this->setError('Authentication is not allowed at this stage'); return false; } $this->edebug('Auth method requested: ' . ($authtype ?: 'UNSPECIFIED'), self::DEBUG_LOWLEVEL); $this->edebug( 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), self::DEBUG_LOWLEVEL ); if (null !== $authtype && !in_array($authtype, $this->server_caps['AUTH'], true)) { $this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL); $authtype = null; } if (empty($authtype)) { foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) { if (in_array($method, $this->server_caps['AUTH'], true)) { $authtype = $method; break; } } if (empty($authtype)) { $this->setError('No supported authentication methods found'); return false; } $this->edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL); } if (!in_array($authtype, $this->server_caps['AUTH'], true)) { $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); return false; } } elseif (empty($authtype)) { $authtype = 'LOGIN'; } switch ($authtype) { case 'PLAIN': if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { return false; } if (!$this->sendCommand( 'User & Password', base64_encode("\0" . $username . "\0" . $password), 235 ) ) { return false; } break; case 'LOGIN': if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { return false; } if (!$this->sendCommand('Username', base64_encode($username), 334)) { return false; } if (!$this->sendCommand('Password', base64_encode($password), 235)) { return false; } break; case 'CRAM-MD5': if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { return false; } $challenge = base64_decode(substr($this->last_reply, 4)); $response = $username . ' ' . $this->hmac($challenge, $password); return $this->sendCommand('Username', base64_encode($response), 235); case 'XOAUTH2': if (null === $OAuth) { return false; } $oauth = $OAuth->getOauth64(); if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { return false; } break; default: $this->setError("Authentication method \"$authtype\" is not supported"); return false; } return true; } protected function hmac($data, $key) { if (function_exists('hash_hmac')) { return hash_hmac('md5', $data, $key); } $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))); } public function connected() { if (is_resource($this->smtp_conn)) { $sock_status = stream_get_meta_data($this->smtp_conn); if ($sock_status['eof']) { $this->edebug( 'SMTP NOTICE: EOF caught while checking if connected', self::DEBUG_CLIENT ); $this->close(); return false; } return true; } return false; } public function close() { $this->setError(''); $this->server_caps = null; $this->helo_rply = null; if (is_resource($this->smtp_conn)) { fclose($this->smtp_conn); $this->smtp_conn = null; $this->edebug('Connection: closed', self::DEBUG_CONNECTION); } } public function data($msg_data) { if (!$this->sendCommand('DATA', 'DATA', 354)) { return false; } $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data)); $field = substr($lines[0], 0, strpos($lines[0], ':')); $in_headers = false; if (!empty($field) && strpos($field, ' ') === false) { $in_headers = true; } foreach ($lines as $line) { $lines_out = []; if ($in_headers && $line === '') { $in_headers = false; } while (isset($line[self::MAX_LINE_LENGTH])) { $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); if (!$pos) { $pos = self::MAX_LINE_LENGTH - 1; $lines_out[] = substr($line, 0, $pos); $line = substr($line, $pos); } else { $lines_out[] = substr($line, 0, $pos); $line = substr($line, $pos + 1); } if ($in_headers) { $line = "\t" . $line; } } $lines_out[] = $line; foreach ($lines_out as $line_out) { if (!empty($line_out) && $line_out[0] === '.') { $line_out = '.' . $line_out; } $this->client_send($line_out . static::LE, 'DATA'); } } $savetimelimit = $this->Timelimit; $this->Timelimit *= 2; $result = $this->sendCommand('DATA END', '.', 250); $this->recordLastTransactionID(); $this->Timelimit = $savetimelimit; return $result; } public function hello($host = '') { return $this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host); } protected function sendHello($hello, $host) { $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); $this->helo_rply = $this->last_reply; if ($noerror) { $this->parseHelloFields($hello); } else { $this->server_caps = null; } return $noerror; } protected function parseHelloFields($type) { $this->server_caps = []; $lines = explode("\n", $this->helo_rply); foreach ($lines as $n => $s) { $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->server_caps[$name] = $fields; } } } public function mail($from) { $useVerp = ($this->do_verp ? ' XVERP' : ''); return $this->sendCommand( 'MAIL FROM', 'MAIL FROM:<' . $from . '>' . $useVerp, 250 ); } public function quit($close_on_error = true) { $noerror = $this->sendCommand('QUIT', 'QUIT', 221); $err = $this->error; if ($noerror || $close_on_error) { $this->close(); $this->error = $err; } return $noerror; } public function recipient($address, $dsn = '') { if (empty($dsn)) { $rcpt = 'RCPT TO:<' . $address . '>'; } else { $dsn = strtoupper($dsn); $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] ); } public function reset() { return $this->sendCommand('RSET', 'RSET', 250); } protected function sendCommand($command, $commandstring, $expect) { if (!$this->connected()) { $this->setError("Called $command without being connected"); return false; } if ((strpos($commandstring, "\n") !== false) || (strpos($commandstring, "\r") !== false)) { $this->setError("Command '$command' contained line breaks"); return false; } $this->client_send($commandstring . static::LE, $command); $this->last_reply = $this->get_lines(); $matches = []; if (preg_match('/^([\d]{3})[ -](?:([\d]\\.[\d]\\.[\d]{1,2}) )?/', $this->last_reply, $matches)) { $code = (int) $matches[1]; $code_ex = (count($matches) > 2 ? $matches[2] : null); $detail = preg_replace( "/{$code}[ -]" . ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m', '', $this->last_reply ); } else { $code = (int) substr($this->last_reply, 0, 3); $code_ex = null; $detail = substr($this->last_reply, 4); } $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); if (!in_array($code, (array) $expect, true)) { $this->setError( "$command command failed", $detail, $code, $code_ex ); $this->edebug( 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, self::DEBUG_CLIENT ); return false; } $this->setError(''); return true; } public function sendAndMail($from) { return $this->sendCommand('SAML', "SAML FROM:$from", 250); } public function verify($name) { return $this->sendCommand('VRFY', "VRFY $name", [250, 251]); } public function noop() { return $this->sendCommand('NOOP', 'NOOP', 250); } public function turn() { $this->setError('The SMTP TURN command is not implemented'); $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); return false; } public function client_send($data, $command = '') { if (self::DEBUG_LOWLEVEL > $this->do_debug && in_array($command, ['User & Password', 'Username', 'Password'], true)) { $this->edebug('CLIENT -> SERVER: [credentials hidden]', self::DEBUG_CLIENT); } else { $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT); } set_error_handler([$this, 'errorHandler']); $result = fwrite($this->smtp_conn, $data); restore_error_handler(); return $result; } public function getError() { return $this->error; } public function getServerExtList() { return $this->server_caps; } public function getServerExt($name) { if (!$this->server_caps) { $this->setError('No HELO/EHLO was sent'); return; } if (!array_key_exists($name, $this->server_caps)) { if ('HELO' === $name) { return $this->server_caps['EHLO']; } if ('EHLO' === $name || array_key_exists('EHLO', $this->server_caps)) { return false; } $this->setError('HELO handshake was used; No information about server extensions available'); return; } return $this->server_caps[$name]; } public function getLastReply() { return $this->last_reply; } protected function get_lines() { if (!is_resource($this->smtp_conn)) { return ''; } $data = ''; $endtime = 0; stream_set_timeout($this->smtp_conn, $this->Timeout); if ($this->Timelimit > 0) { $endtime = time() + $this->Timelimit; } $selR = [$this->smtp_conn]; $selW = null; while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { if (!stream_select($selR, $selW, $selW, $this->Timelimit)) { $this->edebug( 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', self::DEBUG_LOWLEVEL ); break; } $str = @fgets($this->smtp_conn, self::MAX_REPLY_LENGTH); $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL); $data .= $str; if (!isset($str[3]) || $str[3] === ' ' || $str[3] === "\r" || $str[3] === "\n") { break; } $info = stream_get_meta_data($this->smtp_conn); if ($info['timed_out']) { $this->edebug( 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', self::DEBUG_LOWLEVEL ); break; } if ($endtime && time() > $endtime) { $this->edebug( 'SMTP -> get_lines(): timelimit reached (' . $this->Timelimit . ' sec)', self::DEBUG_LOWLEVEL ); break; } } return $data; } public function setVerp($enabled = false) { $this->do_verp = $enabled; } public function getVerp() { return $this->do_verp; } protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') { $this->error = [ 'error' => $message, 'detail' => $detail, 'smtp_code' => $smtp_code, 'smtp_code_ex' => $smtp_code_ex, ]; } public function setDebugOutput($method = 'echo') { $this->Debugoutput = $method; } public function getDebugOutput() { return $this->Debugoutput; } public function setDebugLevel($level = 0) { $this->do_debug = $level; } public function getDebugLevel() { return $this->do_debug; } public function setTimeout($timeout = 0) { $this->Timeout = $timeout; } public function getTimeout() { return $this->Timeout; } protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0) { $notice = 'Connection failed.'; $this->setError( $notice, $errmsg, (string) $errno ); $this->edebug( "$notice Error #$errno: $errmsg [$errfile line $errline]", self::DEBUG_CONNECTION ); } protected function recordLastTransactionID() { $reply = $this->getLastReply(); if (empty($reply)) { $this->last_smtp_transaction_id = null; } else { $this->last_smtp_transaction_id = false; foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) { $this->last_smtp_transaction_id = trim($matches[1]); break; } } } return $this->last_smtp_transaction_id; } public function getLastTransactionID() { return $this->last_smtp_transaction_id; } }

Decoded(de-Obfuscated) php code

<?php

namespace PHPMailer\PHPMailer;

class SMTP
{
    const VERSION = '6.1.4';
    const LE = "\r\n";
    const DEFAULT_PORT = 25;
    const MAX_LINE_LENGTH = 998;
    const MAX_REPLY_LENGTH = 512;
    const DEBUG_OFF = 0;
    const DEBUG_CLIENT = 1;
    const DEBUG_SERVER = 2;
    const DEBUG_CONNECTION = 3;
    const DEBUG_LOWLEVEL = 4;
    public $do_debug = self::DEBUG_OFF;
    public $Debugoutput = 'echo';
    public $do_verp = false;
    public $Timeout = 300;
    public $Timelimit = 300;
    protected $smtp_transaction_id_patterns = array('exim' => '/[\\d]{3} OK id=(.*)/', 'sendmail' => '/[\\d]{3} 2.0.0 (.*) Message/', 'postfix' => '/[\\d]{3} 2.0.0 Ok: queued as (.*)/', 'Microsoft_ESMTP' => '/[0-9]{3} 2.[\\d].0 (.*)@(?:.*) Queued mail for delivery/', 'Amazon_SES' => '/[\\d]{3} Ok (.*)/', 'SendGrid' => '/[\\d]{3} Ok: queued as (.*)/', 'CampaignMonitor' => '/[\\d]{3} 2.0.0 OK:([a-zA-Z\\d]{48})/');
    protected $last_smtp_transaction_id;
    protected $smtp_conn;
    protected $error = array('error' => '', 'detail' => '', 'smtp_code' => '', 'smtp_code_ex' => '');
    protected $helo_rply;
    protected $server_caps;
    protected $last_reply = '';
    protected function edebug($str, $level = 0)
    {
        if ($level > $this->do_debug) {
            return;
        }
        if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
            $this->Debugoutput->debug($str);
            return;
        }
        if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) {
            call_user_func($this->Debugoutput, $str, $level);
            return;
        }
        switch ($this->Debugoutput) {
            case 'error_log':
                error_log($str);
                break;
            case 'html':
                echo gmdate('Y-m-d H:i:s'), ' ', htmlentities(preg_replace('/[\\r\\n]+/', '', $str), ENT_QUOTES, 'UTF-8'), "<br>\n";
                break;
            case 'echo':
            default:
                $str = preg_replace('/\\r\\n|\\r/m', "\n", $str);
                echo gmdate('Y-m-d H:i:s'), "\t", trim(str_replace("\n", "\n                   \t                  ", trim($str))), "\n";
        }
    }
    public function connect($host, $port = null, $timeout = 30, $options = array())
    {
        static $streamok;
        if (null === $streamok) {
            $streamok = function_exists('stream_socket_client');
        }
        $this->setError('');
        if ($this->connected()) {
            $this->setError('Already connected to a server');
            return false;
        }
        if (empty($port)) {
            $port = self::DEFAULT_PORT;
        }
        $this->edebug("Connection: opening to {$host}:{$port}, timeout={$timeout}, options=" . (count($options) > 0 ? var_export($options, true) : 'array()'), self::DEBUG_CONNECTION);
        $errno = 0;
        $errstr = '';
        if ($streamok) {
            $socket_context = stream_context_create($options);
            set_error_handler([$this, 'errorHandler']);
            $this->smtp_conn = stream_socket_client($host . ':' . $port, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $socket_context);
            restore_error_handler();
        } else {
            $this->edebug('Connection: stream_socket_client not available, falling back to fsockopen', self::DEBUG_CONNECTION);
            set_error_handler([$this, 'errorHandler']);
            $this->smtp_conn = fsockopen($host, $port, $errno, $errstr, $timeout);
            restore_error_handler();
        }
        if (!is_resource($this->smtp_conn)) {
            $this->setError('Failed to connect to server', '', (string) $errno, $errstr);
            $this->edebug('SMTP ERROR: ' . $this->error['error'] . ": {$errstr} ({$errno})", self::DEBUG_CLIENT);
            return false;
        }
        $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
        if (strpos(PHP_OS, 'WIN') !== 0) {
            $max = (int) ini_get('max_execution_time');
            if (0 !== $max && $timeout > $max) {
                @set_time_limit($timeout);
            }
            stream_set_timeout($this->smtp_conn, $timeout, 0);
        }
        $announce = $this->get_lines();
        $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
        return true;
    }
    public function startTLS()
    {
        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_TLSs_{_O\\IENT";
            $crypto_method = "STREAM_CRYPTO_METHOD_TLSs_{_O\\IENT";
        }
        set_error_handler([$this, 'errorHandler']);
        $crypto_ok = stream_socket_enable_crypto($this->smtp_conn, true, $crypto_method);
        restore_error_handler();
        return (bool) $crypto_ok;
    }
    public function authenticate($username, $password, $authtype = null, $OAuth = null)
    {
        if (!$this->server_caps) {
            $this->setError('Authentication is not allowed before HELO/EHLO');
            return false;
        }
        if (array_key_exists('EHLO', $this->server_caps)) {
            if (!array_key_exists('AUTH', $this->server_caps)) {
                $this->setError('Authentication is not allowed at this stage');
                return false;
            }
            $this->edebug('Auth method requested: ' . ($authtype ?: 'UNSPECIFIED'), self::DEBUG_LOWLEVEL);
            $this->edebug('Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), self::DEBUG_LOWLEVEL);
            if (null !== $authtype && !in_array($authtype, $this->server_caps['AUTH'], true)) {
                $this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL);
                $authtype = null;
            }
            if (empty($authtype)) {
                foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) {
                    if (in_array($method, $this->server_caps['AUTH'], true)) {
                        $authtype = $method;
                        break;
                    }
                }
                if (empty($authtype)) {
                    $this->setError('No supported authentication methods found');
                    return false;
                }
                $this->edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL);
            }
            if (!in_array($authtype, $this->server_caps['AUTH'], true)) {
                $this->setError("The requested authentication method \"{$authtype}\" is not supported by the server");
                return false;
            }
        } elseif (empty($authtype)) {
            $authtype = 'LOGIN';
        }
        switch ($authtype) {
            case 'PLAIN':
                if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
                    return false;
                }
                if (!$this->sendCommand('User & Password', base64_encode("\0" . $username . "\0" . $password), 235)) {
                    return false;
                }
                break;
            case 'LOGIN':
                if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
                    return false;
                }
                if (!$this->sendCommand('Username', base64_encode($username), 334)) {
                    return false;
                }
                if (!$this->sendCommand('Password', base64_encode($password), 235)) {
                    return false;
                }
                break;
            case 'CRAM-MD5':
                if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
                    return false;
                }
                $challenge = base64_decode(substr($this->last_reply, 4));
                $response = $username . ' ' . $this->hmac($challenge, $password);
                return $this->sendCommand('Username', base64_encode($response), 235);
            case 'XOAUTH2':
                if (null === $OAuth) {
                    return false;
                }
                $oauth = $OAuth->getOauth64();
                if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
                    return false;
                }
                break;
            default:
                $this->setError("Authentication method \"{$authtype}\" is not supported");
                return false;
        }
        return true;
    }
    protected function hmac($data, $key)
    {
        if (function_exists('hash_hmac')) {
            return hash_hmac('md5', $data, $key);
        }
        $bytelen = 64;
        if (strlen($key) > $bytelen) {
            $key = pack('H*', md5($key));
        }
        $key = str_pad($key, $bytelen, "\0");
        $ipad = str_pad('', $bytelen, "6");
        $opad = str_pad('', $bytelen, "\\");
        $k_ipad = $key ^ $ipad;
        $k_opad = $key ^ $opad;
        return md5($k_opad . pack('H*', md5($k_ipad . $data)));
    }
    public function connected()
    {
        if (is_resource($this->smtp_conn)) {
            $sock_status = stream_get_meta_data($this->smtp_conn);
            if ($sock_status['eof']) {
                $this->edebug('SMTP NOTICE: EOF caught while checking if connected', self::DEBUG_CLIENT);
                $this->close();
                return false;
            }
            return true;
        }
        return false;
    }
    public function close()
    {
        $this->setError('');
        $this->server_caps = null;
        $this->helo_rply = null;
        if (is_resource($this->smtp_conn)) {
            fclose($this->smtp_conn);
            $this->smtp_conn = null;
            $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
        }
    }
    public function data($msg_data)
    {
        if (!$this->sendCommand('DATA', 'DATA', 354)) {
            return false;
        }
        $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
        $field = substr($lines[0], 0, strpos($lines[0], ':'));
        $in_headers = false;
        if (!empty($field) && strpos($field, ' ') === false) {
            $in_headers = true;
        }
        foreach ($lines as $line) {
            $lines_out = [];
            if ($in_headers && $line === '') {
                $in_headers = false;
            }
            while (isset($line[self::MAX_LINE_LENGTH])) {
                $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
                if (!$pos) {
                    $pos = self::MAX_LINE_LENGTH - 1;
                    $lines_out[] = substr($line, 0, $pos);
                    $line = substr($line, $pos);
                } else {
                    $lines_out[] = substr($line, 0, $pos);
                    $line = substr($line, $pos + 1);
                }
                if ($in_headers) {
                    $line = "\t" . $line;
                }
            }
            $lines_out[] = $line;
            foreach ($lines_out as $line_out) {
                if (!empty($line_out) && $line_out[0] === '.') {
                    $line_out = '.' . $line_out;
                }
                $this->client_send($line_out . static::LE, 'DATA');
            }
        }
        $savetimelimit = $this->Timelimit;
        $this->Timelimit *= 2;
        $result = $this->sendCommand('DATA END', '.', 250);
        $this->recordLastTransactionID();
        $this->Timelimit = $savetimelimit;
        return $result;
    }
    public function hello($host = '')
    {
        return $this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host);
    }
    protected function sendHello($hello, $host)
    {
        $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
        $this->helo_rply = $this->last_reply;
        if ($noerror) {
            $this->parseHelloFields($hello);
        } else {
            $this->server_caps = null;
        }
        return $noerror;
    }
    protected function parseHelloFields($type)
    {
        $this->server_caps = [];
        $lines = explode("\n", $this->helo_rply);
        foreach ($lines as $n => $s) {
            $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->server_caps[$name] = $fields;
            }
        }
    }
    public function mail($from)
    {
        $useVerp = $this->do_verp ? ' XVERP' : '';
        return $this->sendCommand('MAIL FROM', 'MAIL FROM:<' . $from . '>' . $useVerp, 250);
    }
    public function quit($close_on_error = true)
    {
        $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
        $err = $this->error;
        if ($noerror || $close_on_error) {
            $this->close();
            $this->error = $err;
        }
        return $noerror;
    }
    public function recipient($address, $dsn = '')
    {
        if (empty($dsn)) {
            $rcpt = 'RCPT TO:<' . $address . '>';
        } else {
            $dsn = strtoupper($dsn);
            $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]);
    }
    public function reset()
    {
        return $this->sendCommand('RSET', 'RSET', 250);
    }
    protected function sendCommand($command, $commandstring, $expect)
    {
        if (!$this->connected()) {
            $this->setError("Called {$command} without being connected");
            return false;
        }
        if (strpos($commandstring, "\n") !== false || strpos($commandstring, "\r") !== false) {
            $this->setError("Command '{$command}' contained line breaks");
            return false;
        }
        $this->client_send($commandstring . static::LE, $command);
        $this->last_reply = $this->get_lines();
        $matches = [];
        if (preg_match('/^([\\d]{3})[ -](?:([\\d]\\.[\\d]\\.[\\d]{1,2}) )?/', $this->last_reply, $matches)) {
            $code = (int) $matches[1];
            $code_ex = count($matches) > 2 ? $matches[2] : null;
            $detail = preg_replace("/{$code}[ -]" . ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m', '', $this->last_reply);
        } else {
            $code = (int) substr($this->last_reply, 0, 3);
            $code_ex = null;
            $detail = substr($this->last_reply, 4);
        }
        $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
        if (!in_array($code, (array) $expect, true)) {
            $this->setError("{$command} command failed", $detail, $code, $code_ex);
            $this->edebug('SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, self::DEBUG_CLIENT);
            return false;
        }
        $this->setError('');
        return true;
    }
    public function sendAndMail($from)
    {
        return $this->sendCommand('SAML', "SAML FROM:{$from}", 250);
    }
    public function verify($name)
    {
        return $this->sendCommand('VRFY', "VRFY {$name}", [250, 251]);
    }
    public function noop()
    {
        return $this->sendCommand('NOOP', 'NOOP', 250);
    }
    public function turn()
    {
        $this->setError('The SMTP TURN command is not implemented');
        $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
        return false;
    }
    public function client_send($data, $command = '')
    {
        if (self::DEBUG_LOWLEVEL > $this->do_debug && in_array($command, ['User & Password', 'Username', 'Password'], true)) {
            $this->edebug('CLIENT -> SERVER: [credentials hidden]', self::DEBUG_CLIENT);
        } else {
            $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT);
        }
        set_error_handler([$this, 'errorHandler']);
        $result = fwrite($this->smtp_conn, $data);
        restore_error_handler();
        return $result;
    }
    public function getError()
    {
        return $this->error;
    }
    public function getServerExtList()
    {
        return $this->server_caps;
    }
    public function getServerExt($name)
    {
        if (!$this->server_caps) {
            $this->setError('No HELO/EHLO was sent');
            return;
        }
        if (!array_key_exists($name, $this->server_caps)) {
            if ('HELO' === $name) {
                return $this->server_caps['EHLO'];
            }
            if ('EHLO' === $name || array_key_exists('EHLO', $this->server_caps)) {
                return false;
            }
            $this->setError('HELO handshake was used; No information about server extensions available');
            return;
        }
        return $this->server_caps[$name];
    }
    public function getLastReply()
    {
        return $this->last_reply;
    }
    protected function get_lines()
    {
        if (!is_resource($this->smtp_conn)) {
            return '';
        }
        $data = '';
        $endtime = 0;
        stream_set_timeout($this->smtp_conn, $this->Timeout);
        if ($this->Timelimit > 0) {
            $endtime = time() + $this->Timelimit;
        }
        $selR = [$this->smtp_conn];
        $selW = null;
        while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
            if (!stream_select($selR, $selW, $selW, $this->Timelimit)) {
                $this->edebug('SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', self::DEBUG_LOWLEVEL);
                break;
            }
            $str = @fgets($this->smtp_conn, self::MAX_REPLY_LENGTH);
            $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL);
            $data .= $str;
            if (!isset($str[3]) || $str[3] === ' ' || $str[3] === "\r" || $str[3] === "\n") {
                break;
            }
            $info = stream_get_meta_data($this->smtp_conn);
            if ($info['timed_out']) {
                $this->edebug('SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', self::DEBUG_LOWLEVEL);
                break;
            }
            if ($endtime && time() > $endtime) {
                $this->edebug('SMTP -> get_lines(): timelimit reached (' . $this->Timelimit . ' sec)', self::DEBUG_LOWLEVEL);
                break;
            }
        }
        return $data;
    }
    public function setVerp($enabled = false)
    {
        $this->do_verp = $enabled;
    }
    public function getVerp()
    {
        return $this->do_verp;
    }
    protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
    {
        $this->error = ['error' => $message, 'detail' => $detail, 'smtp_code' => $smtp_code, 'smtp_code_ex' => $smtp_code_ex];
    }
    public function setDebugOutput($method = 'echo')
    {
        $this->Debugoutput = $method;
    }
    public function getDebugOutput()
    {
        return $this->Debugoutput;
    }
    public function setDebugLevel($level = 0)
    {
        $this->do_debug = $level;
    }
    public function getDebugLevel()
    {
        return $this->do_debug;
    }
    public function setTimeout($timeout = 0)
    {
        $this->Timeout = $timeout;
    }
    public function getTimeout()
    {
        return $this->Timeout;
    }
    protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0)
    {
        $notice = 'Connection failed.';
        $this->setError($notice, $errmsg, (string) $errno);
        $this->edebug("{$notice} Error #{$errno}: {$errmsg} [{$errfile} line {$errline}]", self::DEBUG_CONNECTION);
    }
    protected function recordLastTransactionID()
    {
        $reply = $this->getLastReply();
        if (empty($reply)) {
            $this->last_smtp_transaction_id = null;
        } else {
            $this->last_smtp_transaction_id = false;
            foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
                if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
                    $this->last_smtp_transaction_id = trim($matches[1]);
                    break;
                }
            }
        }
        return $this->last_smtp_transaction_id;
    }
    public function getLastTransactionID()
    {
        return $this->last_smtp_transaction_id;
    }
}


Malware detection & removal plugin for WordPress

(C)2020 Wordpress Doctor All rights reserved.