/** * Connects to the smtp-server */ protected function connect() { // connect $this->connection = new RemoteFile(MAIL_SMTP_HOST, MAIL_SMTP_PORT); $this->getSMTPStatus(); if ($this->statusCode != 220) { throw new SystemException($this->formatError("can not connect to '" . MAIL_SMTP_HOST . ":" . MAIL_SMTP_PORT . "'")); } $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ''; if (empty($host)) { $host = gethostname(); if ($host === false) { $host = 'localhost'; } } // send ehlo $this->write('EHLO ' . $host); $extensions = explode(Mail::$lineEnding, $this->read()); $this->getSMTPStatus(array_shift($extensions)); if ($this->statusCode == 250) { $extensions = array_map(function ($element) { return strtolower(substr($element, 4)); }, $extensions); if ($this->connection->hasTLSSupport() && in_array('starttls', $extensions)) { $this->write('STARTTLS'); $this->getSMTPStatus(); if ($this->statusCode != 220) { throw new SystemException($this->formatError("cannot enable STARTTLS, though '" . MAIL_SMTP_HOST . ":" . MAIL_SMTP_PORT . "' advertised it")); } if (!$this->connection->setTLS(true)) { throw new SystemException('enabling TLS failed'); } // repeat EHLO $this->write('EHLO ' . $host); $extensions = explode(Mail::$lineEnding, $this->read()); $this->getSMTPStatus(array_shift($extensions)); if ($this->statusCode != 250) { throw new SystemException($this->formatError("could not EHLO after enabling STARTTLS at '" . MAIL_SMTP_HOST . ":" . MAIL_SMTP_PORT . "'")); } } // do authentication if (MAIL_SMTP_USER != '' || MAIL_SMTP_PASSWORD != '') { $this->auth(); } } else { // send helo $this->write('HELO ' . $host); $this->getSMTPStatus(); if ($this->statusCode != 250) { throw new SystemException($this->formatError("can not connect to '" . MAIL_SMTP_HOST . ":" . MAIL_SMTP_PORT . "'")); } } }
/** * Executes the HTTP request. */ public function execute() { // connect $remoteFile = new RemoteFile(($this->useSSL ? 'ssl://' : '') . $this->host, $this->port, $this->options['timeout'], array('ssl' => array('peer_name' => $this->originHost))); if ($this->originUseSSL && PROXY_SERVER_HTTP) { if ($this->useSSL) { throw new SystemException("Unable to proxy HTTPS when using TLS for proxy connection"); } $request = "CONNECT " . $this->originHost . ":" . $this->originPort . " HTTP/1.0\r\n"; if (isset($this->headers['user-agent'])) { $request .= 'user-agent: ' . reset($this->headers['user-agent']) . "\r\n"; } $request .= "Host: " . $this->originHost . ":" . $this->originPort . "\r\n"; $request .= "\r\n"; $remoteFile->puts($request); $this->replyHeaders = array(); while (!$remoteFile->eof()) { $line = $remoteFile->gets(); if (rtrim($line) === '') { $this->parseReplyHeaders(); break; } $this->replyHeaders[] = $line; } if ($this->statusCode != 200) { throw new SystemException("Expected 200 Ok as reply to my CONNECT, got '" . $this->statusCode . "'"); } $remoteFile->setTLS(true); } $request = $this->options['method'] . " " . $this->path . ($this->query ? '?' . $this->query : '') . " HTTP/1.1\r\n"; // add headers foreach ($this->headers as $name => $values) { foreach ($values as $value) { $request .= $name . ": " . $value . "\r\n"; } } $request .= "\r\n"; // add post parameters if ($this->options['method'] !== 'GET') { $request .= $this->body . "\r\n\r\n"; } $remoteFile->puts($request); $inHeader = true; $this->replyHeaders = array(); $this->replyBody = ''; $chunkLength = 0; $bodyLength = 0; $chunkedTransferRegex = new Regex('(^|,)[ \\t]*chunked[ \\t]*$', Regex::CASE_INSENSITIVE); // read http response, until one of is true // a) EOF is reached // b) bodyLength is at least maxLength // c) bodyLength is at least Content-Length while (!($remoteFile->eof() || isset($this->options['maxLength']) && $bodyLength >= $this->options['maxLength'] || isset($this->replyHeaders['content-length']) && $bodyLength >= end($this->replyHeaders['content-length']))) { if ($chunkLength) { if (isset($this->options['maxLength'])) { $chunkLength = min($chunkLength, $this->options['maxLength'] - $bodyLength); } $line = $remoteFile->read($chunkLength); } else { if (!$inHeader && (!isset($this->replyHeaders['transfer-encoding']) || !$chunkedTransferRegex->match(end($this->replyHeaders['transfer-encoding'])))) { $length = 1024; if (isset($this->options['maxLength'])) { $length = min($length, $this->options['maxLength'] - $bodyLength); } if (isset($this->replyHeaders['content-length'])) { $length = min($length, end($this->replyHeaders['content-length']) - $bodyLength); } $line = $remoteFile->read($length); } else { $line = $remoteFile->gets(); } } if ($inHeader) { if (rtrim($line) === '') { $inHeader = false; $this->parseReplyHeaders(); continue; } $this->replyHeaders[] = $line; } else { if (isset($this->replyHeaders['transfer-encoding']) && $chunkedTransferRegex->match(end($this->replyHeaders['transfer-encoding']))) { // last chunk finished if ($chunkLength === 0) { // read hex data and trash chunk-extension list($hex) = explode(';', $line, 2); $chunkLength = hexdec($hex); // $chunkLength === 0 -> no more data if ($chunkLength === 0) { // clear remaining response while (!$remoteFile->gets(1024)) { } // remove chunked from transfer-encoding $this->replyHeaders['transfer-encoding'] = array_filter(array_map(function ($element) use($chunkedTransferRegex) { return $chunkedTransferRegex->replace($element, ''); }, $this->replyHeaders['transfer-encoding']), 'trim'); if (empty($this->replyHeaders['transfer-encoding'])) { unset($this->replyHeaders['transfer-encoding']); } // break out of main reading loop break; } } else { $this->replyBody .= $line; $chunkLength -= strlen($line); $bodyLength += strlen($line); if ($chunkLength === 0) { $remoteFile->read(2); } // CRLF } } else { $this->replyBody .= $line; $bodyLength += strlen($line); } } } if (isset($this->options['maxLength'])) { $this->replyBody = substr($this->replyBody, 0, $this->options['maxLength']); } $remoteFile->close(); $this->parseReply(); }