/** * Send a message. * @TODO: Implement the DIGEST-MD5 and GSSAPI auth protocol. Implement SSLv1 * and v2 support. * * @param \Hoa\Mail\Message $message Message. * @return bool */ public function send(Mail\Message $message) { $content = $message->getFormattedContent(); $headers = $message->getHeaders(); $client = $this->getClient(); $client->connect(); $this->ifNot(220, 'Not able to connect to the server'); $domain = $message->getDomain($this->getUsername() ?: $headers['from']); $client->writeAll('EHLO ' . $domain . CRLF); $ehlo = preg_split('#' . CRLF . '250[\\-\\s]+#', $client->read(2048)); if (true === in_array('STARTTLS', $ehlo)) { $client->writeAll('STARTTLS' . CRLF); $this->ifNot(220, 'Cannot start a TLS connection'); if (true !== $client->enableEncryption(true, $client::ENCRYPTION_TLS)) { throw new Mail\Exception\Transport('Cannot enable a TLS connection.', 1); } $client->writeAll('EHLO ' . $domain . CRLF); $ehlo = preg_split('#' . CRLF . '250[\\-\\s]+#', $client->read(2048)); } $matches = null; foreach ($ehlo as $entry) { if (0 !== preg_match('#^AUTH (.+)$#', $entry, $matches)) { break; } } if (empty($matches)) { throw new Mail\Exception\Transport('The server does not support authentication, we cannot ' . 'authenticate.', 2); } $auth = explode(' ', $matches[1]); $username = $this->getUsername(); $password = $this->getPassword(); if (true === in_array('PLAIN', $auth)) { $client->writeAll('AUTH PLAIN' . CRLF); $this->ifNot(334, 'Authentication failed (PLAIN)'); $challenge = base64_encode("" . $username . "" . $password); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong username or password'); } elseif (true === in_array('LOGIN', $auth)) { $client->writeAll('AUTH LOGIN' . CRLF); $this->ifNot(334, 'Authentication failed (LOGIN)'); $challenge = base64_encode($username); $client->writeAll($challenge . CRLF); $this->ifNot(334, 'Wrong username'); $challenge = base64_encode($password); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong password'); } elseif (true === in_array('CRAM-MD5', $auth)) { $client->writeAll('AUTH CRAM-MD5' . CRLF); $line = $this->ifNot(334, 'Authentication failed (CRAM-MD5)'); $handle = base64_decode(substr($line, 4)); $challenge = base64_encode($username . ' ' . static::hmac($password, $handle)); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong username or password'); } else { throw new Mail\Transport('%s does not support authentication algorithms available ' . 'on the server (%s).', 3, [__CLASS__, implode(', ', $auth)]); } $from = $message->getAddress($headers['from']); $client->writeAll('MAIL FROM: <' . $from . '>' . CRLF); $this->ifNot(250, 'Sender ' . $from . ' is wrong'); foreach ($message->getRecipients() as $recipient) { $client->writeAll('RCPT TO: <' . $recipient . '>' . CRLF); $this->ifNot(250, 'Recipient ' . $recipient . ' is wrong'); } $client->writeAll('DATA' . CRLF); $this->ifNot(354, 'Cannot send data'); $client->writeAll($content . CRLF . '.' . CRLF); $this->ifNot(250, 'Something went wrong with data'); $client->writeAll('QUIT' . CRLF); $this->ifNot(221, 'Cannot quit properly'); $client->disconnect(); return true; }
/** * Send a message. * Timeouts are defined as the RFC2821 Section 4.5.3.2 suggests. * * @TODO: Implement the DIGEST-MD5 and GSSAPI auth protocol. Implement SSLv1 * and v2 support. * * @param \Hoa\Mail\Message $message Message. * @return bool */ public function send(Mail\Message $message) { $content = $message->getFormattedContent(); $headers = $message->getHeaders(); $client = $this->getClient(); $client->connect(); $client->setStreamBlocking(true); $client->setStreamTimeout(5 * 60); $this->ifNot(220, 'Not able to connect to the server', 'The server timed out while trying to connect.'); $domain = $message->getDomain($this->getUsername() ?: $headers['from']); $client->writeAll('EHLO ' . $domain . CRLF); $ehlo = $this->ifNot(250, 'Not able to get supported extensions from the server.', 'The server timed out while answering to a `EHLO` command.'); if (true === in_array('STARTTLS', $ehlo)) { $client->writeAll('STARTTLS' . CRLF); $this->ifNot(220, 'Cannot start a TLS connection', 'The server timed out while starting a TLS encryption.'); if (true !== $client->enableEncryption(true, $client::ENCRYPTION_TLS)) { throw new Mail\Exception\Transport('Cannot enable a TLS connection.', 3); } $client->writeAll('EHLO ' . $domain . CRLF); $ehlo = $this->ifNot(250, 'Not able to get supported extensions from the server.', 'The server timed out while answering to a `EHLO` command ' . 'after having established a TLS connection.'); } $matches = null; foreach ($ehlo as $entry) { if (0 !== preg_match('#^AUTH (.+)$#', $entry, $matches)) { break; } } if (empty($matches)) { throw new Mail\Exception\Transport('The server does not support authentication, we cannot ' . 'authenticate.', 4); } $auth = explode(' ', $matches[1]); $username = $this->getUsername(); $password = $this->getPassword(); if (true === in_array('PLAIN', $auth)) { $client->writeAll('AUTH PLAIN' . CRLF); $this->ifNot(334, 'Authentication failed (PLAIN)', 'The server timed out while answering to an `AUTH PLAIN` ' . 'authentication.'); $challenge = base64_encode("" . $username . "" . $password); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong username or password', 'The server timed out while asserting whether the password ' . 'is correct for an `AUTH PLAIN` authentication.'); } elseif (true === in_array('LOGIN', $auth)) { $client->writeAll('AUTH LOGIN' . CRLF); $this->ifNot(334, 'Authentication failed (LOGIN)', 'The server timed out while answering to an `AUTH LOGIN` ' . 'authentication.'); $challenge = base64_encode($username); $client->writeAll($challenge . CRLF); $this->ifNot(334, 'Wrong username', 'The server timed out while asserting whether the username ' . 'is correct for an `AUTH LOGIN` authentication.'); $challenge = base64_encode($password); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong password', 'The server timed out while asserting whether the password ' . 'is correct for an `AUTH LOGIN` authentication.'); } elseif (true === in_array('CRAM-MD5', $auth)) { $client->writeAll('AUTH CRAM-MD5' . CRLF); $line = $this->ifNot(334, 'Authentication failed (CRAM-MD5)', 'The server timed out while answering to an `AUTH CRAM-MD5` ' . 'authentication.'); $handle = base64_decode(substr($line, 4)); $challenge = base64_encode($username . ' ' . static::hmac($password, $handle)); $client->writeAll($challenge . CRLF); $this->ifNot(235, 'Wrong username or password', 'The server timed out while asserting whether the username ' . 'and password are correct for an `AUTH CRAM-MD5` authentication.'); } else { throw new Mail\Transport('%s does not support authentication algorithms available ' . 'on the server (%s).', 5, [__CLASS__, implode(', ', $auth)]); } $client->setStreamTimeout(5 * 60); $from = $message->getAddress($headers['from']); $client->writeAll('MAIL FROM: <' . $from . '>' . CRLF); $this->ifNot(250, 'Sender ' . $from . ' is wrong', 'The server timed out while asserting whether the sender\'s ' . 'email is correct.'); $client->setStreamTimeout(5 * 60); foreach ($message->getRecipients() as $recipient) { $client->writeAll('RCPT TO: <' . $recipient . '>' . CRLF); $this->ifNot(250, 'Recipient ' . $recipient . ' is wrong', 'The server timed out while asserting whether the ' . 'recipient\'s emails are correct.'); } $client->setStreamTimeout(5 * 60); $client->writeAll('DATA' . CRLF); $this->ifNot(354, 'Cannot send data', 'The server timed out while answering to a `DATA` command.'); $client->setStreamTimeout(10 * 60); $client->writeAll($content . CRLF . '.' . CRLF); $this->ifNot(250, 'Something went wrong with data', 'The server timed out while asserting all the data have been ' . 'received correctly.'); $client->writeAll('QUIT' . CRLF); $this->ifNot(221, 'Cannot quit properly', 'The server timed out while trying to quit properly.'); $client->disconnect(); return true; }