PHPMailer $this->smtp->error info is overwritten after SMTP connect fail, useful error msg discarded

  php, phpmailer, smtp

PHPMailer 5.2.28
Running inside Zen Cart PHP based ecommerce system.

I may be misunderstanding things but I’m stepping through PHPMailer when I accidentally mistype the host or port number and the useful error message that is created gets discarded before PHPMailer returns from Send().

The sequence of events is:

  • Create PHPMailer instance with Host = ‘mistaken-smtp.gmail.com’, everything else normal and try to send an email.
  • smtpConnect() calls $this->smtp->connect
  • in class.smtp.php the connect() function sets a custom errorHandler: set_error_handler(array($this, ‘errorHandler’));
  • The call to fsockopen() fails because of the mistyped hostname
  • errorHandler is invoked, calls $this->setError($notice, $errno, $errmsg) with a useful message like ‘cannot connect to host mistaken-smtp.gmail.com’
  • Because the connection failed, connect() then calls setError itself (line 319), overwriting the error object set by the errorHandler but still has some useful information about what caused the connection failure, and returns false.
  • Because $this->smtp->connect returned false, PHPMailer.smtpConnect skips the workhorse body of the function and skips to line 1727 where it calls $this->smtp->close() and will return false.
  • The call to $this->smtp->close causes the smtp class to complete wipe out its error object with $this->setError(”); .. it’s here that any useful error messages get completely lost.
  • Because smtpConnect() returned false, PHPMailer.smtpSend throws a phpmailerException on line 1558 which is caught in postSend() on line 1352 and PHPMailer.setError is called with the exception’s message, which by now is a rather unuseful "SMTP connect() failed. https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting"
  • PHPMail.setError does try to inspect $this->smtp->getError() on line 3273 instead of using the plain $msg passed in, but because the close() earlier wiped out the error details in the smtp object, PHPMailer.setError simply sets ErrorInfo to the unhelpful error string from the exception.

This behaviour seems to have been around for ages so I imagine no-one has cared enough to address it in detail. I can certainly work around this but it seems buggy. Is there another way to get error info out of PHPMailer in this case?

This came to my attention because we hit some kind of quota limit with gmail, trying to mailshot via smtp.gmail.com sent 100 emails OK but then repeatedly failed to connect, the only error message coming to our code being ‘SMTP connect() failed’. In our case the hostname, port etc were fine (unlike the fictional case above) but the gmail server refused the connection and the error handling I mentioned above was invoked, it’s just hard to reproduce easily. It would have been very valuable to get the actual SMTP error message returned that was lost due to the dodgy error handling I outline above. (For anyone else hitting this, I spent an hour on live chat with google support to be told to use smtp-relay.gmail.com instead of smtp.gmail.com and this does appear to get around the quota problem.)

I’m aware there’s a version 6 which may not contain this behaviour, but as it’s a major breaking release and we’re dependent on the integration of 5.x in our ecommerce system and upgrade is not possible right now.

Source: Ask PHP

LEAVE A COMMENT