*/ /** Allow to send mails by smtp */ class smtp { /** Debug mode */ public $debug = 0; /** The debug file used to store all the communication between the client and * the server. The file contains the passwords if used ! */ public $debugFile = "/tmp/debugSMTP"; /** The authentication user allow to send SMTP mails */ public $user = null; /** The authentication password allow to send SMTP mails */ public $password = null; /** The SMTP server name or IP */ public $server = "127.0.0.1"; /** The SMTP port to use (if false, use 25 if no SSL or 465 if SSL, * if user defined, use the user value) */ public $port = false; /** The SMTPS support by tunnelling the session in SSL transport */ public $ssl = false; /** Check the certification chain in SSL mode */ public $sslCheck = true; /** The Timeout between the answer of the SMTP server. If the server don't * answer in this time, an exception is raised */ public $timeout = 10; /** Activate STARTTLS if needed. Allowed values : none, may, encrypt */ public $starttls = "may"; /** Check the certificate in STARTTLS */ public $starttlsCheck = false; /** The authentication methods in an array. Allowed : plain, login */ public $authmethods = array ("plain", "login"); /** The socket of the connection */ private $smtpStream = null; /** Connect to the SMTP server */ public function connect () { $context = array (); if ($this->ssl) { $this->server = "tls://$this->server"; $context["ssl"]["verify_peer_name"] = $this->sslCheck; $context["ssl"]["verify_peer"] = $this->sslCheck; if ($this->port === false) $this->port = 465; } else { if ($this->port === false) $this->port = 25; } $mainContext = stream_context_create ($context); $this->debug ("####SMTP Connection to $this->server:$this->port (". date ("Y/m/d H:i:s").")\n"); ini_set('track_errors', 1); $this->smtpStream = @stream_socket_client ("$this->server:$this->port", $errno, $errstr, $this->timeout, STREAM_CLIENT_CONNECT, $mainContext); ini_set('track_errors', 0); if ($this->smtpStream === false) { if ($errstr === "" && $php_errormsg !== "") $errstr = $php_errormsg; throw new \Exception (sprintf (_("Can't connect to SMTP server : %s"), $errstr), 500); } stream_set_timeout ($this->smtpStream, $this->timeout); // Wait for banner $banner = $this->getLine ("SMTP Banner"); // Send EHLO $features = $this->putLine ("EHLO ".gethostname ()."\r\n"); $features = explode ("\r\n", $features); if (in_array ("250-STARTTLS", $features)) { // Server supports STARTTLS if ($this->starttls === "may" || $this->starttls === "encrypt") { $this->putLine ("STARTTLS\r\n"); $context["ssl"]["verify_peer_name"] = $this->starttlsCheck; $context["ssl"]["verify_peer"] = $this->starttlsCheck; stream_context_set_option ($this->smtpStream, $context); // The track_errors permit to create the $php_errormsg in case of // warning ini_set('track_errors', 1); if (@stream_socket_enable_crypto ($this->smtpStream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT) === false) throw new \Exception (sprintf (_("Can't activate STARTTLS %s"), strstr ($php_errormsg, ": ")), 500); ini_set('track_errors', 0); $this->debug ("STARTTLS ACTIVATED\n"); } } elseif ($this->starttls === "encrypt") throw new \Exception (_("Server doesn't supports STARTTLS"), 500); if ($this->user !== null && $this->password !== null) { $auths = preg_grep ("#^250-AUTH #", $features); $auths = reset ($auths); if (strpos ($auths, "PLAIN") && in_array ("plain", $this->authmethods)) { // Send User and password AUTH PLAIN $this->putLine ("AUTH PLAIN ". base64_encode ($this->user.chr(0).$this->user.chr(0). $this->password)."\r\n"); } elseif (strpos ($auths, "LOGIN") && in_array ("login", $this->authmethods)) { // Send User and password AUTH LOGIN $this->putLine ("AUTH LOGIN ".base64_encode ($this->user)."\r\n"); $this->putLine (base64_encode ($this->password)."\r\n"); } else throw new \Exception ( _("No authentication method available for the server"), 500); } } /** Send the mail to the users * @param string $from The email address used for the from enveloppe * @param string|array $to the recipient of the email. Not displayed in the * email content * @param string $completeMail The content of the email * @return the message from the server (where is stored the message queue * identifier */ public function send ($from, $to, $completeMail) { if ($this->smtpStream === null) throw new \Exception ( _("Can't send email : not connected to SMTP server"), 500); if (is_string ($to)) $to = array ($to); $this->putLine ("MAIL FROM: $from\r\n"); foreach ($to as $t) $this->putLine ("RCPT TO: $t\r\n"); $this->putLine ("DATA\r\n"); if (substr ($completeMail, -2) !== "\r\n") $completeMail .= "\r\n"; return $this->putLine ("$completeMail.\r\n"); } /** Disconnect from the SMTP server */ public function disconnect () { if ($this->smtpStream === null) throw new \Exception ( _("Can't send email : not connected to SMTP server"), 500); $this->putLine ("QUIT\r\n"); fclose ($this->smtpStream); } /** Reset the session to start a new mail in the same SMTP connection */ public function reset () { if ($this->smtpStream === null) throw new \Exception ( _("Can't send email : not connected to SMTP server"), 500); $this->putLine ("RSET\r\n"); } /** Send something to the SMTP server. Wait the acknoledgement line */ private function putLine ($data) { $this->debug ("> $data"); fwrite ($this->smtpStream, $data); return $this->getLine ($data); } /** Wait something from the server */ private function getLine ($message = "") { $this->debug ("Waiting for ".rtrim ($message)." answer\n", 2); $content = ""; while (1) { $line = stream_get_line ($this->smtpStream, 1024, "\r\n"); if ($line === false) break; $meta = stream_get_meta_data ($this->smtpStream); if ($meta["timed_out"] !== FALSE) { $this->debug ("Timeout when waiting $message\n"); throw new \Exception ("Timeout when waiting $message", 500); } $content .= $line."\r\n"; if (substr ($line, 3, 1) === " ") break; } $this->debug ("< $content"); if (substr ($line, 0, 1) !== "2" && substr ($line, 0, 1) !== "3") { $this->debug ("Can't send mail : server answer : $line\n"); throw new \Exception ("Can't send mail : server answer : $line", 500); } return $content; } /** Save the connection debug in file */ private function debug ($message, $priority = 1) { if ($this->debug == false) return; if ($priority > $this->debug) return; file_put_contents ($this->debugFile, $message, FILE_APPEND); } }