X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=class%2Fdeliver%2FDeliver_SMTP.class.php;h=da7fdcdc8ada62756a913a79b12d1d95674e582b;hb=33aab559e47bc6c36748eb7d105b47dbdd6d89c1;hp=431ad74cce9828fc327fcc85f96ed7e28a084086;hpb=85d3e98ce160643bab060be0db4be0fe1af689f9;p=squirrelmail.git diff --git a/class/deliver/Deliver_SMTP.class.php b/class/deliver/Deliver_SMTP.class.php index 431ad74c..da7fdcdc 100644 --- a/class/deliver/Deliver_SMTP.class.php +++ b/class/deliver/Deliver_SMTP.class.php @@ -5,7 +5,7 @@ * * SMTP delivery backend for the Deliver class. * - * @copyright © 1999-2007 The SquirrelMail Project Team + * @copyright 1999-2024 The SquirrelMail Project Team * @license http://opensource.org/licenses/gpl-license.php GNU Public License * @version $Id$ * @package squirrelmail @@ -57,16 +57,16 @@ class Deliver_SMTP extends Deliver { function preWriteToStream(&$s) { if ($s) { - if ($s{0} == '.') $s = '.' . $s; + if ($s[0] == '.') $s = '.' . $s; $s = str_replace("\n.","\n..",$s); } } - function initStream($message, $domain, $length=0, $host='', $port='', $user='', $pass='', $authpop=false) { + function initStream($message, $domain, $length=0, $host='', $port='', $user='', $pass='', $authpop=false, $pop_host='', $stream_options=array()) { global $use_smtp_tls,$smtp_auth_mech; if ($authpop) { - $this->authPop($host, '', $user, $pass); + $this->authPop($user, $pass, $pop_host, ''); } $rfc822_header = $message->rfc822_header; @@ -90,9 +90,23 @@ class Deliver_SMTP extends Deliver { $from->mailbox = ''; } + // NB: Using "ssl://" ensures the highest possible TLS version + // will be negotiated with the server (whereas "tls://" only + // uses TLS version 1.0) + // if ($use_smtp_tls == 1) { if ((check_php_version(4,3)) && (extension_loaded('openssl'))) { - $stream = @fsockopen('tls://' . $host, $port, $errorNumber, $errorString); + if (function_exists('stream_socket_client')) { + $server_address = 'ssl://' . $host . ':' . $port; + $ssl_context = @stream_context_create($stream_options); + $connect_timeout = ini_get('default_socket_timeout'); + // null timeout is broken + if ($connect_timeout == 0) + $connect_timeout = 30; + $stream = @stream_socket_client($server_address, $errorNumber, $errorString, $connect_timeout, STREAM_CLIENT_CONNECT, $ssl_context); + } else { + $stream = @fsockopen('ssl://' . $host, $port, $errorNumber, $errorString); + } $this->tls_enabled = true; } else { /** @@ -135,13 +149,21 @@ class Deliver_SMTP extends Deliver { $helohost = $domain; } + // if the host is an IPv4 address, enclose it in brackets + // + if (preg_match('/^\d+\.\d+\.\d+\.\d+$/', $helohost)) + $helohost = '[' . $helohost . ']'; + + $hook_result = do_hook('smtp_helo_override', $helohost); + if (!empty($hook_result)) $helohost = $hook_result; + /* Lets introduce ourselves */ fputs($stream, "EHLO $helohost\r\n"); // Read ehlo response $tmp = $this->parse_ehlo_response($stream); if ($this->errorCheck($tmp,$stream)) { // fall back to HELO if EHLO is not supported (error 5xx) - if ($this->dlv_ret_nr{0} == '5') { + if ($this->dlv_ret_nr[0] == '5') { fputs($stream, "HELO $helohost\r\n"); $tmp = fgets($stream,1024); if ($this->errorCheck($tmp,$stream)) { @@ -205,7 +227,25 @@ class Deliver_SMTP extends Deliver { } // FIXME: check ehlo response before using authentication - if (( $smtp_auth_mech == 'cram-md5') or ( $smtp_auth_mech == 'digest-md5' )) { + + // Try authentication by a plugin + // + // NOTE: there is another hook in functions/auth.php called "smtp_auth" + // that allows a plugin to specify a different set of login credentials + // (so is slightly mis-named, but is too old to change), so be careful + // that you do not confuse your hook names. + // + $smtp_auth_args = array( + 'auth_mech' => $smtp_auth_mech, + 'user' => $user, + 'pass' => $pass, + 'host' => $host, + 'port' => $port, + 'stream' => $stream, + ); + if (boolean_hook_function('smtp_authenticate', $smtp_auth_args, 1)) { + // authentication succeeded + } else if (( $smtp_auth_mech == 'cram-md5') or ( $smtp_auth_mech == 'digest-md5' )) { // Doing some form of non-plain auth if ($smtp_auth_mech == 'cram-md5') { fputs($stream, "AUTH CRAM-MD5\r\n"); @@ -294,7 +334,7 @@ class Deliver_SMTP extends Deliver { } /* Ok, who is sending the message? */ - $fromaddress = ($from->mailbox && $from->host) ? + $fromaddress = (strlen($from->mailbox) && $from->host) ? $from->mailbox.'@'.$from->host : ''; fputs($stream, 'MAIL FROM:<'.$fromaddress.">\r\n"); $tmp = fgets($stream, 1024); @@ -305,8 +345,19 @@ class Deliver_SMTP extends Deliver { /* send who the recipients are */ for ($i = 0, $cnt = count($to); $i < $cnt; $i++) { if (!$to[$i]->host) $to[$i]->host = $domain; - if ($to[$i]->mailbox) { - fputs($stream, 'RCPT TO:<'.$to[$i]->mailbox.'@'.$to[$i]->host.">\r\n"); + if (strlen($to[$i]->mailbox)) { + // Ask for DSN if user has requested such and remote server supports it + if (isset($rfc822_header->dsn) && $rfc822_header->dsn + && array_key_exists('DSN',$this->ehlo)) { + // TODO: Make the DSN parameters configurable by admin? user? + fputs($stream, 'RCPT TO:<'.$to[$i]->mailbox.'@'.$to[$i]->host."> NOTIFY=SUCCESS,DELAY,FAILURE\r\n"); + // Retry without DSN fields for cranky MTAs + if ($this->errorCheck($tmp, $stream)) { + fputs($stream, 'RCPT TO:<'.$to[$i]->mailbox.'@'.$to[$i]->host.">\r\n"); + } + } + else + fputs($stream, 'RCPT TO:<'.$to[$i]->mailbox.'@'.$to[$i]->host.">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return(0); @@ -316,8 +367,19 @@ class Deliver_SMTP extends Deliver { for ($i = 0, $cnt = count($cc); $i < $cnt; $i++) { if (!$cc[$i]->host) $cc[$i]->host = $domain; - if ($cc[$i]->mailbox) { - fputs($stream, 'RCPT TO:<'.$cc[$i]->mailbox.'@'.$cc[$i]->host.">\r\n"); + if (strlen($cc[$i]->mailbox)) { + // Ask for DSN if user has requested such and remote server supports it + if (isset($rfc822_header->dsn) && $rfc822_header->dsn + && array_key_exists('DSN',$this->ehlo)) { + // TODO: Make the DSN parameters configurable by admin? user? + fputs($stream, 'RCPT TO:<'.$cc[$i]->mailbox.'@'.$cc[$i]->host."> NOTIFY=SUCCESS,DELAY,FAILURE\r\n"); + // Retry without DSN fields for cranky MTAs + if ($this->errorCheck($tmp, $stream)) { + fputs($stream, 'RCPT TO:<'.$cc[$i]->mailbox.'@'.$cc[$i]->host.">\r\n"); + } + } + else + fputs($stream, 'RCPT TO:<'.$cc[$i]->mailbox.'@'.$cc[$i]->host.">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return(0); @@ -327,8 +389,19 @@ class Deliver_SMTP extends Deliver { for ($i = 0, $cnt = count($bcc); $i < $cnt; $i++) { if (!$bcc[$i]->host) $bcc[$i]->host = $domain; - if ($bcc[$i]->mailbox) { - fputs($stream, 'RCPT TO:<'.$bcc[$i]->mailbox.'@'.$bcc[$i]->host.">\r\n"); + if (strlen($bcc[$i]->mailbox)) { + // Ask for DSN if user has requested such and remote server supports it + if (isset($rfc822_header->dsn) && $rfc822_header->dsn + && array_key_exists('DSN',$this->ehlo)) { + // TODO: Make the DSN parameters configurable by admin? user? + fputs($stream, 'RCPT TO:<'.$bcc[$i]->mailbox.'@'.$bcc[$i]->host."> NOTIFY=SUCCESS,DELAY,FAILURE\r\n"); + // Retry without DSN fields for cranky MTAs + if ($this->errorCheck($tmp, $stream)) { + fputs($stream, 'RCPT TO:<'.$bcc[$i]->mailbox.'@'.$bcc[$i]->host.">\r\n"); + } + } + else + fputs($stream, 'RCPT TO:<'.$bcc[$i]->mailbox.'@'.$bcc[$i]->host.">\r\n"); $tmp = fgets($stream, 1024); if ($this->errorCheck($tmp, $stream)) { return(0); @@ -368,7 +441,7 @@ class Deliver_SMTP extends Deliver { $server_msg .= substr($line, 4); } - if ( ((int) $err_num{0}) < 4) { + if ( ((int) $err_num[0]) < 4) { return false; } @@ -423,7 +496,7 @@ class Deliver_SMTP extends Deliver { return true; } - function authPop($pop_server='', $pop_port='', $user, $pass) { + function authPop($user, $pass, $pop_server='', $pop_port='') { if (!$pop_port) { $pop_port = 110; }