Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
      • Traits
    • Reflection
    • Security
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

  • FallbackMailer
  • Message
  • MimePart
  • SendmailMailer
  • SmtpMailer

Interfaces

  • IMailer

Exceptions

  • FallbackMailerException
  • SendException
  • SmtpException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Mail;
  9: 
 10: use Nette;
 11: 
 12: 
 13: /**
 14:  * Sends emails via the SMTP server.
 15:  */
 16: class SmtpMailer implements IMailer
 17: {
 18:     use Nette\SmartObject;
 19: 
 20:     /** @var resource */
 21:     private $connection;
 22: 
 23:     /** @var string */
 24:     private $host;
 25: 
 26:     /** @var int */
 27:     private $port;
 28: 
 29:     /** @var string */
 30:     private $username;
 31: 
 32:     /** @var string */
 33:     private $password;
 34: 
 35:     /** @var string ssl | tls | (empty) */
 36:     private $secure;
 37: 
 38:     /** @var int */
 39:     private $timeout;
 40: 
 41:     /** @var resource */
 42:     private $context;
 43: 
 44:     /** @var bool */
 45:     private $persistent;
 46: 
 47:     /** @var string */
 48:     private $clientHost;
 49: 
 50: 
 51:     public function __construct(array $options = [])
 52:     {
 53:         if (isset($options['host'])) {
 54:             $this->host = $options['host'];
 55:             $this->port = isset($options['port']) ? (int) $options['port'] : null;
 56:         } else {
 57:             $this->host = ini_get('SMTP');
 58:             $this->port = (int) ini_get('smtp_port');
 59:         }
 60:         $this->username = isset($options['username']) ? $options['username'] : '';
 61:         $this->password = isset($options['password']) ? $options['password'] : '';
 62:         $this->secure = isset($options['secure']) ? $options['secure'] : '';
 63:         $this->timeout = isset($options['timeout']) ? (int) $options['timeout'] : 20;
 64:         $this->context = isset($options['context']) ? stream_context_create($options['context']) : stream_context_get_default();
 65:         if (!$this->port) {
 66:             $this->port = $this->secure === 'ssl' ? 465 : 25;
 67:         }
 68:         $this->persistent = !empty($options['persistent']);
 69:         if (isset($options['clientHost'])) {
 70:             $this->clientHost = $options['clientHost'];
 71:         } else {
 72:             $this->clientHost = isset($_SERVER['HTTP_HOST']) && preg_match('#^[\w.-]+\z#', $_SERVER['HTTP_HOST'])
 73:                 ? $_SERVER['HTTP_HOST']
 74:                 : 'localhost';
 75:         }
 76:     }
 77: 
 78: 
 79:     /**
 80:      * Sends email.
 81:      * @return void
 82:      * @throws SmtpException
 83:      */
 84:     public function send(Message $mail)
 85:     {
 86:         $tmp = clone $mail;
 87:         $tmp->setHeader('Bcc', null);
 88:         $data = $tmp->generateMessage();
 89: 
 90:         try {
 91:             if (!$this->connection) {
 92:                 $this->connect();
 93:             }
 94: 
 95:             if (($from = $mail->getHeader('Return-Path'))
 96:                 || ($from = key($mail->getHeader('From')))
 97:             ) {
 98:                 $this->write("MAIL FROM:<$from>", 250);
 99:             }
100: 
101:             foreach (array_merge(
102:                 (array) $mail->getHeader('To'),
103:                 (array) $mail->getHeader('Cc'),
104:                 (array) $mail->getHeader('Bcc')
105:             ) as $email => $name) {
106:                 $this->write("RCPT TO:<$email>", [250, 251]);
107:             }
108: 
109:             $this->write('DATA', 354);
110:             $data = preg_replace('#^\.#m', '..', $data);
111:             $this->write($data);
112:             $this->write('.', 250);
113: 
114:             if (!$this->persistent) {
115:                 $this->write('QUIT', 221);
116:                 $this->disconnect();
117:             }
118:         } catch (SmtpException $e) {
119:             if ($this->connection) {
120:                 $this->disconnect();
121:             }
122:             throw $e;
123:         }
124:     }
125: 
126: 
127:     /**
128:      * Connects and authenticates to SMTP server.
129:      * @return void
130:      */
131:     protected function connect()
132:     {
133:         $this->connection = @stream_socket_client(// @ is escalated to exception
134:             ($this->secure === 'ssl' ? 'ssl://' : '') . $this->host . ':' . $this->port,
135:             $errno, $error, $this->timeout, STREAM_CLIENT_CONNECT, $this->context
136:         );
137:         if (!$this->connection) {
138:             throw new SmtpException($error ?: error_get_last()['message'], $errno);
139:         }
140:         stream_set_timeout($this->connection, $this->timeout, 0);
141:         $this->read(); // greeting
142: 
143:         $this->write("EHLO $this->clientHost");
144:         $ehloResponse = $this->read();
145:         if ((int) $ehloResponse !== 250) {
146:             $this->write("HELO $this->clientHost", 250);
147:         }
148: 
149:         if ($this->secure === 'tls') {
150:             $this->write('STARTTLS', 220);
151:             if (!stream_socket_enable_crypto($this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
152:                 throw new SmtpException('Unable to connect via TLS.');
153:             }
154:             $this->write("EHLO $this->clientHost", 250);
155:         }
156: 
157:         if ($this->username != null && $this->password != null) {
158:             $authMechanisms = [];
159:             if (preg_match('~^250[ -]AUTH (.*)$~im', $ehloResponse, $matches)) {
160:                 $authMechanisms = explode(' ', trim($matches[1]));
161:             }
162: 
163:             if (in_array('PLAIN', $authMechanisms, true)) {
164:                 $credentials = $this->username . "\0" . $this->username . "\0" . $this->password;
165:                 $this->write('AUTH PLAIN ' . base64_encode($credentials), 235, 'PLAIN credentials');
166:             } else {
167:                 $this->write('AUTH LOGIN', 334);
168:                 $this->write(base64_encode($this->username), 334, 'username');
169:                 $this->write(base64_encode($this->password), 235, 'password');
170:             }
171:         }
172:     }
173: 
174: 
175:     /**
176:      * Disconnects from SMTP server.
177:      * @return void
178:      */
179:     protected function disconnect()
180:     {
181:         fclose($this->connection);
182:         $this->connection = null;
183:     }
184: 
185: 
186:     /**
187:      * Writes data to server and checks response against expected code if some provided.
188:      * @param  string
189:      * @param  int|int[] response code
190:      * @param  string  error message
191:      * @return void
192:      */
193:     protected function write($line, $expectedCode = null, $message = null)
194:     {
195:         fwrite($this->connection, $line . Message::EOL);
196:         if ($expectedCode) {
197:             $response = $this->read();
198:             if (!in_array((int) $response, (array) $expectedCode, true)) {
199:                 throw new SmtpException('SMTP server did not accept ' . ($message ? $message : $line) . ' with error: ' . trim($response));
200:             }
201:         }
202:     }
203: 
204: 
205:     /**
206:      * Reads response from server.
207:      * @return string
208:      */
209:     protected function read()
210:     {
211:         $s = '';
212:         while (($line = fgets($this->connection, 1000)) != null) { // intentionally ==
213:             $s .= $line;
214:             if (substr($line, 3, 1) === ' ') {
215:                 break;
216:             }
217:         }
218:         return $s;
219:     }
220: }
221: 
Nette 2.4-20180918 API API documentation generated by ApiGen 2.8.0