SMTP Authentication

Hi guys,

I have a problem sending emails from my e-commerce cms (opencart) due to a restriction placed on me by my hosting provider. They require that all incoming and outgoing SMTP requests are authenticated otherwise they are blocked by the hosting company firewall.

Anyway, opencart has an environment variable that allows you to specify the incoming SMTP password but no field for the outgoing.

This means that I receive my admin emails because they don’t leave the server (the mail server sends back to itself) but the customer gets no order confirmation email because it needs to be authenticated to leave the server :frowning:

I can see no other option other than modifying the opencart mail script to hardcode outgoing authentication but unfortunately it is something I have no experience with to date. Could one of you PHP gurus please examine the mail script from opencart below and suggest a way to do this?

I would be grateful if you could also specify the exact variables to pass to the mail function you modify in order for me to hardcode the controller file that calls it.

kind regards

Silversurfer



<?php
final class Mail {
	protected $to;
	protected $from;
	protected $sender;
	protected $subject;
	protected $text;
	protected $html;
	protected $attachments = array();
	public $protocol = 'mail';
	public $hostname;
	public $username;
	public $password;
	public $port = 25;
	public $timeout = 5;
	public $newline = "\
";
	public $crlf = "\\r\
";
	public $verp = FALSE;
	public $parameter = '';

	public function setTo($to) {
		$this->to = $to;
	}

	public function setFrom($from) {
		$this->from = $from;
	}

	public function addheader($header, $value) {
		$this->headers[$header] = $value;
	}

	public function setSender($sender) {
		$this->sender = html_entity_decode($sender);
	}

	public function setSubject($subject) {
		$this->subject = html_entity_decode($subject);
	}

	public function setText($text) {
		$this->text = $text;
	}

	public function setHtml($html) {
		$this->html = $html;
	}

	public function addAttachment($file, $filename = '') {
		if (!$filename) {
			$filename = basename($file);
		}

		$this->attachments[] = array(
			'filename' => $filename,
			'file'     => $file
		);
	}

	public function send() {
		if (!$this->to) {
			exit('Error: E-Mail to required!');
		}

		if (!$this->from) {
			exit('Error: E-Mail from required!');
		}

		if (!$this->sender) {
			exit('Error: E-Mail sender required!');
		}

		if (!$this->subject) {
			exit('Error: E-Mail subject required!');
		}

		if ((!$this->text) && (!$this->html)) {
			exit('Error: E-Mail message required!');
		}

		if (is_array($this->to)) {
			$to = implode(',', $this->to);
		} else {
			$to = $this->to;
		}

		$boundary = '----=_NextPart_' . md5(time());

		$header = '';

		if ($this->protocol != 'mail') {
			$header .= 'To: ' . $to . $this->newline;
			$header .= 'Subject: ' . $this->subject . $this->newline;
		}
		
		$header .= 'Date: ' . date("D, d M Y H:i:s O") . $this->newline;
		//$header .= 'From: "' . $this->sender . '" <' . $this->from . '>' . $this->newline;
		//$header .= 'From: ' . $this->sender . '<' . $this->from . '>' . $this->newline;
		$header .= 'From: ' . '=?UTF-8?B?'.base64_encode($this->sender).'?=' . '<' . $this->from . '>' . $this->newline;
		$header .= 'Reply-To: ' . $this->sender . '<' . $this->from . '>' . $this->newline;
		$header .= 'Return-Path: ' . $this->from . $this->newline;
		$header .= 'X-Mailer: PHP/' . phpversion() . $this->newline;
		$header .= 'MIME-Version: 1.0' . $this->newline;
		$header .= 'Content-Type: multipart/mixed; boundary="' . $boundary . '"' . $this->newline;

		if (!$this->html) {
			$message  = '--' . $boundary . $this->newline;
			$message .= 'Content-Type: text/plain; charset="utf-8"' . $this->newline;
			$message .= 'Content-Transfer-Encoding: 8bit' . $this->newline . $this->newline;
			$message .= $this->text . $this->newline;
		} else {
			$message  = '--' . $boundary . $this->newline;
			$message .= 'Content-Type: multipart/alternative; boundary="' . $boundary . '_alt"' . $this->newline . $this->newline;
			$message .= '--' . $boundary . '_alt' . $this->newline;
			$message .= 'Content-Type: text/plain; charset="utf-8"' . $this->newline;
			$message .= 'Content-Transfer-Encoding: 8bit' . $this->newline;

			if ($this->text) {
				$message .= $this->text . $this->newline;
			} else {
				$message .= 'This is a HTML email and your email client software does not support HTML email!' . $this->newline;
			}

			$message .= '--' . $boundary . '_alt' . $this->newline;
			$message .= 'Content-Type: text/html; charset="utf-8"' . $this->newline;
			$message .= 'Content-Transfer-Encoding: 8bit' . $this->newline . $this->newline;
			$message .= $this->html . $this->newline;
			$message .= '--' . $boundary . '_alt--' . $this->newline;
		}

		foreach ($this->attachments as $attachment) {
			if (file_exists($attachment['file'])) {
				$handle = fopen($attachment['file'], 'r');
				$content = fread($handle, filesize($attachment['file']));

				fclose($handle);

				$message .= '--' . $boundary . $this->newline;
				$message .= 'Content-Type: application/octetstream' . $this->newline;
				$message .= 'Content-Transfer-Encoding: base64' . $this->newline;
				$message .= 'Content-Disposition: attachment; filename="' . basename($attachment['filename']) . '"' . $this->newline;
				$message .= 'Content-ID: <' . basename($attachment['filename']) . '>' . $this->newline . $this->newline;
				$message .= chunk_split(base64_encode($content));
			}
		}

		$message .= '--' . $boundary . '--' . $this->newline;

		if ($this->protocol == 'mail') {
			ini_set('sendmail_from', $this->from);

			if ($this->parameter) {
				mail($to, '=?UTF-8?B?'.base64_encode($this->subject).'?=', $message, $header, $this->parameter);
			} else {
				mail($to, '=?UTF-8?B?'.base64_encode($this->subject).'?=', $message, $header);
			}

		} elseif ($this->protocol == 'smtp') {
			$handle = fsockopen($this->hostname, $this->port, $errno, $errstr, $this->timeout);

			if (!$handle) {
				error_log('Error: ' . $errstr . ' (' . $errno . ')');
			} else {
				if (substr(PHP_OS, 0, 3) != 'WIN') {
					socket_set_timeout($handle, $this->timeout, 0);
				}

				while ($line = fgets($handle, 515)) {
					if (substr($line, 3, 1) == ' ') {
						break;
					}
				}

				if (substr($this->hostname, 0, 3) == 'tls') {
					fputs($handle, 'STARTTLS' . $this->crlf);

					while ($line = fgets($handle, 515)) {
						$reply .= $line;

						if (substr($line, 3, 1) == ' ') {
							break;
						}
					}

					if (substr($reply, 0, 3) != 220) {
						error_log('Error: STARTTLS not accepted from server!');
					}
				}

				if (!empty($this->username)  && !empty($this->password)) {
					fputs($handle, 'EHLO ' . getenv('SERVER_NAME') . $this->crlf);

					$reply = '';

					while ($line = fgets($handle, 515)) {
						$reply .= $line;

						if (substr($line, 3, 1) == ' ') {
							break;
						}
					}

					if (substr($reply, 0, 3) != 250) {
						error_log('Error: EHLO not accepted from server!');
					}

					fputs($handle, 'AUTH LOGIN' . $this->crlf);

					$reply = '';

					while ($line = fgets($handle, 515)) {
						$reply .= $line;

						if (substr($line, 3, 1) == ' ') {
							break;
						}
					}

					if (substr($reply, 0, 3) != 334) {
						error_log('Error: AUTH LOGIN not accepted from server!');
					}

					fputs($handle, base64_encode($this->username) . $this->crlf);

					$reply = '';

					while ($line = fgets($handle, 515)) {
						$reply .= $line;

						if (substr($line, 3, 1) == ' ') {
							break;
						}
					}

					if (substr($reply, 0, 3) != 334) {
						error_log('Error: Username not accepted from server!');
					}

					fputs($handle, base64_encode($this->password) . $this->crlf);

					$reply = '';

					while ($line = fgets($handle, 515)) {
						$reply .= $line;

						if (substr($line, 3, 1) == ' ') {
							break;
						}
					}

					if (substr($reply, 0, 3) != 235) {
						error_log('Error: Password not accepted from server!');
					}
				} else {
					fputs($handle, 'HELO ' . getenv('SERVER_NAME') . $this->crlf);

					$reply = '';

					while ($line = fgets($handle, 515)) {
						$reply .= $line;

						if (substr($line, 3, 1) == ' ') {
							break;
						}
					}

					if (substr($reply, 0, 3) != 250) {
						error_log('Error: HELO not accepted from server!');
					}
				}

				if ($this->verp) {
					fputs($handle, 'MAIL FROM: <' . $this->from . '>XVERP' . $this->crlf);
				} else {
					fputs($handle, 'MAIL FROM: <' . $this->from . '>' . $this->crlf);
				}

				$reply = '';

				while ($line = fgets($handle, 515)) {
					$reply .= $line;

					if (substr($line, 3, 1) == ' ') {
						break;
					}
				}

				if (substr($reply, 0, 3) != 250) {
					error_log('Error: MAIL FROM not accepted from server!');
				}

				if (!is_array($this->to)) {
					fputs($handle, 'RCPT TO: <' . $this->to . '>' . $this->crlf);

					$reply = '';

					while ($line = fgets($handle, 515)) {
						$reply .= $line;

						if (substr($line, 3, 1) == ' ') {
							break;
						}
					}

					if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) {
						error_log('Error: RCPT TO not accepted from server!');
					}
				} else {
					foreach ($this->to as $recipient) {
						fputs($handle, 'RCPT TO: <' . $recipient . '>' . $this->crlf);

						$reply = '';

						while ($line = fgets($handle, 515)) {
							$reply .= $line;

							if (substr($line, 3, 1) == ' ') {
								break;
							}
						}

						if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) {
							error_log('Error: RCPT TO not accepted from server!');
						}
					}
				}

				fputs($handle, 'DATA' . $this->crlf);

				$reply = '';

				while ($line = fgets($handle, 515)) {
					$reply .= $line;

					if (substr($line, 3, 1) == ' ') {
						break;
					}
				}

				if (substr($reply, 0, 3) != 354) {
					error_log('Error: DATA not accepted from server!');
				}

				fputs($handle, $header . $message . $this->crlf);
				fputs($handle, '.' . $this->crlf);

				$reply = '';

				while ($line = fgets($handle, 515)) {
					$reply .= $line;

					if (substr($line, 3, 1) == ' ') {
						break;
					}
				}

				if (substr($reply, 0, 3) != 250) {
					error_log('Error: DATA not accepted from server!');
				}

				fputs($handle, 'QUIT' . $this->crlf);

				$reply = '';

				while ($line = fgets($handle, 515)) {
					$reply .= $line;

					if (substr($line, 3, 1) == ' ') {
						break;
					}
				}

				if (substr($reply, 0, 3) != 221) {
					error_log('Error: QUIT not accepted from server!');
				}

				fclose($handle);
			}
		}
	}
}
?>

There is no incoming smtp configuration as smtp handles outgoing mail only. Keyword for incoming mail is pop3 or imap.

Can you provide the rejected mails’ error message?

Hi there,

No error is logged but the suppoort team assure me that it is their firewall blocking the mail and that it needs authentication and although I am aware of POP and IMAP you can use SMTP for incoming mail which would appear to be the case. I say that because in Opencart there are two choices , to use sendmail which I don’t want and SMTP, there are no pop or IMAP settings. Just asks for SMTP username, SMTP password, SMTP port and SMTP timeout - that is why I presume that it is using SMTP for both incoming and outgoing.

Can you help me?

I hope so.

If a firewall (i.e. packet filter) is involved, it appears that your provider blocks port 25 which sounds strange to me. More likely seems a missing authentication which leads to your mail being rejected by the provider’s mta. However, according to smtp rules, an error message should be fired. Do you have access to the mail logfile (usually /var/log/maillog)?

If you can replace the built in mail function with swiftmailer library, it has extensive smtp connectivity and logging functionality

OpenCart has no incoming mail functionality whatsoever. Sounds like your hosting is spinning you a line

Thats true, i use opencart since 18 months and myself a small developer too, there is no incoming mail functionality till now for opencart. It uses smtp to send mails. Ask your hosting provider with full details and smtp logs. it would show errors if your configured store admin email was not authenticated to be sent. you may recheck the SMTP port configured in admin again.