git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@3762 bf3deb0d-5f1a-0410-827f-c0cc1f45334c
274 lines
9.0 KiB
PHP
274 lines
9.0 KiB
PHP
<?php
|
|
/** DomFramework
|
|
@package domframework
|
|
@author Dominique Fournier <dominique@fournier38.fr> */
|
|
|
|
/** 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);
|
|
}
|
|
}
|
|
|
|
/** Clean the provided mail and add the <> signs arround it
|
|
* @param string $mail The mail to clean
|
|
* @return string The cleaned mail
|
|
*/
|
|
private function cleanMail ($mail)
|
|
{
|
|
if (! is_string ($mail))
|
|
throw new \Exception ("SMTP: Invalid mail provided: not a string", 500);
|
|
$mail = trim ($mail);
|
|
$pos = strpos ($mail, "<");
|
|
if ($pos !== false)
|
|
$mail = substr ($mail, $pos);
|
|
else
|
|
$mail = "<$mail";
|
|
if (substr ($mail, -1) !== ">")
|
|
$mail .= ">";
|
|
return $mail;
|
|
}
|
|
|
|
/** 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);
|
|
$from = $this->cleanMail ($from);
|
|
if (is_string ($to))
|
|
$to = explode (",", $to);
|
|
if (! is_array ($to) || count ($to) === 0)
|
|
throw new \Exception ("Can't send mail: no valid recipient provided",
|
|
500);
|
|
$this->putLine ("MAIL FROM: $from\r\n");
|
|
foreach ($to as $t)
|
|
{
|
|
$t = $this->cleanMail ($t);
|
|
$this->putLine ("RCPT TO: $t\r\n");
|
|
}
|
|
$this->putLine ("DATA\r\n");
|
|
if (substr ($completeMail, -2) !== "\r\n")
|
|
$completeMail .= "\r\n";
|
|
// Dot on first column : must be doubled to not be detected as the end
|
|
// of SMTP transaction. The SMTP server will remove it before distribute
|
|
// http://tools.ietf.org/html/rfc5321#section-4.5.2
|
|
$completeMail = preg_replace ("#^\.#m", "..", $completeMail);
|
|
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
|
|
* @param string $data The line to send to server
|
|
*/
|
|
private function putLine ($data)
|
|
{
|
|
$this->debug ("> $data");
|
|
fwrite ($this->smtpStream, $data);
|
|
return $this->getLine ($data);
|
|
}
|
|
|
|
/** Wait something from the server
|
|
* @param string $message The answer to wait from 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");
|
|
$this->putLine ("QUIT\r\n");
|
|
fclose ($this->smtpStream);
|
|
throw new \Exception ("Can't send mail : server answer : $line", 500);
|
|
}
|
|
return $content;
|
|
}
|
|
|
|
/** Save the connection debug in file
|
|
* @param string $message The message to save in debug
|
|
* @param integer|null $priority The priority to use
|
|
*/
|
|
private function debug ($message, $priority = 1)
|
|
{
|
|
if ($this->debug == false)
|
|
return;
|
|
if ($priority > $this->debug)
|
|
return;
|
|
file_put_contents ($this->debugFile, $message, FILE_APPEND);
|
|
}
|
|
}
|