ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
SMTP.php
Go to the documentation of this file.
1 <?php
21 namespace PHPMailer\PHPMailer;
22 
30 class SMTP
31 {
37  const VERSION = '6.1.6';
38 
44  const LE = "\r\n";
45 
51  const DEFAULT_PORT = 25;
52 
61  const MAX_LINE_LENGTH = 998;
62 
71  const MAX_REPLY_LENGTH = 512;
72 
78  const DEBUG_OFF = 0;
79 
85  const DEBUG_CLIENT = 1;
86 
92  const DEBUG_SERVER = 2;
93 
99  const DEBUG_CONNECTION = 3;
100 
106  const DEBUG_LOWLEVEL = 4;
107 
119  public $do_debug = self::DEBUG_OFF;
120 
142  public $Debugoutput = 'echo';
143 
152  public $do_verp = false;
153 
163  public $Timeout = 300;
164 
171  public $Timelimit = 300;
172 
181  'exim' => '/[\d]{3} OK id=(.*)/',
182  'sendmail' => '/[\d]{3} 2.0.0 (.*) Message/',
183  'postfix' => '/[\d]{3} 2.0.0 Ok: queued as (.*)/',
184  'Microsoft_ESMTP' => '/[0-9]{3} 2.[\d].0 (.*)@(?:.*) Queued mail for delivery/',
185  'Amazon_SES' => '/[\d]{3} Ok (.*)/',
186  'SendGrid' => '/[\d]{3} Ok: queued as (.*)/',
187  'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/',
188  ];
189 
197 
203  protected $smtp_conn;
204 
210  protected $error = [
211  'error' => '',
212  'detail' => '',
213  'smtp_code' => '',
214  'smtp_code_ex' => '',
215  ];
216 
223  protected $helo_rply;
224 
235  protected $server_caps;
236 
242  protected $last_reply = '';
243 
253  protected function edebug($str, $level = 0)
254  {
255  if ($level > $this->do_debug) {
256  return;
257  }
258  //Is this a PSR-3 logger?
259  if ($this->Debugoutput instanceof \Psr\Log\LoggerInterface) {
260  $this->Debugoutput->debug($str);
261 
262  return;
263  }
264  //Avoid clash with built-in function names
265  if (is_callable($this->Debugoutput) && !in_array($this->Debugoutput, ['error_log', 'html', 'echo'])) {
266  call_user_func($this->Debugoutput, $str, $level);
267 
268  return;
269  }
270  switch ($this->Debugoutput) {
271  case 'error_log':
272  //Don't output, just log
273  error_log($str);
274  break;
275  case 'html':
276  //Cleans up output a bit for a better looking, HTML-safe output
277  echo gmdate('Y-m-d H:i:s'), ' ', htmlentities(
278  preg_replace('/[\r\n]+/', '', $str),
279  ENT_QUOTES,
280  'UTF-8'
281  ), "<br>\n";
282  break;
283  case 'echo':
284  default:
285  //Normalize line breaks
286  $str = preg_replace('/\r\n|\r/m', "\n", $str);
287  echo gmdate('Y-m-d H:i:s'),
288  "\t",
289  //Trim trailing space
290  trim(
291  //Indent for readability, except for trailing break
292  str_replace(
293  "\n",
294  "\n \t ",
295  trim($str)
296  )
297  ),
298  "\n";
299  }
300  }
301 
312  public function connect($host, $port = null, $timeout = 30, $options = [])
313  {
314  static $streamok;
315  //This is enabled by default since 5.0.0 but some providers disable it
316  //Check this once and cache the result
317  if (null === $streamok) {
318  $streamok = function_exists('stream_socket_client');
319  }
320  // Clear errors to avoid confusion
321  $this->setError('');
322  // Make sure we are __not__ connected
323  if ($this->connected()) {
324  // Already connected, generate error
325  $this->setError('Already connected to a server');
326 
327  return false;
328  }
329  if (empty($port)) {
330  $port = self::DEFAULT_PORT;
331  }
332  // Connect to the SMTP server
333  $this->edebug(
334  "Connection: opening to $host:$port, timeout=$timeout, options=" .
335  (count($options) > 0 ? var_export($options, true) : 'array()'),
336  self::DEBUG_CONNECTION
337  );
338  $errno = 0;
339  $errstr = '';
340  if ($streamok) {
341  $socket_context = stream_context_create($options);
342  set_error_handler([$this, 'errorHandler']);
343  $this->smtp_conn = stream_socket_client(
344  $host . ':' . $port,
345  $errno,
346  $errstr,
347  $timeout,
348  STREAM_CLIENT_CONNECT,
349  $socket_context
350  );
351  restore_error_handler();
352  } else {
353  //Fall back to fsockopen which should work in more places, but is missing some features
354  $this->edebug(
355  'Connection: stream_socket_client not available, falling back to fsockopen',
356  self::DEBUG_CONNECTION
357  );
358  set_error_handler([$this, 'errorHandler']);
359  $this->smtp_conn = fsockopen(
360  $host,
361  $port,
362  $errno,
363  $errstr,
364  $timeout
365  );
366  restore_error_handler();
367  }
368  // Verify we connected properly
369  if (!is_resource($this->smtp_conn)) {
370  $this->setError(
371  'Failed to connect to server',
372  '',
373  (string) $errno,
374  $errstr
375  );
376  $this->edebug(
377  'SMTP ERROR: ' . $this->error['error']
378  . ": $errstr ($errno)",
379  self::DEBUG_CLIENT
380  );
381 
382  return false;
383  }
384  $this->edebug('Connection: opened', self::DEBUG_CONNECTION);
385  // SMTP server can take longer to respond, give longer timeout for first read
386  // Windows does not have support for this timeout function
387  if (strpos(PHP_OS, 'WIN') !== 0) {
388  $max = (int) ini_get('max_execution_time');
389  // Don't bother if unlimited
390  if (0 !== $max && $timeout > $max) {
391  @set_time_limit($timeout);
392  }
393  stream_set_timeout($this->smtp_conn, $timeout, 0);
394  }
395  // Get any announcement
396  $announce = $this->get_lines();
397  $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER);
398 
399  return true;
400  }
401 
407  public function startTLS()
408  {
409  if (!$this->sendCommand('STARTTLS', 'STARTTLS', 220)) {
410  return false;
411  }
412 
413  //Allow the best TLS version(s) we can
414  $crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
415 
416  //PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
417  //so add them back in manually if we can
418  if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
419  $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
420  $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
421  }
422 
423  // Begin encrypted connection
424  set_error_handler([$this, 'errorHandler']);
425  $crypto_ok = stream_socket_enable_crypto(
426  $this->smtp_conn,
427  true,
428  $crypto_method
429  );
430  restore_error_handler();
431 
432  return (bool) $crypto_ok;
433  }
434 
448  public function authenticate(
449  $username,
450  $password,
451  $authtype = null,
452  $OAuth = null
453  ) {
454  if (!$this->server_caps) {
455  $this->setError('Authentication is not allowed before HELO/EHLO');
456 
457  return false;
458  }
459 
460  if (array_key_exists('EHLO', $this->server_caps)) {
461  // SMTP extensions are available; try to find a proper authentication method
462  if (!array_key_exists('AUTH', $this->server_caps)) {
463  $this->setError('Authentication is not allowed at this stage');
464  // 'at this stage' means that auth may be allowed after the stage changes
465  // e.g. after STARTTLS
466 
467  return false;
468  }
469 
470  $this->edebug('Auth method requested: ' . ($authtype ?: 'UNSPECIFIED'), self::DEBUG_LOWLEVEL);
471  $this->edebug(
472  'Auth methods available on the server: ' . implode(',', $this->server_caps['AUTH']),
473  self::DEBUG_LOWLEVEL
474  );
475 
476  //If we have requested a specific auth type, check the server supports it before trying others
477  if (null !== $authtype && !in_array($authtype, $this->server_caps['AUTH'], true)) {
478  $this->edebug('Requested auth method not available: ' . $authtype, self::DEBUG_LOWLEVEL);
479  $authtype = null;
480  }
481 
482  if (empty($authtype)) {
483  //If no auth mechanism is specified, attempt to use these, in this order
484  //Try CRAM-MD5 first as it's more secure than the others
485  foreach (['CRAM-MD5', 'LOGIN', 'PLAIN', 'XOAUTH2'] as $method) {
486  if (in_array($method, $this->server_caps['AUTH'], true)) {
487  $authtype = $method;
488  break;
489  }
490  }
491  if (empty($authtype)) {
492  $this->setError('No supported authentication methods found');
493 
494  return false;
495  }
496  $this->edebug('Auth method selected: ' . $authtype, self::DEBUG_LOWLEVEL);
497  }
498 
499  if (!in_array($authtype, $this->server_caps['AUTH'], true)) {
500  $this->setError("The requested authentication method \"$authtype\" is not supported by the server");
501 
502  return false;
503  }
504  } elseif (empty($authtype)) {
505  $authtype = 'LOGIN';
506  }
507  switch ($authtype) {
508  case 'PLAIN':
509  // Start authentication
510  if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) {
511  return false;
512  }
513  // Send encoded username and password
514  if (!$this->sendCommand(
515  'User & Password',
516  base64_encode("\0" . $username . "\0" . $password),
517  235
518  )
519  ) {
520  return false;
521  }
522  break;
523  case 'LOGIN':
524  // Start authentication
525  if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) {
526  return false;
527  }
528  if (!$this->sendCommand('Username', base64_encode($username), 334)) {
529  return false;
530  }
531  if (!$this->sendCommand('Password', base64_encode($password), 235)) {
532  return false;
533  }
534  break;
535  case 'CRAM-MD5':
536  // Start authentication
537  if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) {
538  return false;
539  }
540  // Get the challenge
541  $challenge = base64_decode(substr($this->last_reply, 4));
542 
543  // Build the response
544  $response = $username . ' ' . $this->hmac($challenge, $password);
545 
546  // send encoded credentials
547  return $this->sendCommand('Username', base64_encode($response), 235);
548  case 'XOAUTH2':
549  //The OAuth instance must be set up prior to requesting auth.
550  if (null === $OAuth) {
551  return false;
552  }
553  $oauth = $OAuth->getOauth64();
554 
555  // Start authentication
556  if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) {
557  return false;
558  }
559  break;
560  default:
561  $this->setError("Authentication method \"$authtype\" is not supported");
562 
563  return false;
564  }
565 
566  return true;
567  }
568 
579  protected function hmac($data, $key)
580  {
581  if (function_exists('hash_hmac')) {
582  return hash_hmac('md5', $data, $key);
583  }
584 
585  // The following borrowed from
586  // http://php.net/manual/en/function.mhash.php#27225
587 
588  // RFC 2104 HMAC implementation for php.
589  // Creates an md5 HMAC.
590  // Eliminates the need to install mhash to compute a HMAC
591  // by Lance Rushing
592 
593  $bytelen = 64; // byte length for md5
594  if (strlen($key) > $bytelen) {
595  $key = pack('H*', md5($key));
596  }
597  $key = str_pad($key, $bytelen, chr(0x00));
598  $ipad = str_pad('', $bytelen, chr(0x36));
599  $opad = str_pad('', $bytelen, chr(0x5c));
600  $k_ipad = $key ^ $ipad;
601  $k_opad = $key ^ $opad;
602 
603  return md5($k_opad . pack('H*', md5($k_ipad . $data)));
604  }
605 
611  public function connected()
612  {
613  if (is_resource($this->smtp_conn)) {
614  $sock_status = stream_get_meta_data($this->smtp_conn);
615  if ($sock_status['eof']) {
616  // The socket is valid but we are not connected
617  $this->edebug(
618  'SMTP NOTICE: EOF caught while checking if connected',
619  self::DEBUG_CLIENT
620  );
621  $this->close();
622 
623  return false;
624  }
625 
626  return true; // everything looks good
627  }
628 
629  return false;
630  }
631 
638  public function close()
639  {
640  $this->setError('');
641  $this->server_caps = null;
642  $this->helo_rply = null;
643  if (is_resource($this->smtp_conn)) {
644  // close the connection and cleanup
645  fclose($this->smtp_conn);
646  $this->smtp_conn = null; //Makes for cleaner serialization
647  $this->edebug('Connection: closed', self::DEBUG_CONNECTION);
648  }
649  }
650 
664  public function data($msg_data)
665  {
666  //This will use the standard timelimit
667  if (!$this->sendCommand('DATA', 'DATA', 354)) {
668  return false;
669  }
670 
671  /* The server is ready to accept data!
672  * According to rfc821 we should not send more than 1000 characters on a single line (including the LE)
673  * so we will break the data up into lines by \r and/or \n then if needed we will break each of those into
674  * smaller lines to fit within the limit.
675  * We will also look for lines that start with a '.' and prepend an additional '.'.
676  * NOTE: this does not count towards line-length limit.
677  */
678 
679  // Normalize line breaks before exploding
680  $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data));
681 
682  /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field
683  * of the first line (':' separated) does not contain a space then it _should_ be a header and we will
684  * process all lines before a blank line as headers.
685  */
686 
687  $field = substr($lines[0], 0, strpos($lines[0], ':'));
688  $in_headers = false;
689  if (!empty($field) && strpos($field, ' ') === false) {
690  $in_headers = true;
691  }
692 
693  foreach ($lines as $line) {
694  $lines_out = [];
695  if ($in_headers && $line === '') {
696  $in_headers = false;
697  }
698  //Break this line up into several smaller lines if it's too long
699  //Micro-optimisation: isset($str[$len]) is faster than (strlen($str) > $len),
700  while (isset($line[self::MAX_LINE_LENGTH])) {
701  //Working backwards, try to find a space within the last MAX_LINE_LENGTH chars of the line to break on
702  //so as to avoid breaking in the middle of a word
703  $pos = strrpos(substr($line, 0, self::MAX_LINE_LENGTH), ' ');
704  //Deliberately matches both false and 0
705  if (!$pos) {
706  //No nice break found, add a hard break
707  $pos = self::MAX_LINE_LENGTH - 1;
708  $lines_out[] = substr($line, 0, $pos);
709  $line = substr($line, $pos);
710  } else {
711  //Break at the found point
712  $lines_out[] = substr($line, 0, $pos);
713  //Move along by the amount we dealt with
714  $line = substr($line, $pos + 1);
715  }
716  //If processing headers add a LWSP-char to the front of new line RFC822 section 3.1.1
717  if ($in_headers) {
718  $line = "\t" . $line;
719  }
720  }
721  $lines_out[] = $line;
722 
723  //Send the lines to the server
724  foreach ($lines_out as $line_out) {
725  //RFC2821 section 4.5.2
726  if (!empty($line_out) && $line_out[0] === '.') {
727  $line_out = '.' . $line_out;
728  }
729  $this->client_send($line_out . static::LE, 'DATA');
730  }
731  }
732 
733  //Message data has been sent, complete the command
734  //Increase timelimit for end of DATA command
735  $savetimelimit = $this->Timelimit;
736  $this->Timelimit *= 2;
737  $result = $this->sendCommand('DATA END', '.', 250);
738  $this->recordLastTransactionID();
739  //Restore timelimit
740  $this->Timelimit = $savetimelimit;
741 
742  return $result;
743  }
744 
756  public function hello($host = '')
757  {
758  //Try extended hello first (RFC 2821)
759  return $this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host);
760  }
761 
773  protected function sendHello($hello, $host)
774  {
775  $noerror = $this->sendCommand($hello, $hello . ' ' . $host, 250);
776  $this->helo_rply = $this->last_reply;
777  if ($noerror) {
778  $this->parseHelloFields($hello);
779  } else {
780  $this->server_caps = null;
781  }
782 
783  return $noerror;
784  }
785 
792  protected function parseHelloFields($type)
793  {
794  $this->server_caps = [];
795  $lines = explode("\n", $this->helo_rply);
796 
797  foreach ($lines as $n => $s) {
798  //First 4 chars contain response code followed by - or space
799  $s = trim(substr($s, 4));
800  if (empty($s)) {
801  continue;
802  }
803  $fields = explode(' ', $s);
804  if (!empty($fields)) {
805  if (!$n) {
806  $name = $type;
807  $fields = $fields[0];
808  } else {
809  $name = array_shift($fields);
810  switch ($name) {
811  case 'SIZE':
812  $fields = ($fields ? $fields[0] : 0);
813  break;
814  case 'AUTH':
815  if (!is_array($fields)) {
816  $fields = [];
817  }
818  break;
819  default:
820  $fields = true;
821  }
822  }
823  $this->server_caps[$name] = $fields;
824  }
825  }
826  }
827 
840  public function mail($from)
841  {
842  $useVerp = ($this->do_verp ? ' XVERP' : '');
843 
844  return $this->sendCommand(
845  'MAIL FROM',
846  'MAIL FROM:<' . $from . '>' . $useVerp,
847  250
848  );
849  }
850 
860  public function quit($close_on_error = true)
861  {
862  $noerror = $this->sendCommand('QUIT', 'QUIT', 221);
863  $err = $this->error; //Save any error
864  if ($noerror || $close_on_error) {
865  $this->close();
866  $this->error = $err; //Restore any error from the quit command
867  }
868 
869  return $noerror;
870  }
871 
884  public function recipient($address, $dsn = '')
885  {
886  if (empty($dsn)) {
887  $rcpt = 'RCPT TO:<' . $address . '>';
888  } else {
889  $dsn = strtoupper($dsn);
890  $notify = [];
891 
892  if (strpos($dsn, 'NEVER') !== false) {
893  $notify[] = 'NEVER';
894  } else {
895  foreach (['SUCCESS', 'FAILURE', 'DELAY'] as $value) {
896  if (strpos($dsn, $value) !== false) {
897  $notify[] = $value;
898  }
899  }
900  }
901 
902  $rcpt = 'RCPT TO:<' . $address . '> NOTIFY=' . implode(',', $notify);
903  }
904 
905  return $this->sendCommand(
906  'RCPT TO',
907  $rcpt,
908  [250, 251]
909  );
910  }
911 
919  public function reset()
920  {
921  return $this->sendCommand('RSET', 'RSET', 250);
922  }
923 
933  protected function sendCommand($command, $commandstring, $expect)
934  {
935  if (!$this->connected()) {
936  $this->setError("Called $command without being connected");
937 
938  return false;
939  }
940  //Reject line breaks in all commands
941  if ((strpos($commandstring, "\n") !== false) || (strpos($commandstring, "\r") !== false)) {
942  $this->setError("Command '$command' contained line breaks");
943 
944  return false;
945  }
946  $this->client_send($commandstring . static::LE, $command);
947 
948  $this->last_reply = $this->get_lines();
949  // Fetch SMTP code and possible error code explanation
950  $matches = [];
951  if (preg_match('/^([\d]{3})[ -](?:([\d]\\.[\d]\\.[\d]{1,2}) )?/', $this->last_reply, $matches)) {
952  $code = (int) $matches[1];
953  $code_ex = (count($matches) > 2 ? $matches[2] : null);
954  // Cut off error code from each response line
955  $detail = preg_replace(
956  "/{$code}[ -]" .
957  ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m',
958  '',
959  $this->last_reply
960  );
961  } else {
962  // Fall back to simple parsing if regex fails
963  $code = (int) substr($this->last_reply, 0, 3);
964  $code_ex = null;
965  $detail = substr($this->last_reply, 4);
966  }
967 
968  $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER);
969 
970  if (!in_array($code, (array) $expect, true)) {
971  $this->setError(
972  "$command command failed",
973  $detail,
974  $code,
975  $code_ex
976  );
977  $this->edebug(
978  'SMTP ERROR: ' . $this->error['error'] . ': ' . $this->last_reply,
979  self::DEBUG_CLIENT
980  );
981 
982  return false;
983  }
984 
985  $this->setError('');
986 
987  return true;
988  }
989 
1004  public function sendAndMail($from)
1005  {
1006  return $this->sendCommand('SAML', "SAML FROM:$from", 250);
1007  }
1008 
1016  public function verify($name)
1017  {
1018  return $this->sendCommand('VRFY', "VRFY $name", [250, 251]);
1019  }
1020 
1027  public function noop()
1028  {
1029  return $this->sendCommand('NOOP', 'NOOP', 250);
1030  }
1031 
1041  public function turn()
1042  {
1043  $this->setError('The SMTP TURN command is not implemented');
1044  $this->edebug('SMTP NOTICE: ' . $this->error['error'], self::DEBUG_CLIENT);
1045 
1046  return false;
1047  }
1048 
1057  public function client_send($data, $command = '')
1058  {
1059  //If SMTP transcripts are left enabled, or debug output is posted online
1060  //it can leak credentials, so hide credentials in all but lowest level
1061  if (self::DEBUG_LOWLEVEL > $this->do_debug &&
1062  in_array($command, ['User & Password', 'Username', 'Password'], true)) {
1063  $this->edebug('CLIENT -> SERVER: [credentials hidden]', self::DEBUG_CLIENT);
1064  } else {
1065  $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT);
1066  }
1067  set_error_handler([$this, 'errorHandler']);
1068  $result = fwrite($this->smtp_conn, $data);
1069  restore_error_handler();
1070 
1071  return $result;
1072  }
1073 
1079  public function getError()
1080  {
1081  return $this->error;
1082  }
1083 
1089  public function getServerExtList()
1090  {
1091  return $this->server_caps;
1092  }
1093 
1111  public function getServerExt($name)
1112  {
1113  if (!$this->server_caps) {
1114  $this->setError('No HELO/EHLO was sent');
1115 
1116  return;
1117  }
1118 
1119  if (!array_key_exists($name, $this->server_caps)) {
1120  if ('HELO' === $name) {
1121  return $this->server_caps['EHLO'];
1122  }
1123  if ('EHLO' === $name || array_key_exists('EHLO', $this->server_caps)) {
1124  return false;
1125  }
1126  $this->setError('HELO handshake was used; No information about server extensions available');
1127 
1128  return;
1129  }
1130 
1131  return $this->server_caps[$name];
1132  }
1133 
1139  public function getLastReply()
1140  {
1141  return $this->last_reply;
1142  }
1143 
1153  protected function get_lines()
1154  {
1155  // If the connection is bad, give up straight away
1156  if (!is_resource($this->smtp_conn)) {
1157  return '';
1158  }
1159  $data = '';
1160  $endtime = 0;
1161  stream_set_timeout($this->smtp_conn, $this->Timeout);
1162  if ($this->Timelimit > 0) {
1163  $endtime = time() + $this->Timelimit;
1164  }
1165  $selR = [$this->smtp_conn];
1166  $selW = null;
1167  while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) {
1168  //Must pass vars in here as params are by reference
1169  if (!stream_select($selR, $selW, $selW, $this->Timelimit)) {
1170  $this->edebug(
1171  'SMTP -> get_lines(): select timed-out in (' . $this->Timelimit . ' sec)',
1172  self::DEBUG_LOWLEVEL
1173  );
1174  break;
1175  }
1176  //Deliberate noise suppression - errors are handled afterwards
1177  $str = @fgets($this->smtp_conn, self::MAX_REPLY_LENGTH);
1178  $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL);
1179  $data .= $str;
1180  // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
1181  // or 4th character is a space or a line break char, we are done reading, break the loop.
1182  // String array access is a significant micro-optimisation over strlen
1183  if (!isset($str[3]) || $str[3] === ' ' || $str[3] === "\r" || $str[3] === "\n") {
1184  break;
1185  }
1186  // Timed-out? Log and break
1187  $info = stream_get_meta_data($this->smtp_conn);
1188  if ($info['timed_out']) {
1189  $this->edebug(
1190  'SMTP -> get_lines(): stream timed-out (' . $this->Timeout . ' sec)',
1191  self::DEBUG_LOWLEVEL
1192  );
1193  break;
1194  }
1195  // Now check if reads took too long
1196  if ($endtime && time() > $endtime) {
1197  $this->edebug(
1198  'SMTP -> get_lines(): timelimit reached (' .
1199  $this->Timelimit . ' sec)',
1200  self::DEBUG_LOWLEVEL
1201  );
1202  break;
1203  }
1204  }
1205 
1206  return $data;
1207  }
1208 
1214  public function setVerp($enabled = false)
1215  {
1216  $this->do_verp = $enabled;
1217  }
1218 
1224  public function getVerp()
1225  {
1226  return $this->do_verp;
1227  }
1228 
1237  protected function setError($message, $detail = '', $smtp_code = '', $smtp_code_ex = '')
1238  {
1239  $this->error = [
1240  'error' => $message,
1241  'detail' => $detail,
1242  'smtp_code' => $smtp_code,
1243  'smtp_code_ex' => $smtp_code_ex,
1244  ];
1245  }
1246 
1252  public function setDebugOutput($method = 'echo')
1253  {
1254  $this->Debugoutput = $method;
1255  }
1256 
1262  public function getDebugOutput()
1263  {
1264  return $this->Debugoutput;
1265  }
1266 
1272  public function setDebugLevel($level = 0)
1273  {
1274  $this->do_debug = $level;
1275  }
1276 
1282  public function getDebugLevel()
1283  {
1284  return $this->do_debug;
1285  }
1286 
1292  public function setTimeout($timeout = 0)
1293  {
1294  $this->Timeout = $timeout;
1295  }
1296 
1302  public function getTimeout()
1303  {
1304  return $this->Timeout;
1305  }
1306 
1315  protected function errorHandler($errno, $errmsg, $errfile = '', $errline = 0)
1316  {
1317  $notice = 'Connection failed.';
1318  $this->setError(
1319  $notice,
1320  $errmsg,
1321  (string) $errno
1322  );
1323  $this->edebug(
1324  "$notice Error #$errno: $errmsg [$errfile line $errline]",
1325  self::DEBUG_CONNECTION
1326  );
1327  }
1328 
1338  protected function recordLastTransactionID()
1339  {
1340  $reply = $this->getLastReply();
1341 
1342  if (empty($reply)) {
1343  $this->last_smtp_transaction_id = null;
1344  } else {
1345  $this->last_smtp_transaction_id = false;
1346  foreach ($this->smtp_transaction_id_patterns as $smtp_transaction_id_pattern) {
1347  $matches = [];
1348  if (preg_match($smtp_transaction_id_pattern, $reply, $matches)) {
1349  $this->last_smtp_transaction_id = trim($matches[1]);
1350  break;
1351  }
1352  }
1353  }
1354 
1356  }
1357 
1367  public function getLastTransactionID()
1368  {
1370  }
1371 }
data($msg_data)
Send an SMTP DATA command.
Definition: SMTP.php:664
hmac($data, $key)
Calculate an MD5 HMAC hash.
Definition: SMTP.php:579
setTimeout($timeout=0)
Set SMTP timeout.
Definition: SMTP.php:1292
$result
parseHelloFields($type)
Parse a reply to HELO/EHLO command to discover server extensions.
Definition: SMTP.php:792
$type
getLastReply()
Get the last reply from the server.
Definition: SMTP.php:1139
quit($close_on_error=true)
Send an SMTP QUIT command.
Definition: SMTP.php:860
$code
Definition: example_050.php:99
verify($name)
Send an SMTP VRFY command.
Definition: SMTP.php:1016
recordLastTransactionID()
Extract and return the ID of the last SMTP transaction based on a list of patterns provided in SMTP::...
Definition: SMTP.php:1338
getDebugLevel()
Get debug output level.
Definition: SMTP.php:1282
getError()
Get the latest error.
Definition: SMTP.php:1079
sendHello($hello, $host)
Send an SMTP HELO or EHLO command.
Definition: SMTP.php:773
$s
Definition: pwgen.php:45
$from
get_lines()
Read the SMTP server&#39;s response.
Definition: SMTP.php:1153
foreach($paths as $path) $dsn
Definition: migrateto20.php:56
setError($message, $detail='', $smtp_code='', $smtp_code_ex='')
Set error messages and codes.
Definition: SMTP.php:1237
getDebugOutput()
Get debug output method.
Definition: SMTP.php:1262
edebug($str, $level=0)
Output debugging info via a user-selected method.
Definition: SMTP.php:253
sendAndMail($from)
Send an SMTP SAML command.
Definition: SMTP.php:1004
catch(Exception $e) $message
recipient($address, $dsn='')
Send an SMTP RCPT command.
Definition: SMTP.php:884
close()
Close the socket and clean up the state of the class.
Definition: SMTP.php:638
getLastTransactionID()
Get the queue/transaction ID of the last SMTP transaction If no reply has been received yet...
Definition: SMTP.php:1367
getVerp()
Get VERP address generation mode.
Definition: SMTP.php:1224
connect($host, $port=null, $timeout=30, $options=[])
Connect to an SMTP server.
Definition: SMTP.php:312
getServerExt($name)
Get metadata about the SMTP server from its HELO/EHLO response.
Definition: SMTP.php:1111
sendCommand($command, $commandstring, $expect)
Send a command to an SMTP server and check its return code.
Definition: SMTP.php:933
hello($host='')
Send an SMTP HELO or EHLO command.
Definition: SMTP.php:756
mail($from)
Send an SMTP MAIL command.
Definition: SMTP.php:840
$n
Definition: RandomTest.php:85
setVerp($enabled=false)
Enable or disable VERP address generation.
Definition: SMTP.php:1214
client_send($data, $command='')
Send raw data to the server.
Definition: SMTP.php:1057
startTLS()
Initiate a TLS (encrypted) session.
Definition: SMTP.php:407
$password
Definition: cron.php:14
getServerExtList()
Get SMTP extensions available on the server.
Definition: SMTP.php:1089
turn()
Send an SMTP TURN command.
Definition: SMTP.php:1041
getTimeout()
Get SMTP timeout.
Definition: SMTP.php:1302
errorHandler($errno, $errmsg, $errfile='', $errline=0)
Reports an error number and string.
Definition: SMTP.php:1315
noop()
Send an SMTP NOOP command.
Definition: SMTP.php:1027
reset()
Send an SMTP RSET command.
Definition: SMTP.php:919
setDebugOutput($method='echo')
Set debug output method.
Definition: SMTP.php:1252
authenticate( $username, $password, $authtype=null, $OAuth=null)
Perform SMTP authentication.
Definition: SMTP.php:448
$info
Definition: index.php:5
PHPMailer RFC821 SMTP email transport class.
Definition: SMTP.php:30
connected()
Check connection state.
Definition: SMTP.php:611
$response
$key
Definition: croninfo.php:18
$data
Definition: bench.php:6
setDebugLevel($level=0)
Set debug output level.
Definition: SMTP.php:1272
Get an OAuth2 token from an OAuth2 provider.