1 <?php |
1 <?php |
2 /** |
2 /** |
3 * PHPMailer RFC821 SMTP email transport class. |
3 * PHPMailer RFC821 SMTP email transport class. |
4 * Version 5.2.7 |
4 * PHP Version 5 |
5 * PHP version 5.0.0 |
5 * @package PHPMailer |
6 * @category PHP |
6 * @link https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project |
7 * @package PHPMailer |
7 * @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> |
8 * @link https://github.com/PHPMailer/PHPMailer/ |
|
9 * @author Marcus Bointon (coolbru) <phpmailer@synchromedia.co.uk> |
|
10 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> |
8 * @author Jim Jagielski (jimjag) <jimjag@gmail.com> |
11 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> |
9 * @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> |
12 * @copyright 2013 Marcus Bointon |
10 * @author Brent R. Matzelle (original founder) |
13 * @copyright 2004 - 2008 Andy Prevost |
11 * @copyright 2014 Marcus Bointon |
14 * @copyright 2010 - 2012 Jim Jagielski |
12 * @copyright 2010 - 2012 Jim Jagielski |
15 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) |
13 * @copyright 2004 - 2009 Andy Prevost |
|
14 * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License |
|
15 * @note This program is distributed in the hope that it will be useful - WITHOUT |
|
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
17 * FITNESS FOR A PARTICULAR PURPOSE. |
16 */ |
18 */ |
17 |
19 |
18 /** |
20 /** |
19 * PHPMailer RFC821 SMTP email transport class. |
21 * PHPMailer RFC821 SMTP email transport class. |
20 * |
22 * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. |
21 * Implements RFC 821 SMTP commands |
23 * @package PHPMailer |
22 * and provides some utility methods for sending mail to an SMTP server. |
24 * @author Chris Ryan |
23 * |
25 * @author Marcus Bointon <phpmailer@synchromedia.co.uk> |
24 * PHP Version 5.0.0 |
|
25 * |
|
26 * @category PHP |
|
27 * @package PHPMailer |
|
28 * @link https://github.com/PHPMailer/PHPMailer/blob/master/class.smtp.php |
|
29 * @author Chris Ryan <unknown@example.com> |
|
30 * @author Marcus Bointon <phpmailer@synchromedia.co.uk> |
|
31 * @license http://www.gnu.org/copyleft/lesser.html Distributed under the Lesser General Public License (LGPL) |
|
32 */ |
26 */ |
33 |
|
34 class SMTP |
27 class SMTP |
35 { |
28 { |
36 /** |
29 /** |
|
30 * The PHPMailer SMTP version number. |
|
31 * @var string |
|
32 */ |
|
33 const VERSION = '5.2.22'; |
|
34 |
|
35 /** |
|
36 * SMTP line break constant. |
|
37 * @var string |
|
38 */ |
|
39 const CRLF = "\r\n"; |
|
40 |
|
41 /** |
|
42 * The SMTP port to use if one is not specified. |
|
43 * @var integer |
|
44 */ |
|
45 const DEFAULT_SMTP_PORT = 25; |
|
46 |
|
47 /** |
|
48 * The maximum line length allowed by RFC 2822 section 2.1.1 |
|
49 * @var integer |
|
50 */ |
|
51 const MAX_LINE_LENGTH = 998; |
|
52 |
|
53 /** |
|
54 * Debug level for no output |
|
55 */ |
|
56 const DEBUG_OFF = 0; |
|
57 |
|
58 /** |
|
59 * Debug level to show client -> server messages |
|
60 */ |
|
61 const DEBUG_CLIENT = 1; |
|
62 |
|
63 /** |
|
64 * Debug level to show client -> server and server -> client messages |
|
65 */ |
|
66 const DEBUG_SERVER = 2; |
|
67 |
|
68 /** |
|
69 * Debug level to show connection status, client -> server and server -> client messages |
|
70 */ |
|
71 const DEBUG_CONNECTION = 3; |
|
72 |
|
73 /** |
|
74 * Debug level to show all messages |
|
75 */ |
|
76 const DEBUG_LOWLEVEL = 4; |
|
77 |
|
78 /** |
37 * The PHPMailer SMTP Version number. |
79 * The PHPMailer SMTP Version number. |
38 */ |
80 * @var string |
39 const VERSION = '5.2.7'; |
81 * @deprecated Use the `VERSION` constant instead |
40 |
|
41 /** |
|
42 * SMTP line break constant. |
|
43 */ |
|
44 const CRLF = "\r\n"; |
|
45 |
|
46 /** |
|
47 * The SMTP port to use if one is not specified. |
|
48 */ |
|
49 const DEFAULT_SMTP_PORT = 25; |
|
50 |
|
51 /** |
|
52 * The PHPMailer SMTP Version number. |
|
53 * @type string |
|
54 * @deprecated This should be a constant |
|
55 * @see SMTP::VERSION |
82 * @see SMTP::VERSION |
56 */ |
83 */ |
57 public $Version = '5.2.7'; |
84 public $Version = '5.2.22'; |
58 |
85 |
59 /** |
86 /** |
60 * SMTP server port number. |
87 * SMTP server port number. |
61 * @type int |
88 * @var integer |
62 * @deprecated This is only ever ued as default value, so should be a constant |
89 * @deprecated This is only ever used as a default value, so use the `DEFAULT_SMTP_PORT` constant instead |
63 * @see SMTP::DEFAULT_SMTP_PORT |
90 * @see SMTP::DEFAULT_SMTP_PORT |
64 */ |
91 */ |
65 public $SMTP_PORT = 25; |
92 public $SMTP_PORT = 25; |
66 |
93 |
67 /** |
94 /** |
68 * SMTP reply line ending |
95 * SMTP reply line ending. |
69 * @type string |
96 * @var string |
70 * @deprecated Use the class constant instead |
97 * @deprecated Use the `CRLF` constant instead |
71 * @see SMTP::CRLF |
98 * @see SMTP::CRLF |
72 */ |
99 */ |
73 public $CRLF = "\r\n"; |
100 public $CRLF = "\r\n"; |
74 |
101 |
75 /** |
102 /** |
76 * Debug output level. |
103 * Debug output level. |
77 * Options: 0 for no output, 1 for commands, 2 for data and commands |
104 * Options: |
78 * @type int |
105 * * self::DEBUG_OFF (`0`) No debug output, default |
79 */ |
106 * * self::DEBUG_CLIENT (`1`) Client commands |
80 public $do_debug = 0; |
107 * * self::DEBUG_SERVER (`2`) Client commands and server responses |
81 |
108 * * self::DEBUG_CONNECTION (`3`) As DEBUG_SERVER plus connection status |
82 /** |
109 * * self::DEBUG_LOWLEVEL (`4`) Low-level data output, all messages |
83 * The function/method to use for debugging output. |
110 * @var integer |
84 * Options: 'echo', 'html' or 'error_log' |
111 */ |
85 * @type string |
112 public $do_debug = self::DEBUG_OFF; |
|
113 |
|
114 /** |
|
115 * How to handle debug output. |
|
116 * Options: |
|
117 * * `echo` Output plain-text as-is, appropriate for CLI |
|
118 * * `html` Output escaped, line breaks converted to `<br>`, appropriate for browser output |
|
119 * * `error_log` Output to error log as configured in php.ini |
|
120 * |
|
121 * Alternatively, you can provide a callable expecting two params: a message string and the debug level: |
|
122 * <code> |
|
123 * $smtp->Debugoutput = function($str, $level) {echo "debug level $level; message: $str";}; |
|
124 * </code> |
|
125 * @var string|callable |
86 */ |
126 */ |
87 public $Debugoutput = 'echo'; |
127 public $Debugoutput = 'echo'; |
88 |
128 |
89 /** |
129 /** |
90 * Whether to use VERP. |
130 * Whether to use VERP. |
91 * @type bool |
131 * @link http://en.wikipedia.org/wiki/Variable_envelope_return_path |
|
132 * @link http://www.postfix.org/VERP_README.html Info on VERP |
|
133 * @var boolean |
92 */ |
134 */ |
93 public $do_verp = false; |
135 public $do_verp = false; |
94 |
136 |
95 /** |
137 /** |
96 * The SMTP timeout value for reads, in seconds. |
138 * The timeout value for connection, in seconds. |
97 * @type int |
139 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 |
98 */ |
140 * This needs to be quite high to function correctly with hosts using greetdelay as an anti-spam measure. |
99 public $Timeout = 15; |
141 * @link http://tools.ietf.org/html/rfc2821#section-4.5.3.2 |
100 |
142 * @var integer |
101 /** |
143 */ |
102 * The SMTP timelimit value for reads, in seconds. |
144 public $Timeout = 300; |
103 * @type int |
145 |
104 */ |
146 /** |
105 public $Timelimit = 30; |
147 * How long to wait for commands to complete, in seconds. |
|
148 * Default of 5 minutes (300sec) is from RFC2821 section 4.5.3.2 |
|
149 * @var integer |
|
150 */ |
|
151 public $Timelimit = 300; |
|
152 |
|
153 /** |
|
154 * @var array patterns to extract smtp transaction id from smtp reply |
|
155 * Only first capture group will be use, use non-capturing group to deal with it |
|
156 * Extend this class to override this property to fulfil your needs. |
|
157 */ |
|
158 protected $smtp_transaction_id_patterns = array( |
|
159 'exim' => '/[0-9]{3} OK id=(.*)/', |
|
160 'sendmail' => '/[0-9]{3} 2.0.0 (.*) Message/', |
|
161 'postfix' => '/[0-9]{3} 2.0.0 Ok: queued as (.*)/' |
|
162 ); |
106 |
163 |
107 /** |
164 /** |
108 * The socket for the server connection. |
165 * The socket for the server connection. |
109 * @type resource |
166 * @var resource |
110 */ |
167 */ |
111 protected $smtp_conn; |
168 protected $smtp_conn; |
112 |
169 |
113 /** |
170 /** |
114 * Error message, if any, for the last call. |
171 * Error information, if any, for the last SMTP command. |
115 * @type string |
172 * @var array |
116 */ |
173 */ |
117 protected $error = ''; |
174 protected $error = array( |
|
175 'error' => '', |
|
176 'detail' => '', |
|
177 'smtp_code' => '', |
|
178 'smtp_code_ex' => '' |
|
179 ); |
118 |
180 |
119 /** |
181 /** |
120 * The reply the server sent to us for HELO. |
182 * The reply the server sent to us for HELO. |
121 * @type string |
183 * If null, no HELO string has yet been received. |
122 */ |
184 * @var string|null |
123 protected $helo_rply = ''; |
185 */ |
|
186 protected $helo_rply = null; |
|
187 |
|
188 /** |
|
189 * The set of SMTP extensions sent in reply to EHLO command. |
|
190 * Indexes of the array are extension names. |
|
191 * Value at index 'HELO' or 'EHLO' (according to command that was sent) |
|
192 * represents the server name. In case of HELO it is the only element of the array. |
|
193 * Other values can be boolean TRUE or an array containing extension options. |
|
194 * If null, no HELO/EHLO string has yet been received. |
|
195 * @var array|null |
|
196 */ |
|
197 protected $server_caps = null; |
124 |
198 |
125 /** |
199 /** |
126 * The most recent reply received from the server. |
200 * The most recent reply received from the server. |
127 * @type string |
201 * @var string |
128 */ |
202 */ |
129 protected $last_reply = ''; |
203 protected $last_reply = ''; |
130 |
204 |
131 /** |
205 /** |
132 * Constructor. |
|
133 * @access public |
|
134 */ |
|
135 public function __construct() |
|
136 { |
|
137 $this->smtp_conn = 0; |
|
138 $this->error = null; |
|
139 $this->helo_rply = null; |
|
140 |
|
141 $this->do_debug = 0; |
|
142 } |
|
143 |
|
144 /** |
|
145 * Output debugging info via a user-selected method. |
206 * Output debugging info via a user-selected method. |
|
207 * @see SMTP::$Debugoutput |
|
208 * @see SMTP::$do_debug |
146 * @param string $str Debug string to output |
209 * @param string $str Debug string to output |
|
210 * @param integer $level The debug level of this message; see DEBUG_* constants |
147 * @return void |
211 * @return void |
148 */ |
212 */ |
149 protected function edebug($str) |
213 protected function edebug($str, $level = 0) |
150 { |
214 { |
|
215 if ($level > $this->do_debug) { |
|
216 return; |
|
217 } |
|
218 //Avoid clash with built-in function names |
|
219 if (!in_array($this->Debugoutput, array('error_log', 'html', 'echo')) and is_callable($this->Debugoutput)) { |
|
220 call_user_func($this->Debugoutput, $str, $level); |
|
221 return; |
|
222 } |
151 switch ($this->Debugoutput) { |
223 switch ($this->Debugoutput) { |
152 case 'error_log': |
224 case 'error_log': |
153 //Don't output, just log |
225 //Don't output, just log |
154 error_log($str); |
226 error_log($str); |
155 break; |
227 break; |
162 ) |
234 ) |
163 . "<br>\n"; |
235 . "<br>\n"; |
164 break; |
236 break; |
165 case 'echo': |
237 case 'echo': |
166 default: |
238 default: |
167 //Just echoes whatever was received |
239 //Normalize line breaks |
168 echo $str; |
240 $str = preg_replace('/(\r\n|\r|\n)/ms', "\n", $str); |
|
241 echo gmdate('Y-m-d H:i:s') . "\t" . str_replace( |
|
242 "\n", |
|
243 "\n \t ", |
|
244 trim($str) |
|
245 )."\n"; |
169 } |
246 } |
170 } |
247 } |
171 |
248 |
172 /** |
249 /** |
173 * Connect to an SMTP server. |
250 * Connect to an SMTP server. |
174 * @param string $host SMTP server IP or host name |
251 * @param string $host SMTP server IP or host name |
175 * @param int $port The port number to connect to |
252 * @param integer $port The port number to connect to |
176 * @param int $timeout How long to wait for the connection to open |
253 * @param integer $timeout How long to wait for the connection to open |
177 * @param array $options An array of options for stream_context_create() |
254 * @param array $options An array of options for stream_context_create() |
178 * @access public |
255 * @access public |
179 * @return bool |
256 * @return boolean |
180 */ |
257 */ |
181 public function connect($host, $port = null, $timeout = 30, $options = array()) |
258 public function connect($host, $port = null, $timeout = 30, $options = array()) |
182 { |
259 { |
|
260 static $streamok; |
|
261 //This is enabled by default since 5.0.0 but some providers disable it |
|
262 //Check this once and cache the result |
|
263 if (is_null($streamok)) { |
|
264 $streamok = function_exists('stream_socket_client'); |
|
265 } |
183 // Clear errors to avoid confusion |
266 // Clear errors to avoid confusion |
184 $this->error = null; |
267 $this->setError(''); |
185 |
|
186 // Make sure we are __not__ connected |
268 // Make sure we are __not__ connected |
187 if ($this->connected()) { |
269 if ($this->connected()) { |
188 // Already connected, generate error |
270 // Already connected, generate error |
189 $this->error = array('error' => 'Already connected to a server'); |
271 $this->setError('Already connected to a server'); |
190 return false; |
272 return false; |
191 } |
273 } |
192 |
|
193 if (empty($port)) { |
274 if (empty($port)) { |
194 $port = self::DEFAULT_SMTP_PORT; |
275 $port = self::DEFAULT_SMTP_PORT; |
195 } |
276 } |
196 |
|
197 // Connect to the SMTP server |
277 // Connect to the SMTP server |
|
278 $this->edebug( |
|
279 "Connection: opening to $host:$port, timeout=$timeout, options=".var_export($options, true), |
|
280 self::DEBUG_CONNECTION |
|
281 ); |
198 $errno = 0; |
282 $errno = 0; |
199 $errstr = ''; |
283 $errstr = ''; |
200 $socket_context = stream_context_create($options); |
284 if ($streamok) { |
201 //Suppress errors; connection failures are handled at a higher level |
285 $socket_context = stream_context_create($options); |
202 $this->smtp_conn = @stream_socket_client( |
286 set_error_handler(array($this, 'errorHandler')); |
203 $host . ":" . $port, |
287 $this->smtp_conn = stream_socket_client( |
204 $errno, |
288 $host . ":" . $port, |
205 $errstr, |
289 $errno, |
206 $timeout, |
290 $errstr, |
207 STREAM_CLIENT_CONNECT, |
291 $timeout, |
208 $socket_context |
292 STREAM_CLIENT_CONNECT, |
209 ); |
293 $socket_context |
210 |
294 ); |
|
295 restore_error_handler(); |
|
296 } else { |
|
297 //Fall back to fsockopen which should work in more places, but is missing some features |
|
298 $this->edebug( |
|
299 "Connection: stream_socket_client not available, falling back to fsockopen", |
|
300 self::DEBUG_CONNECTION |
|
301 ); |
|
302 set_error_handler(array($this, 'errorHandler')); |
|
303 $this->smtp_conn = fsockopen( |
|
304 $host, |
|
305 $port, |
|
306 $errno, |
|
307 $errstr, |
|
308 $timeout |
|
309 ); |
|
310 restore_error_handler(); |
|
311 } |
211 // Verify we connected properly |
312 // Verify we connected properly |
212 if (empty($this->smtp_conn)) { |
313 if (!is_resource($this->smtp_conn)) { |
213 $this->error = array( |
314 $this->setError( |
214 'error' => 'Failed to connect to server', |
315 'Failed to connect to server', |
215 'errno' => $errno, |
316 $errno, |
216 'errstr' => $errstr |
317 $errstr |
217 ); |
318 ); |
218 if ($this->do_debug >= 1) { |
319 $this->edebug( |
219 $this->edebug( |
320 'SMTP ERROR: ' . $this->error['error'] |
220 'SMTP -> ERROR: ' . $this->error['error'] |
321 . ": $errstr ($errno)", |
221 . ": $errstr ($errno)" |
322 self::DEBUG_CLIENT |
222 ); |
323 ); |
223 } |
|
224 return false; |
324 return false; |
225 } |
325 } |
226 |
326 $this->edebug('Connection: opened', self::DEBUG_CONNECTION); |
227 // SMTP server can take longer to respond, give longer timeout for first read |
327 // SMTP server can take longer to respond, give longer timeout for first read |
228 // Windows does not have support for this timeout function |
328 // Windows does not have support for this timeout function |
229 if (substr(PHP_OS, 0, 3) != 'WIN') { |
329 if (substr(PHP_OS, 0, 3) != 'WIN') { |
230 $max = ini_get('max_execution_time'); |
330 $max = ini_get('max_execution_time'); |
231 if ($max != 0 && $timeout > $max) { // Don't bother if unlimited |
331 // Don't bother if unlimited |
|
332 if ($max != 0 && $timeout > $max) { |
232 @set_time_limit($timeout); |
333 @set_time_limit($timeout); |
233 } |
334 } |
234 stream_set_timeout($this->smtp_conn, $timeout, 0); |
335 stream_set_timeout($this->smtp_conn, $timeout, 0); |
235 } |
336 } |
236 |
|
237 // Get any announcement |
337 // Get any announcement |
238 $announce = $this->get_lines(); |
338 $announce = $this->get_lines(); |
239 |
339 $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); |
240 if ($this->do_debug >= 2) { |
|
241 $this->edebug('SMTP -> FROM SERVER:' . $announce); |
|
242 } |
|
243 |
|
244 return true; |
340 return true; |
245 } |
341 } |
246 |
342 |
247 /** |
343 /** |
248 * Initiate a TLS (encrypted) session. |
344 * Initiate a TLS (encrypted) session. |
249 * @access public |
345 * @access public |
250 * @return bool |
346 * @return boolean |
251 */ |
347 */ |
252 public function startTLS() |
348 public function startTLS() |
253 { |
349 { |
254 if (!$this->sendCommand("STARTTLS", "STARTTLS", 220)) { |
350 if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) { |
255 return false; |
351 return false; |
256 } |
352 } |
|
353 |
|
354 //Allow the best TLS version(s) we can |
|
355 $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT; |
|
356 |
|
357 //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT |
|
358 //so add them back in manually if we can |
|
359 if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) { |
|
360 $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT; |
|
361 $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; |
|
362 } |
|
363 |
257 // Begin encrypted connection |
364 // Begin encrypted connection |
258 if (!stream_socket_enable_crypto( |
365 if (!stream_socket_enable_crypto( |
259 $this->smtp_conn, |
366 $this->smtp_conn, |
260 true, |
367 true, |
261 STREAM_CRYPTO_METHOD_TLS_CLIENT |
368 $crypto_method |
262 ) |
369 )) { |
263 ) { |
|
264 return false; |
370 return false; |
265 } |
371 } |
266 return true; |
372 return true; |
267 } |
373 } |
268 |
374 |
269 /** |
375 /** |
270 * Perform SMTP authentication. |
376 * Perform SMTP authentication. |
271 * Must be run after hello(). |
377 * Must be run after hello(). |
272 * @see hello() |
378 * @see hello() |
273 * @param string $username The user name |
379 * @param string $username The user name |
274 * @param string $password The password |
380 * @param string $password The password |
275 * @param string $authtype The auth type (PLAIN, LOGIN, NTLM, CRAM-MD5) |
381 * @param string $authtype The auth type (PLAIN, LOGIN, CRAM-MD5) |
276 * @param string $realm The auth realm for NTLM |
382 * @param string $realm The auth realm for NTLM |
277 * @param string $workstation The auth workstation for NTLM |
383 * @param string $workstation The auth workstation for NTLM |
278 * @access public |
384 * @param null|OAuth $OAuth An optional OAuth instance (@see PHPMailerOAuth) |
279 * @return bool True if successfully authenticated. |
385 * @return bool True if successfully authenticated.* @access public |
280 */ |
386 */ |
281 public function authenticate( |
387 public function authenticate( |
282 $username, |
388 $username, |
283 $password, |
389 $password, |
284 $authtype = 'LOGIN', |
390 $authtype = null, |
285 $realm = '', |
391 $realm = '', |
286 $workstation = '' |
392 $workstation = '', |
|
393 $OAuth = null |
287 ) { |
394 ) { |
288 if (empty($authtype)) { |
395 if (!$this->server_caps) { |
|
396 $this->setError('Authentication is not allowed before HELO/EHLO'); |
|
397 return false; |
|
398 } |
|
399 |
|
400 if (array_key_exists('EHLO', $this->server_caps)) { |
|
401 // SMTP extensions are available. Let's try to find a proper authentication method |
|
402 |
|
403 if (!array_key_exists('AUTH', $this->server_caps)) { |
|
404 $this->setError('Authentication is not allowed at this stage'); |
|
405 // 'at this stage' means that auth may be allowed after the stage changes |
|
406 // e.g. after STARTTLS |
|
407 return false; |
|
408 } |
|
409 |
|
410 self::edebug('Auth method requested: ' . ($authtype ? $authtype : 'UNKNOWN'), self::DEBUG_LOWLEVEL); |
|
411 self::edebug( |
|
412 'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']), |
|
413 self::DEBUG_LOWLEVEL |
|
414 ); |
|
415 |
|
416 if (empty($authtype)) { |
|
417 foreach (array('CRAM-MD5', 'LOGIN', 'PLAIN') as $method) { |
|
418 if (in_array($method, $this->server_caps['AUTH'])) { |
|
419 $authtype = $method; |
|
420 break; |
|
421 } |
|
422 } |
|
423 if (empty($authtype)) { |
|
424 $this->setError('No supported authentication methods found'); |
|
425 return false; |
|
426 } |
|
427 self::edebug('Auth method selected: '.$authtype, self::DEBUG_LOWLEVEL); |
|
428 } |
|
429 |
|
430 if (!in_array($authtype, $this->server_caps['AUTH'])) { |
|
431 $this->setError("The requested authentication method \"$authtype\" is not supported by the server"); |
|
432 return false; |
|
433 } |
|
434 } elseif (empty($authtype)) { |
289 $authtype = 'LOGIN'; |
435 $authtype = 'LOGIN'; |
290 } |
436 } |
291 |
|
292 switch ($authtype) { |
437 switch ($authtype) { |
293 case 'PLAIN': |
438 case 'PLAIN': |
294 // Start authentication |
439 // Start authentication |
295 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { |
440 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { |
296 return false; |
441 return false; |
409 // RFC 2104 HMAC implementation for php. |
503 // RFC 2104 HMAC implementation for php. |
410 // Creates an md5 HMAC. |
504 // Creates an md5 HMAC. |
411 // Eliminates the need to install mhash to compute a HMAC |
505 // Eliminates the need to install mhash to compute a HMAC |
412 // by Lance Rushing |
506 // by Lance Rushing |
413 |
507 |
414 $b = 64; // byte length for md5 |
508 $bytelen = 64; // byte length for md5 |
415 if (strlen($key) > $b) { |
509 if (strlen($key) > $bytelen) { |
416 $key = pack('H*', md5($key)); |
510 $key = pack('H*', md5($key)); |
417 } |
511 } |
418 $key = str_pad($key, $b, chr(0x00)); |
512 $key = str_pad($key, $bytelen, chr(0x00)); |
419 $ipad = str_pad('', $b, chr(0x36)); |
513 $ipad = str_pad('', $bytelen, chr(0x36)); |
420 $opad = str_pad('', $b, chr(0x5c)); |
514 $opad = str_pad('', $bytelen, chr(0x5c)); |
421 $k_ipad = $key ^ $ipad; |
515 $k_ipad = $key ^ $ipad; |
422 $k_opad = $key ^ $opad; |
516 $k_opad = $key ^ $opad; |
423 |
517 |
424 return md5($k_opad . pack('H*', md5($k_ipad . $data))); |
518 return md5($k_opad . pack('H*', md5($k_ipad . $data))); |
425 } |
519 } |
426 |
520 |
427 /** |
521 /** |
428 * Check connection state. |
522 * Check connection state. |
429 * @access public |
523 * @access public |
430 * @return bool True if connected. |
524 * @return boolean True if connected. |
431 */ |
525 */ |
432 public function connected() |
526 public function connected() |
433 { |
527 { |
434 if (!empty($this->smtp_conn)) { |
528 if (is_resource($this->smtp_conn)) { |
435 $sock_status = stream_get_meta_data($this->smtp_conn); |
529 $sock_status = stream_get_meta_data($this->smtp_conn); |
436 if ($sock_status['eof']) { |
530 if ($sock_status['eof']) { |
437 // the socket is valid but we are not connected |
531 // The socket is valid but we are not connected |
438 if ($this->do_debug >= 1) { |
532 $this->edebug( |
439 $this->edebug( |
533 'SMTP NOTICE: EOF caught while checking if connected', |
440 'SMTP -> NOTICE: EOF caught while checking if connected' |
534 self::DEBUG_CLIENT |
441 ); |
535 ); |
442 } |
|
443 $this->close(); |
536 $this->close(); |
444 return false; |
537 return false; |
445 } |
538 } |
446 return true; // everything looks good |
539 return true; // everything looks good |
447 } |
540 } |
474 * on a single line followed by a <CRLF> with the message headers |
569 * on a single line followed by a <CRLF> with the message headers |
475 * and the message body being separated by and additional <CRLF>. |
570 * and the message body being separated by and additional <CRLF>. |
476 * Implements rfc 821: DATA <CRLF> |
571 * Implements rfc 821: DATA <CRLF> |
477 * @param string $msg_data Message data to send |
572 * @param string $msg_data Message data to send |
478 * @access public |
573 * @access public |
479 * @return bool |
574 * @return boolean |
480 */ |
575 */ |
481 public function data($msg_data) |
576 public function data($msg_data) |
482 { |
577 { |
|
578 //This will use the standard timelimit |
483 if (!$this->sendCommand('DATA', 'DATA', 354)) { |
579 if (!$this->sendCommand('DATA', 'DATA', 354)) { |
484 return false; |
580 return false; |
485 } |
581 } |
486 |
582 |
487 /* The server is ready to accept data! |
583 /* The server is ready to accept data! |
488 * according to rfc821 we should not send more than 1000 |
584 * According to rfc821 we should not send more than 1000 characters on a single line (including the CRLF) |
489 * including the CRLF |
585 * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into |
490 * characters on a single line so we will break the data up |
586 * smaller lines to fit within the limit. |
491 * into lines by \r and/or \n then if needed we will break |
587 * We will also look for lines that start with a '.' and prepend an additional '.'. |
492 * each of those into smaller lines to fit within the limit. |
588 * NOTE: this does not count towards line-length limit. |
493 * in addition we will be looking for lines that start with |
|
494 * a period '.' and append and additional period '.' to that |
|
495 * line. NOTE: this does not count towards limit. |
|
496 */ |
589 */ |
497 |
590 |
498 // Normalize the line breaks before exploding |
591 // Normalize line breaks before exploding |
499 $msg_data = str_replace("\r\n", "\n", $msg_data); |
592 $lines = explode("\n", str_replace(array("\r\n", "\r"), "\n", $msg_data)); |
500 $msg_data = str_replace("\r", "\n", $msg_data); |
593 |
501 $lines = explode("\n", $msg_data); |
594 /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field |
502 |
595 * of the first line (':' separated) does not contain a space then it _should_ be a header and we will |
503 /* We need to find a good way to determine if headers are |
596 * process all lines before a blank line as headers. |
504 * in the msg_data or if it is a straight msg body |
|
505 * currently I am assuming rfc822 definitions of msg headers |
|
506 * and if the first field of the first line (':' separated) |
|
507 * does not contain a space then it _should_ be a header |
|
508 * and we can process all lines before a blank "" line as |
|
509 * headers. |
|
510 */ |
597 */ |
511 |
598 |
512 $field = substr($lines[0], 0, strpos($lines[0], ':')); |
599 $field = substr($lines[0], 0, strpos($lines[0], ':')); |
513 $in_headers = false; |
600 $in_headers = false; |
514 if (!empty($field) && !strstr($field, ' ')) { |
601 if (!empty($field) && strpos($field, ' ') === false) { |
515 $in_headers = true; |
602 $in_headers = true; |
516 } |
603 } |
517 |
604 |
518 //RFC 2822 section 2.1.1 limit |
|
519 $max_line_length = 998; |
|
520 |
|
521 foreach ($lines as $line) { |
605 foreach ($lines as $line) { |
522 $lines_out = null; |
606 $lines_out = array(); |
523 if ($line == '' && $in_headers) { |
607 if ($in_headers and $line == '') { |
524 $in_headers = false; |
608 $in_headers = false; |
525 } |
609 } |
526 // ok we need to break this line up into several smaller lines |
610 //Break this line up into several smaller lines if it's too long |
527 while (strlen($line) > $max_line_length) { |
611 //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len), |
528 $pos = strrpos(substr($line, 0, $max_line_length), ' '); |
612 while (isset($line[self::MAX_LINE_LENGTH])) { |
529 |
613 //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on |
530 // Patch to fix DOS attack |
614 //so as to avoid breaking in the middle of a word |
|
615 $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' '); |
|
616 //Deliberately matches both false and 0 |
531 if (!$pos) { |
617 if (!$pos) { |
532 $pos = $max_line_length - 1; |
618 //No nice break found, add a hard break |
|
619 $pos = self::MAX_LINE_LENGTH - 1; |
533 $lines_out[] = substr($line, 0, $pos); |
620 $lines_out[] = substr($line, 0, $pos); |
534 $line = substr($line, $pos); |
621 $line = substr($line, $pos); |
535 } else { |
622 } else { |
|
623 //Break at the found point |
536 $lines_out[] = substr($line, 0, $pos); |
624 $lines_out[] = substr($line, 0, $pos); |
|
625 //Move along by the amount we dealt with |
537 $line = substr($line, $pos + 1); |
626 $line = substr($line, $pos + 1); |
538 } |
627 } |
539 |
628 //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1 |
540 /* If processing headers add a LWSP-char to the front of new line |
|
541 * rfc822 on long msg headers |
|
542 */ |
|
543 if ($in_headers) { |
629 if ($in_headers) { |
544 $line = "\t" . $line; |
630 $line = "\t" . $line; |
545 } |
631 } |
546 } |
632 } |
547 $lines_out[] = $line; |
633 $lines_out[] = $line; |
548 |
634 |
549 // send the lines to the server |
635 //Send the lines to the server |
550 while (list(, $line_out) = @each($lines_out)) { |
636 foreach ($lines_out as $line_out) { |
551 if (strlen($line_out) > 0) { |
637 //RFC2821 section 4.5.2 |
552 if (substr($line_out, 0, 1) == '.') { |
638 if (!empty($line_out) and $line_out[0] == '.') { |
553 $line_out = '.' . $line_out; |
639 $line_out = '.' . $line_out; |
554 } |
|
555 } |
640 } |
556 $this->client_send($line_out . self::CRLF); |
641 $this->client_send($line_out . self::CRLF); |
557 } |
642 } |
558 } |
643 } |
559 |
644 |
560 // Message data has been sent, complete the command |
645 //Message data has been sent, complete the command |
561 return $this->sendCommand('DATA END', '.', 250); |
646 //Increase timelimit for end of DATA command |
|
647 $savetimelimit = $this->Timelimit; |
|
648 $this->Timelimit = $this->Timelimit * 2; |
|
649 $result = $this->sendCommand('DATA END', '.', 250); |
|
650 //Restore timelimit |
|
651 $this->Timelimit = $savetimelimit; |
|
652 return $result; |
562 } |
653 } |
563 |
654 |
564 /** |
655 /** |
565 * Send an SMTP HELO or EHLO command. |
656 * Send an SMTP HELO or EHLO command. |
566 * Used to identify the sending server to the receiving server. |
657 * Used to identify the sending server to the receiving server. |
567 * This makes sure that client and server are in a known state. |
658 * This makes sure that client and server are in a known state. |
568 * Implements from RFC 821: HELO <SP> <domain> <CRLF> |
659 * Implements RFC 821: HELO <SP> <domain> <CRLF> |
569 * and RFC 2821 EHLO. |
660 * and RFC 2821 EHLO. |
570 * @param string $host The host name or IP to connect to |
661 * @param string $host The host name or IP to connect to |
571 * @access public |
662 * @access public |
572 * @return bool |
663 * @return boolean |
573 */ |
664 */ |
574 public function hello($host = '') |
665 public function hello($host = '') |
575 { |
666 { |
576 // Try extended hello first (RFC 2821) |
667 //Try extended hello first (RFC 2821) |
577 if (!$this->sendHello('EHLO', $host)) { |
668 return (boolean)($this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host)); |
578 if (!$this->sendHello('HELO', $host)) { |
|
579 return false; |
|
580 } |
|
581 } |
|
582 |
|
583 return true; |
|
584 } |
669 } |
585 |
670 |
586 /** |
671 /** |
587 * Send an SMTP HELO or EHLO command. |
672 * Send an SMTP HELO or EHLO command. |
588 * Low-level implementation used by hello() |
673 * Low-level implementation used by hello() |
589 * @see hello() |
674 * @see hello() |
590 * @param string $hello The HELO string |
675 * @param string $hello The HELO string |
591 * @param string $host The hostname to say we are |
676 * @param string $host The hostname to say we are |
592 * @access protected |
677 * @access protected |
593 * @return bool |
678 * @return boolean |
594 */ |
679 */ |
595 protected function sendHello($hello, $host) |
680 protected function sendHello($hello, $host) |
596 { |
681 { |
597 $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); |
682 $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250); |
598 $this->helo_rply = $this->last_reply; |
683 $this->helo_rply = $this->last_reply; |
|
684 if ($noerror) { |
|
685 $this->parseHelloFields($hello); |
|
686 } else { |
|
687 $this->server_caps = null; |
|
688 } |
599 return $noerror; |
689 return $noerror; |
|
690 } |
|
691 |
|
692 /** |
|
693 * Parse a reply to HELO/EHLO command to discover server extensions. |
|
694 * In case of HELO, the only parameter that can be discovered is a server name. |
|
695 * @access protected |
|
696 * @param string $type - 'HELO' or 'EHLO' |
|
697 */ |
|
698 protected function parseHelloFields($type) |
|
699 { |
|
700 $this->server_caps = array(); |
|
701 $lines = explode("\n", $this->helo_rply); |
|
702 |
|
703 foreach ($lines as $n => $s) { |
|
704 //First 4 chars contain response code followed by - or space |
|
705 $s = trim(substr($s, 4)); |
|
706 if (empty($s)) { |
|
707 continue; |
|
708 } |
|
709 $fields = explode(' ', $s); |
|
710 if (!empty($fields)) { |
|
711 if (!$n) { |
|
712 $name = $type; |
|
713 $fields = $fields[0]; |
|
714 } else { |
|
715 $name = array_shift($fields); |
|
716 switch ($name) { |
|
717 case 'SIZE': |
|
718 $fields = ($fields ? $fields[0] : 0); |
|
719 break; |
|
720 case 'AUTH': |
|
721 if (!is_array($fields)) { |
|
722 $fields = array(); |
|
723 } |
|
724 break; |
|
725 default: |
|
726 $fields = true; |
|
727 } |
|
728 } |
|
729 $this->server_caps[$name] = $fields; |
|
730 } |
|
731 } |
600 } |
732 } |
601 |
733 |
602 /** |
734 /** |
603 * Send an SMTP MAIL command. |
735 * Send an SMTP MAIL command. |
604 * Starts a mail transaction from the email address specified in |
736 * Starts a mail transaction from the email address specified in |
622 |
754 |
623 /** |
755 /** |
624 * Send an SMTP QUIT command. |
756 * Send an SMTP QUIT command. |
625 * Closes the socket if there is no error or the $close_on_error argument is true. |
757 * Closes the socket if there is no error or the $close_on_error argument is true. |
626 * Implements from rfc 821: QUIT <CRLF> |
758 * Implements from rfc 821: QUIT <CRLF> |
627 * @param bool $close_on_error Should the connection close if an error occurs? |
759 * @param boolean $close_on_error Should the connection close if an error occurs? |
628 * @access public |
760 * @access public |
629 * @return bool |
761 * @return boolean |
630 */ |
762 */ |
631 public function quit($close_on_error = true) |
763 public function quit($close_on_error = true) |
632 { |
764 { |
633 $noerror = $this->sendCommand('QUIT', 'QUIT', 221); |
765 $noerror = $this->sendCommand('QUIT', 'QUIT', 221); |
634 $e = $this->error; //Save any error |
766 $err = $this->error; //Save any error |
635 if ($noerror or $close_on_error) { |
767 if ($noerror or $close_on_error) { |
636 $this->close(); |
768 $this->close(); |
637 $this->error = $e; //Restore any error from the quit command |
769 $this->error = $err; //Restore any error from the quit command |
638 } |
770 } |
639 return $noerror; |
771 return $noerror; |
640 } |
772 } |
641 |
773 |
642 /** |
774 /** |
643 * Send an SMTP RCPT command. |
775 * Send an SMTP RCPT command. |
644 * Sets the TO argument to $to. |
776 * Sets the TO argument to $toaddr. |
645 * Returns true if the recipient was accepted false if it was rejected. |
777 * Returns true if the recipient was accepted false if it was rejected. |
646 * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> |
778 * Implements from rfc 821: RCPT <SP> TO:<forward-path> <CRLF> |
647 * @param string $to The address the message is being sent to |
779 * @param string $address The address the message is being sent to |
648 * @access public |
780 * @access public |
649 * @return bool |
781 * @return boolean |
650 */ |
782 */ |
651 public function recipient($to) |
783 public function recipient($address) |
652 { |
784 { |
653 return $this->sendCommand( |
785 return $this->sendCommand( |
654 'RCPT TO ', |
786 'RCPT TO', |
655 'RCPT TO:<' . $to . '>', |
787 'RCPT TO:<' . $address . '>', |
656 array(250, 251) |
788 array(250, 251) |
657 ); |
789 ); |
658 } |
790 } |
659 |
791 |
660 /** |
792 /** |
661 * Send an SMTP RSET command. |
793 * Send an SMTP RSET command. |
662 * Abort any transaction that is currently in progress. |
794 * Abort any transaction that is currently in progress. |
663 * Implements rfc 821: RSET <CRLF> |
795 * Implements rfc 821: RSET <CRLF> |
664 * @access public |
796 * @access public |
665 * @return bool True on success. |
797 * @return boolean True on success. |
666 */ |
798 */ |
667 public function reset() |
799 public function reset() |
668 { |
800 { |
669 return $this->sendCommand('RSET', 'RSET', 250); |
801 return $this->sendCommand('RSET', 'RSET', 250); |
670 } |
802 } |
671 |
803 |
672 /** |
804 /** |
673 * Send a command to an SMTP server and check its return code. |
805 * Send a command to an SMTP server and check its return code. |
674 * @param string $command The command name - not sent to the server |
806 * @param string $command The command name - not sent to the server |
675 * @param string $commandstring The actual command to send |
807 * @param string $commandstring The actual command to send |
676 * @param int|array $expect One or more expected integer success codes |
808 * @param integer|array $expect One or more expected integer success codes |
677 * @access protected |
809 * @access protected |
678 * @return bool True on success. |
810 * @return boolean True on success. |
679 */ |
811 */ |
680 protected function sendCommand($command, $commandstring, $expect) |
812 protected function sendCommand($command, $commandstring, $expect) |
681 { |
813 { |
682 if (!$this->connected()) { |
814 if (!$this->connected()) { |
683 $this->error = array( |
815 $this->setError("Called $command without being connected"); |
684 "error" => "Called $command without being connected" |
816 return false; |
|
817 } |
|
818 //Reject line breaks in all commands |
|
819 if (strpos($commandstring, "\n") !== false or strpos($commandstring, "\r") !== false) { |
|
820 $this->setError("Command '$command' contained line breaks"); |
|
821 return false; |
|
822 } |
|
823 $this->client_send($commandstring . self::CRLF); |
|
824 |
|
825 $this->last_reply = $this->get_lines(); |
|
826 // Fetch SMTP code and possible error code explanation |
|
827 $matches = array(); |
|
828 if (preg_match("/^([0-9]{3})[ -](?:([0-9]\\.[0-9]\\.[0-9]) )?/", $this->last_reply, $matches)) { |
|
829 $code = $matches[1]; |
|
830 $code_ex = (count($matches) > 2 ? $matches[2] : null); |
|
831 // Cut off error code from each response line |
|
832 $detail = preg_replace( |
|
833 "/{$code}[ -]".($code_ex ? str_replace('.', '\\.', $code_ex).' ' : '')."/m", |
|
834 '', |
|
835 $this->last_reply |
|
836 ); |
|
837 } else { |
|
838 // Fall back to simple parsing if regex fails |
|
839 $code = substr($this->last_reply, 0, 3); |
|
840 $code_ex = null; |
|
841 $detail = substr($this->last_reply, 4); |
|
842 } |
|
843 |
|
844 $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); |
|
845 |
|
846 if (!in_array($code, (array)$expect)) { |
|
847 $this->setError( |
|
848 "$command command failed", |
|
849 $detail, |
|
850 $code, |
|
851 $code_ex |
|
852 ); |
|
853 $this->edebug( |
|
854 'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply, |
|
855 self::DEBUG_CLIENT |
685 ); |
856 ); |
686 return false; |
857 return false; |
687 } |
858 } |
688 $this->client_send($commandstring . self::CRLF); |
859 |
689 |
860 $this->setError(''); |
690 $reply = $this->get_lines(); |
|
691 $code = substr($reply, 0, 3); |
|
692 |
|
693 if ($this->do_debug >= 2) { |
|
694 $this->edebug('SMTP -> FROM SERVER:' . $reply); |
|
695 } |
|
696 |
|
697 if (!in_array($code, (array)$expect)) { |
|
698 $this->last_reply = null; |
|
699 $this->error = array( |
|
700 "error" => "$command command failed", |
|
701 "smtp_code" => $code, |
|
702 "detail" => substr($reply, 4) |
|
703 ); |
|
704 if ($this->do_debug >= 1) { |
|
705 $this->edebug( |
|
706 'SMTP -> ERROR: ' . $this->error['error'] . ': ' . $reply |
|
707 ); |
|
708 } |
|
709 return false; |
|
710 } |
|
711 |
|
712 $this->last_reply = $reply; |
|
713 $this->error = null; |
|
714 return true; |
861 return true; |
715 } |
862 } |
716 |
863 |
717 /** |
864 /** |
718 * Send an SMTP SAML command. |
865 * Send an SMTP SAML command. |
723 * will send the message to the users terminal if they are logged |
870 * will send the message to the users terminal if they are logged |
724 * in and send them an email. |
871 * in and send them an email. |
725 * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> |
872 * Implements rfc 821: SAML <SP> FROM:<reverse-path> <CRLF> |
726 * @param string $from The address the message is from |
873 * @param string $from The address the message is from |
727 * @access public |
874 * @access public |
728 * @return bool |
875 * @return boolean |
729 */ |
876 */ |
730 public function sendAndMail($from) |
877 public function sendAndMail($from) |
731 { |
878 { |
732 return $this->sendCommand("SAML", "SAML FROM:$from", 250); |
879 return $this->sendCommand('SAML', "SAML FROM:$from", 250); |
733 } |
880 } |
734 |
881 |
735 /** |
882 /** |
736 * Send an SMTP VRFY command. |
883 * Send an SMTP VRFY command. |
737 * @param string $name The name to verify |
884 * @param string $name The name to verify |
738 * @access public |
885 * @access public |
739 * @return bool |
886 * @return boolean |
740 */ |
887 */ |
741 public function verify($name) |
888 public function verify($name) |
742 { |
889 { |
743 return $this->sendCommand("VRFY", "VRFY $name", array(250, 251)); |
890 return $this->sendCommand('VRFY', "VRFY $name", array(250, 251)); |
744 } |
891 } |
745 |
892 |
746 /** |
893 /** |
747 * Send an SMTP NOOP command. |
894 * Send an SMTP NOOP command. |
748 * Used to keep keep-alives alive, doesn't actually do anything |
895 * Used to keep keep-alives alive, doesn't actually do anything |
749 * @access public |
896 * @access public |
750 * @return bool |
897 * @return boolean |
751 */ |
898 */ |
752 public function noop() |
899 public function noop() |
753 { |
900 { |
754 return $this->sendCommand("NOOP", "NOOP", 250); |
901 return $this->sendCommand('NOOP', 'NOOP', 250); |
755 } |
902 } |
756 |
903 |
757 /** |
904 /** |
758 * Send an SMTP TURN command. |
905 * Send an SMTP TURN command. |
759 * This is an optional command for SMTP that this class does not support. |
906 * This is an optional command for SMTP that this class does not support. |
760 * This method is here to make the RFC821 Definition |
907 * This method is here to make the RFC821 Definition complete for this class |
761 * complete for this class and __may__ be implemented in future |
908 * and _may_ be implemented in future |
762 * Implements from rfc 821: TURN <CRLF> |
909 * Implements from rfc 821: TURN <CRLF> |
763 * @access public |
910 * @access public |
764 * @return bool |
911 * @return boolean |
765 */ |
912 */ |
766 public function turn() |
913 public function turn() |
767 { |
914 { |
768 $this->error = array( |
915 $this->setError('The SMTP TURN command is not implemented'); |
769 'error' => 'The SMTP TURN command is not implemented' |
916 $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT); |
770 ); |
|
771 if ($this->do_debug >= 1) { |
|
772 $this->edebug('SMTP -> NOTICE: ' . $this->error['error']); |
|
773 } |
|
774 return false; |
917 return false; |
775 } |
918 } |
776 |
919 |
777 /** |
920 /** |
778 * Send raw data to the server. |
921 * Send raw data to the server. |
779 * @param string $data The data to send |
922 * @param string $data The data to send |
780 * @access public |
923 * @access public |
781 * @return int|bool The number of bytes sent to the server or FALSE on error |
924 * @return integer|boolean The number of bytes sent to the server or false on error |
782 */ |
925 */ |
783 public function client_send($data) |
926 public function client_send($data) |
784 { |
927 { |
785 if ($this->do_debug >= 1) { |
928 $this->edebug("CLIENT -> SERVER: $data", self::DEBUG_CLIENT); |
786 $this->edebug("CLIENT -> SMTP: $data"); |
|
787 } |
|
788 return fwrite($this->smtp_conn, $data); |
929 return fwrite($this->smtp_conn, $data); |
789 } |
930 } |
790 |
931 |
791 /** |
932 /** |
792 * Get the latest error. |
933 * Get the latest error. |
817 * @access protected |
1009 * @access protected |
818 * @return string |
1010 * @return string |
819 */ |
1011 */ |
820 protected function get_lines() |
1012 protected function get_lines() |
821 { |
1013 { |
|
1014 // If the connection is bad, give up straight away |
|
1015 if (!is_resource($this->smtp_conn)) { |
|
1016 return ''; |
|
1017 } |
822 $data = ''; |
1018 $data = ''; |
823 $endtime = 0; |
1019 $endtime = 0; |
824 // If the connection is bad, give up now |
|
825 if (!is_resource($this->smtp_conn)) { |
|
826 return $data; |
|
827 } |
|
828 stream_set_timeout($this->smtp_conn, $this->Timeout); |
1020 stream_set_timeout($this->smtp_conn, $this->Timeout); |
829 if ($this->Timelimit > 0) { |
1021 if ($this->Timelimit > 0) { |
830 $endtime = time() + $this->Timelimit; |
1022 $endtime = time() + $this->Timelimit; |
831 } |
1023 } |
832 while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { |
1024 while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { |
833 $str = @fgets($this->smtp_conn, 515); |
1025 $str = @fgets($this->smtp_conn, 515); |
834 if ($this->do_debug >= 4) { |
1026 $this->edebug("SMTP -> get_lines(): \$data is \"$data\"", self::DEBUG_LOWLEVEL); |
835 $this->edebug("SMTP -> get_lines(): \$data was \"$data\""); |
1027 $this->edebug("SMTP -> get_lines(): \$str is \"$str\"", self::DEBUG_LOWLEVEL); |
836 $this->edebug("SMTP -> get_lines(): \$str is \"$str\""); |
|
837 } |
|
838 $data .= $str; |
1028 $data .= $str; |
839 if ($this->do_debug >= 4) { |
1029 // If 4th character is a space, we are done reading, break the loop, micro-optimisation over strlen |
840 $this->edebug("SMTP -> get_lines(): \$data is \"$data\""); |
1030 if ((isset($str[3]) and $str[3] == ' ')) { |
841 } |
|
842 // if 4th character is a space, we are done reading, break the loop |
|
843 if (substr($str, 3, 1) == ' ') { |
|
844 break; |
1031 break; |
845 } |
1032 } |
846 // Timed-out? Log and break |
1033 // Timed-out? Log and break |
847 $info = stream_get_meta_data($this->smtp_conn); |
1034 $info = stream_get_meta_data($this->smtp_conn); |
848 if ($info['timed_out']) { |
1035 if ($info['timed_out']) { |
849 if ($this->do_debug >= 4) { |
1036 $this->edebug( |
850 $this->edebug( |
1037 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', |
851 'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)' |
1038 self::DEBUG_LOWLEVEL |
852 ); |
1039 ); |
853 } |
|
854 break; |
1040 break; |
855 } |
1041 } |
856 // Now check if reads took too long |
1042 // Now check if reads took too long |
857 if ($endtime) { |
1043 if ($endtime and time() > $endtime) { |
858 if (time() > $endtime) { |
1044 $this->edebug( |
859 if ($this->do_debug >= 4) { |
1045 'SMTP -> get_lines(): timelimit reached ('. |
860 $this->edebug( |
1046 $this->Timelimit . ' sec)', |
861 'SMTP -> get_lines(): timelimit reached (' |
1047 self::DEBUG_LOWLEVEL |
862 . $this->Timelimit . ' sec)' |
1048 ); |
863 ); |
1049 break; |
864 } |
|
865 break; |
|
866 } |
|
867 } |
1050 } |
868 } |
1051 } |
869 return $data; |
1052 return $data; |
870 } |
1053 } |
871 |
1054 |
872 /** |
1055 /** |
873 * Enable or disable VERP address generation. |
1056 * Enable or disable VERP address generation. |
874 * @param bool $enabled |
1057 * @param boolean $enabled |
875 */ |
1058 */ |
876 public function setVerp($enabled = false) |
1059 public function setVerp($enabled = false) |
877 { |
1060 { |
878 $this->do_verp = $enabled; |
1061 $this->do_verp = $enabled; |
879 } |
1062 } |
880 |
1063 |
881 /** |
1064 /** |
882 * Get VERP address generation mode. |
1065 * Get VERP address generation mode. |
883 * @return bool |
1066 * @return boolean |
884 */ |
1067 */ |
885 public function getVerp() |
1068 public function getVerp() |
886 { |
1069 { |
887 return $this->do_verp; |
1070 return $this->do_verp; |
888 } |
1071 } |
889 |
1072 |
890 /** |
1073 /** |
|
1074 * Set error messages and codes. |
|
1075 * @param string $message The error message |
|
1076 * @param string $detail Further detail on the error |
|
1077 * @param string $smtp_code An associated SMTP error code |
|
1078 * @param string $smtp_code_ex Extended SMTP code |
|
1079 */ |
|
1080 protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '') |
|
1081 { |
|
1082 $this->error = array( |
|
1083 'error' => $message, |
|
1084 'detail' => $detail, |
|
1085 'smtp_code' => $smtp_code, |
|
1086 'smtp_code_ex' => $smtp_code_ex |
|
1087 ); |
|
1088 } |
|
1089 |
|
1090 /** |
891 * Set debug output method. |
1091 * Set debug output method. |
892 * @param string $method The function/method to use for debugging output. |
1092 * @param string|callable $method The name of the mechanism to use for debugging output, or a callable to handle it. |
893 */ |
1093 */ |
894 public function setDebugOutput($method = 'echo') |
1094 public function setDebugOutput($method = 'echo') |
895 { |
1095 { |
896 $this->Debugoutput = $method; |
1096 $this->Debugoutput = $method; |
897 } |
1097 } |
905 return $this->Debugoutput; |
1105 return $this->Debugoutput; |
906 } |
1106 } |
907 |
1107 |
908 /** |
1108 /** |
909 * Set debug output level. |
1109 * Set debug output level. |
910 * @param int $level |
1110 * @param integer $level |
911 */ |
1111 */ |
912 public function setDebugLevel($level = 0) |
1112 public function setDebugLevel($level = 0) |
913 { |
1113 { |
914 $this->do_debug = $level; |
1114 $this->do_debug = $level; |
915 } |
1115 } |
916 |
1116 |
917 /** |
1117 /** |
918 * Get debug output level. |
1118 * Get debug output level. |
919 * @return int |
1119 * @return integer |
920 */ |
1120 */ |
921 public function getDebugLevel() |
1121 public function getDebugLevel() |
922 { |
1122 { |
923 return $this->do_debug; |
1123 return $this->do_debug; |
924 } |
1124 } |
925 |
1125 |
926 /** |
1126 /** |
927 * Set SMTP timeout. |
1127 * Set SMTP timeout. |
928 * @param int $timeout |
1128 * @param integer $timeout |
929 */ |
1129 */ |
930 public function setTimeout($timeout = 0) |
1130 public function setTimeout($timeout = 0) |
931 { |
1131 { |
932 $this->Timeout = $timeout; |
1132 $this->Timeout = $timeout; |
933 } |
1133 } |
934 |
1134 |
935 /** |
1135 /** |
936 * Get SMTP timeout. |
1136 * Get SMTP timeout. |
937 * @return int |
1137 * @return integer |
938 */ |
1138 */ |
939 public function getTimeout() |
1139 public function getTimeout() |
940 { |
1140 { |
941 return $this->Timeout; |
1141 return $this->Timeout; |
942 } |
1142 } |
|
1143 |
|
1144 /** |
|
1145 * Reports an error number and string. |
|
1146 * @param integer $errno The error number returned by PHP. |
|
1147 * @param string $errmsg The error message returned by PHP. |
|
1148 */ |
|
1149 protected function errorHandler($errno, $errmsg) |
|
1150 { |
|
1151 $notice = 'Connection: Failed to connect to server.'; |
|
1152 $this->setError( |
|
1153 $notice, |
|
1154 $errno, |
|
1155 $errmsg |
|
1156 ); |
|
1157 $this->edebug( |
|
1158 $notice . ' Error number ' . $errno . '. "Error notice: ' . $errmsg, |
|
1159 self::DEBUG_CONNECTION |
|
1160 ); |
|
1161 } |
|
1162 |
|
1163 /** |
|
1164 * Will return the ID of the last smtp transaction based on a list of patterns provided |
|
1165 * in SMTP::$smtp_transaction_id_patterns. |
|
1166 * If no reply has been received yet, it will return null. |
|
1167 * If no pattern has been matched, it will return false. |
|
1168 * @return bool|null|string |
|
1169 */ |
|
1170 public function getLastTransactionID() |
|
1171 { |
|
1172 $reply = $this->getLastReply(); |
|
1173 |
|
1174 if (empty($reply)) { |
|
1175 return null; |
|
1176 } |
|
1177 |
|
1178 foreach($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) { |
|
1179 if(preg_match($smtp_transaction_id_pattern, $reply, $matches)) { |
|
1180 return $matches[1]; |
|
1181 } |
|
1182 } |
|
1183 |
|
1184 return false; |
|
1185 } |
943 } |
1186 } |